bunderstack 0.23.4 → 0.24.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 (159) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +7 -4
  3. package/dist/api/builder.d.ts +13 -11
  4. package/dist/api/builder.d.ts.map +1 -1
  5. package/dist/api/builder.js.map +1 -1
  6. package/dist/api/context.d.ts +6 -6
  7. package/dist/api/context.d.ts.map +1 -1
  8. package/dist/api/context.js +1 -1
  9. package/dist/api/context.js.map +1 -1
  10. package/dist/api/crud-router.d.ts +6 -6
  11. package/dist/api/realtime-router.d.ts +1 -1
  12. package/dist/api/storage-router.d.ts +5 -5
  13. package/dist/auth.d.ts +20 -9
  14. package/dist/auth.d.ts.map +1 -1
  15. package/dist/auth.js +3 -8
  16. package/dist/auth.js.map +1 -1
  17. package/dist/backend-internals.d.ts +5 -13
  18. package/dist/backend-internals.d.ts.map +1 -1
  19. package/dist/backend-internals.js.map +1 -1
  20. package/dist/backend.d.ts +39 -5
  21. package/dist/backend.d.ts.map +1 -1
  22. package/dist/backend.js +37 -46
  23. package/dist/backend.js.map +1 -1
  24. package/dist/blueprint-generator.d.ts +1 -0
  25. package/dist/blueprint-generator.d.ts.map +1 -1
  26. package/dist/blueprint-generator.js +29 -1
  27. package/dist/blueprint-generator.js.map +1 -1
  28. package/dist/blueprint.d.ts +2 -1
  29. package/dist/blueprint.d.ts.map +1 -1
  30. package/dist/blueprint.js +9 -2
  31. package/dist/blueprint.js.map +1 -1
  32. package/dist/cli.d.ts.map +1 -1
  33. package/dist/cli.js +14 -2
  34. package/dist/cli.js.map +1 -1
  35. package/dist/config.d.ts +23 -25
  36. package/dist/config.d.ts.map +1 -1
  37. package/dist/config.js +1 -1
  38. package/dist/config.js.map +1 -1
  39. package/dist/email/smtp.d.ts +7 -1
  40. package/dist/email/smtp.d.ts.map +1 -1
  41. package/dist/email/smtp.js +5 -1
  42. package/dist/email/smtp.js.map +1 -1
  43. package/dist/email.d.ts +3 -29
  44. package/dist/email.d.ts.map +1 -1
  45. package/dist/email.js +1 -194
  46. package/dist/email.js.map +1 -1
  47. package/dist/env-probe.d.ts +9 -0
  48. package/dist/env-probe.d.ts.map +1 -0
  49. package/dist/env-probe.js +73 -0
  50. package/dist/env-probe.js.map +1 -0
  51. package/dist/env.d.ts +2 -5
  52. package/dist/env.d.ts.map +1 -1
  53. package/dist/env.js +2 -9
  54. package/dist/env.js.map +1 -1
  55. package/dist/hosted-contract.d.ts +11 -0
  56. package/dist/hosted-contract.d.ts.map +1 -0
  57. package/dist/hosted-contract.js +46 -0
  58. package/dist/hosted-contract.js.map +1 -0
  59. package/dist/index.d.ts +3 -2
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/inspect.d.ts +15 -0
  64. package/dist/inspect.d.ts.map +1 -0
  65. package/dist/inspect.js +43 -0
  66. package/dist/inspect.js.map +1 -0
  67. package/dist/internal-tables-pg.d.ts +43 -94
  68. package/dist/internal-tables-pg.d.ts.map +1 -1
  69. package/dist/internal-tables-pg.js +14 -17
  70. package/dist/internal-tables-pg.js.map +1 -1
  71. package/dist/internal-tables.d.ts +213 -488
  72. package/dist/internal-tables.d.ts.map +1 -1
  73. package/dist/internal-tables.js +33 -33
  74. package/dist/internal-tables.js.map +1 -1
  75. package/dist/jobs/define.d.ts +20 -20
  76. package/dist/jobs/define.d.ts.map +1 -1
  77. package/dist/jobs/define.js.map +1 -1
  78. package/dist/manifest-diff.d.ts +6 -0
  79. package/dist/manifest-diff.d.ts.map +1 -0
  80. package/dist/manifest-diff.js +42 -0
  81. package/dist/manifest-diff.js.map +1 -0
  82. package/dist/manifest.d.ts +10 -2
  83. package/dist/manifest.d.ts.map +1 -1
  84. package/dist/manifest.js +27 -29
  85. package/dist/manifest.js.map +1 -1
  86. package/dist/messaging/email.d.ts +16 -0
  87. package/dist/messaging/email.d.ts.map +1 -0
  88. package/dist/messaging/email.js +8 -0
  89. package/dist/messaging/email.js.map +1 -0
  90. package/dist/messaging/index.d.ts +8 -0
  91. package/dist/messaging/index.d.ts.map +1 -0
  92. package/dist/messaging/index.js +4 -0
  93. package/dist/messaging/index.js.map +1 -0
  94. package/dist/messaging/journal.d.ts +4 -0
  95. package/dist/messaging/journal.d.ts.map +1 -0
  96. package/dist/messaging/journal.js +19 -0
  97. package/dist/messaging/journal.js.map +1 -0
  98. package/dist/messaging/runtime.d.ts +16 -0
  99. package/dist/messaging/runtime.d.ts.map +1 -0
  100. package/dist/messaging/runtime.js +213 -0
  101. package/dist/messaging/runtime.js.map +1 -0
  102. package/dist/messaging/standalone.d.ts +27 -0
  103. package/dist/messaging/standalone.d.ts.map +1 -0
  104. package/dist/messaging/standalone.js +18 -0
  105. package/dist/messaging/standalone.js.map +1 -0
  106. package/dist/messaging/telegram.d.ts +16 -0
  107. package/dist/messaging/telegram.d.ts.map +1 -0
  108. package/dist/messaging/telegram.js +5 -0
  109. package/dist/messaging/telegram.js.map +1 -0
  110. package/dist/messaging/types.d.ts +22 -0
  111. package/dist/messaging/types.d.ts.map +1 -0
  112. package/dist/messaging/types.js +17 -0
  113. package/dist/messaging/types.js.map +1 -0
  114. package/dist/provision-internals.d.ts +1 -1
  115. package/dist/provision-internals.js +1 -1
  116. package/dist/provision-internals.js.map +1 -1
  117. package/dist/provision-runtime.d.ts +7 -0
  118. package/dist/provision-runtime.d.ts.map +1 -0
  119. package/dist/provision-runtime.js +50 -0
  120. package/dist/provision-runtime.js.map +1 -0
  121. package/dist/provision-schema.d.ts +16 -0
  122. package/dist/provision-schema.d.ts.map +1 -0
  123. package/dist/provision-schema.js +63 -0
  124. package/dist/provision-schema.js.map +1 -0
  125. package/dist/provision.d.ts +4 -21
  126. package/dist/provision.d.ts.map +1 -1
  127. package/dist/provision.js +10 -111
  128. package/dist/provision.js.map +1 -1
  129. package/dist/runtime.d.ts +17 -14
  130. package/dist/runtime.d.ts.map +1 -1
  131. package/dist/runtime.js +20 -33
  132. package/dist/runtime.js.map +1 -1
  133. package/dist/schema-export-pg.d.ts +1 -1
  134. package/dist/schema-export-pg.d.ts.map +1 -1
  135. package/dist/schema-export-pg.js +1 -1
  136. package/dist/schema-export-pg.js.map +1 -1
  137. package/dist/schema-export.d.ts +1 -1
  138. package/dist/schema-export.d.ts.map +1 -1
  139. package/dist/schema-export.js +1 -1
  140. package/dist/schema-export.js.map +1 -1
  141. package/dist/testing/fixture.d.ts +3 -3
  142. package/dist/testing/fixture.d.ts.map +1 -1
  143. package/dist/testing/fixture.js +12 -10
  144. package/dist/testing/fixture.js.map +1 -1
  145. package/dist/testing/messaging.d.ts +21 -0
  146. package/dist/testing/messaging.d.ts.map +1 -0
  147. package/dist/testing/messaging.js +34 -0
  148. package/dist/testing/messaging.js.map +1 -0
  149. package/dist/testing.d.ts +1 -0
  150. package/dist/testing.d.ts.map +1 -1
  151. package/dist/testing.js.map +1 -1
  152. package/llms-full.txt +416 -226
  153. package/llms.txt +4 -3
  154. package/package.json +10 -2
  155. package/skills/creating-bunderstack-apps/references/application-structure.md +16 -7
  156. package/skills/creating-bunderstack-apps/references/verification.md +7 -6
  157. package/skills/migrating-to-bunderstack/SKILL.md +75 -51
  158. package/skills/migrating-to-bunderstack/references/audit-checklist.md +2 -2
  159. package/skills/migrating-to-bunderstack/references/runtime-replacements.md +20 -15
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { BlueprintCheckError, generateBlueprint, } from './blueprint-generator.js';
3
3
  import { installSkills } from './cli-skills.js';
4
4
  const help = `Usage:
5
- bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check]
5
+ bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check|--hosted-check]
6
6
  bunderstack skills [--dir <path>] [--check]
7
7
 
8
8
  blueprint Generate a committed deployment declaration for a TanStack Start
@@ -53,9 +53,21 @@ export async function runCli(args, io, generate = generateBlueprint) {
53
53
  for (let index = 1; index < args.length; index++) {
54
54
  const argument = args[index];
55
55
  if (argument === '--check') {
56
+ if (options.hostedCheck) {
57
+ io.stderr('[bunderstack] --check and --hosted-check are mutually exclusive');
58
+ return 2;
59
+ }
56
60
  options.check = true;
57
61
  continue;
58
62
  }
63
+ if (argument === '--hosted-check') {
64
+ if (options.check) {
65
+ io.stderr('[bunderstack] --check and --hosted-check are mutually exclusive');
66
+ return 2;
67
+ }
68
+ options.hostedCheck = true;
69
+ continue;
70
+ }
59
71
  if (argument === '--entry' || argument === '--output') {
60
72
  const value = args[++index];
61
73
  if (!value || value.startsWith('--')) {
@@ -80,7 +92,7 @@ export async function runCli(args, io, generate = generateBlueprint) {
80
92
  }
81
93
  try {
82
94
  const result = await generate(options);
83
- io.stdout(options.check || !result.changed
95
+ io.stdout(options.check || options.hostedCheck || !result.changed
84
96
  ? 'bunderstack.blueprint.yaml is current'
85
97
  : 'Generated bunderstack.blueprint.yaml');
86
98
  return 0;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env bun\nimport {\n BlueprintCheckError,\n generateBlueprint,\n type GenerateBlueprintOptions,\n} from './blueprint-generator'\nimport { installSkills } from './cli-skills'\n\nexport type CliIo = {\n stdout(message: string): void\n stderr(message: string): void\n}\n\nconst help = `Usage:\n bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check]\n bunderstack skills [--dir <path>] [--check]\n\nblueprint Generate a committed deployment declaration for a TanStack Start\n application. Entry precedence: --entry,\n package.json#bunderstack.entry, src/bunderstack.ts.\n\nskills Install the Bunderstack agent skills that match this version into\n .agents/skills, and point AGENTS.md at them so an agent loads them\n before touching the API. --check reports drift without writing.`\n\nexport async function runCli(\n args: string[],\n io: CliIo,\n generate: typeof generateBlueprint = generateBlueprint,\n): Promise<number> {\n if (args[0] === '--help' || args[0] === '-h') {\n io.stdout(help)\n return 0\n }\n if (args[0] === '--version') {\n io.stdout(\n (\n (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n ).version,\n )\n return 0\n }\n if (args[0] === 'skills') {\n const options: { cwd: string; directory?: string; check?: boolean } = {\n cwd: process.cwd(),\n }\n for (let index = 1; index < args.length; index++) {\n const argument = args[index]!\n if (argument === '--check') {\n options.check = true\n continue\n }\n if (argument === '--dir') {\n const value = args[++index]\n if (!value || value.startsWith('--')) {\n io.stderr('[bunderstack] missing value for --dir')\n return 2\n }\n options.directory = value\n continue\n }\n io.stderr(`[bunderstack] unknown option: ${argument}`)\n return 2\n }\n return installSkills(options, io)\n }\n\n if (args[0] !== 'blueprint') {\n io.stderr(help)\n return 2\n }\n const options: GenerateBlueprintOptions = { directory: process.cwd() }\n for (let index = 1; index < args.length; index++) {\n const argument = args[index]!\n if (argument === '--check') {\n options.check = true\n continue\n }\n if (argument === '--entry' || argument === '--output') {\n const value = args[++index]\n if (!value || value.startsWith('--')) {\n io.stderr(`[bunderstack] missing value for ${argument}`)\n return 2\n }\n if (argument === '--entry') options.entry = value\n else options.output = value\n continue\n }\n if (argument.startsWith('-')) {\n io.stderr(`[bunderstack] unknown option: ${argument}`)\n return 2\n }\n if (options.directory !== process.cwd()) {\n io.stderr('[bunderstack] only one application directory is allowed')\n return 2\n }\n options.directory = argument\n }\n try {\n const result = await generate(options)\n io.stdout(\n options.check || !result.changed\n ? 'bunderstack.blueprint.yaml is current'\n : 'Generated bunderstack.blueprint.yaml',\n )\n return 0\n } catch (error) {\n io.stderr(error instanceof Error ? error.message : String(error))\n return error instanceof BlueprintCheckError ? 1 : 1\n }\n}\n\nif (import.meta.main) {\n const exitCode = await runCli(process.argv.slice(2), {\n stdout: (message) => console.log(message),\n stderr: (message) => console.error(message),\n })\n process.exit(exitCode)\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env bun\nimport {\n BlueprintCheckError,\n generateBlueprint,\n type GenerateBlueprintOptions,\n} from './blueprint-generator'\nimport { installSkills } from './cli-skills'\n\nexport type CliIo = {\n stdout(message: string): void\n stderr(message: string): void\n}\n\nconst help = `Usage:\n bunderstack blueprint [directory] [--entry <path>] [--output <path>] [--check|--hosted-check]\n bunderstack skills [--dir <path>] [--check]\n\nblueprint Generate a committed deployment declaration for a TanStack Start\n application. Entry precedence: --entry,\n package.json#bunderstack.entry, src/bunderstack.ts.\n\nskills Install the Bunderstack agent skills that match this version into\n .agents/skills, and point AGENTS.md at them so an agent loads them\n before touching the API. --check reports drift without writing.`\n\nexport async function runCli(\n args: string[],\n io: CliIo,\n generate: typeof generateBlueprint = generateBlueprint,\n): Promise<number> {\n if (args[0] === '--help' || args[0] === '-h') {\n io.stdout(help)\n return 0\n }\n if (args[0] === '--version') {\n io.stdout(\n (\n (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n ).version,\n )\n return 0\n }\n if (args[0] === 'skills') {\n const options: { cwd: string; directory?: string; check?: boolean } = {\n cwd: process.cwd(),\n }\n for (let index = 1; index < args.length; index++) {\n const argument = args[index]!\n if (argument === '--check') {\n options.check = true\n continue\n }\n if (argument === '--dir') {\n const value = args[++index]\n if (!value || value.startsWith('--')) {\n io.stderr('[bunderstack] missing value for --dir')\n return 2\n }\n options.directory = value\n continue\n }\n io.stderr(`[bunderstack] unknown option: ${argument}`)\n return 2\n }\n return installSkills(options, io)\n }\n\n if (args[0] !== 'blueprint') {\n io.stderr(help)\n return 2\n }\n const options: GenerateBlueprintOptions = { directory: process.cwd() }\n for (let index = 1; index < args.length; index++) {\n const argument = args[index]!\n if (argument === '--check') {\n if (options.hostedCheck) {\n io.stderr(\n '[bunderstack] --check and --hosted-check are mutually exclusive',\n )\n return 2\n }\n options.check = true\n continue\n }\n if (argument === '--hosted-check') {\n if (options.check) {\n io.stderr(\n '[bunderstack] --check and --hosted-check are mutually exclusive',\n )\n return 2\n }\n options.hostedCheck = true\n continue\n }\n if (argument === '--entry' || argument === '--output') {\n const value = args[++index]\n if (!value || value.startsWith('--')) {\n io.stderr(`[bunderstack] missing value for ${argument}`)\n return 2\n }\n if (argument === '--entry') options.entry = value\n else options.output = value\n continue\n }\n if (argument.startsWith('-')) {\n io.stderr(`[bunderstack] unknown option: ${argument}`)\n return 2\n }\n if (options.directory !== process.cwd()) {\n io.stderr('[bunderstack] only one application directory is allowed')\n return 2\n }\n options.directory = argument\n }\n try {\n const result = await generate(options)\n io.stdout(\n options.check || options.hostedCheck || !result.changed\n ? 'bunderstack.blueprint.yaml is current'\n : 'Generated bunderstack.blueprint.yaml',\n )\n return 0\n } catch (error) {\n io.stderr(error instanceof Error ? error.message : String(error))\n return error instanceof BlueprintCheckError ? 1 : 1\n }\n}\n\nif (import.meta.main) {\n const exitCode = await runCli(process.argv.slice(2), {\n stdout: (message) => console.log(message),\n stderr: (message) => console.error(message),\n })\n process.exit(exitCode)\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -5,8 +5,8 @@ import type { BunderstackApiBuilder } from './api/builder.js';
5
5
  import type { DatabaseAdapter } from './database/adapter.js';
6
6
  import type { DbFor } from './db.js';
7
7
  import type { AnyDb } from './dialect.js';
8
- import type { EmailConfigInput } from './email.js';
9
8
  import type { IdempotencyConfig } from './idempotency.js';
9
+ import type { MessagingConfig } from './messaging/index.js';
10
10
  import type { RateLimitConfig } from './rate-limit.js';
11
11
  import { type BaseEnv, type EnvConfigInput, type ValidatedEnv } from './env.js';
12
12
  import { type ResolvedStorageBuckets, type StorageConfigInput } from './storage/buckets.js';
@@ -21,7 +21,7 @@ export type AuthConfigContext<TSchema extends Record<string, unknown>, TEnv exte
21
21
  db: DbFor<TSchema>;
22
22
  env: ValidatedEnv<TEnv>;
23
23
  };
24
- export type AuthConfigInput<TSchema extends Record<string, unknown>, TEnv extends EnvConfigInput | undefined = undefined> = BetterAuthConfig | ((ctx: AuthConfigContext<TSchema, TEnv>) => BetterAuthConfig);
24
+ export type AuthConfigInput<TSchema extends Record<string, unknown>, TEnv extends EnvConfigInput | undefined = undefined, TConfig extends BetterAuthConfig = BetterAuthConfig> = TConfig | ((ctx: AuthConfigContext<TSchema, TEnv>) => TConfig);
25
25
  /**
26
26
  * Identity helper for defining an auth config in a separate file with full
27
27
  * type inference — no explicit generic annotations required.
@@ -43,8 +43,12 @@ export type AuthConfigInput<TSchema extends Record<string, unknown>, TEnv extend
43
43
  * }))
44
44
  * ```
45
45
  */
46
- export declare function defineAuth(config: BetterAuthConfig): BetterAuthConfig;
47
- export declare function defineAuth<TSchema extends Record<string, unknown>, TEnv extends EnvConfigInput | undefined = undefined>(schema: TSchema, builder: (ctx: AuthConfigContext<TSchema, TEnv>) => BetterAuthConfig): (ctx: AuthConfigContext<TSchema, TEnv>) => BetterAuthConfig;
46
+ export declare function defineAuth<const TConfig extends BetterAuthConfig>(config: TConfig): TConfig;
47
+ export declare function defineAuth<TSchema extends Record<string, unknown>, TEnv extends EnvConfigInput | undefined, const TConfig extends BetterAuthConfig>(context: {
48
+ schema: TSchema;
49
+ env: TEnv;
50
+ }, builder: (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig): (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig;
51
+ export declare function defineAuth<TSchema extends Record<string, unknown>, const TConfig extends BetterAuthConfig, TEnv extends EnvConfigInput | undefined = undefined>(schema: TSchema, builder: (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig): (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig;
48
52
  /**
49
53
  * The builder as {@link ResolvedConfig} carries it: schema-agnostic, because
50
54
  * ResolvedConfig is not generic. {@link resolveAuthConfig} is the only caller.
@@ -53,7 +57,16 @@ export type AuthConfigFactory = (ctx: {
53
57
  db: AnyDb;
54
58
  env: BaseEnv;
55
59
  }) => BetterAuthConfig;
56
- export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = Record<string, TableAccessInput> | undefined, TStorage extends StorageConfigInput | undefined = StorageConfigInput | undefined, TEnv extends EnvConfigInput | undefined = EnvConfigInput | undefined, TCustomApiRouter extends AnyRouter | undefined = AnyRouter | undefined> = {
60
+ /** Realtime declaration. Independent of schema, storage, and env. */
61
+ export type RealtimeConfigInput = boolean | {
62
+ bufferSize?: number;
63
+ resumeSeconds?: number;
64
+ redis?: string | {
65
+ url: string;
66
+ token?: string;
67
+ };
68
+ };
69
+ export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = Record<string, TableAccessInput> | undefined, TStorage extends StorageConfigInput | undefined = StorageConfigInput | undefined, TEnv extends EnvConfigInput | undefined = EnvConfigInput | undefined, TCustomApiRouter extends AnyRouter | undefined = AnyRouter | undefined, TAuthConfig extends BetterAuthConfig = BetterAuthConfig> = {
57
70
  schema: TSchema;
58
71
  access?: TAccess;
59
72
  database: {
@@ -68,18 +81,17 @@ export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess e
68
81
  * connection, so the application never opens a second one just to satisfy a
69
82
  * config that is built before the app exists.
70
83
  */
71
- auth?: AuthConfigInput<NoInfer<TSchema>, NoInfer<TEnv>>;
84
+ auth?: AuthConfigInput<NoInfer<TSchema>, NoInfer<TEnv>, TAuthConfig>;
72
85
  /**
73
86
  * Reuse an application-owned session reader for the unified API while
74
87
  * keeping Bunderstack's auth handler available.
75
88
  */
76
89
  authResolver?: AuthSessionResolver;
77
90
  storage?: TStorage;
78
- env?: TEnv;
79
91
  background?: {
80
92
  autoStart?: boolean;
81
93
  };
82
- email?: EmailConfigInput;
94
+ messaging?: MessagingConfig;
83
95
  /**
84
96
  * The application's oRPC router. Pass the finished router object, or a
85
97
  * callback when the router needs the framework builder at configuration
@@ -100,14 +112,7 @@ export type BunderstackConfig<TSchema extends Record<string, unknown>, TAccess e
100
112
  idempotency?: boolean | IdempotencyConfig;
101
113
  /** Generate and serve `/api/openapi.json`. Disabled by default. */
102
114
  openapi?: boolean;
103
- realtime?: boolean | {
104
- bufferSize?: number;
105
- resumeSeconds?: number;
106
- redis?: string | {
107
- url: string;
108
- token?: string;
109
- };
110
- };
115
+ realtime?: RealtimeConfigInput;
111
116
  };
112
117
  export type ResolvedConfig = {
113
118
  database: {
@@ -118,16 +123,9 @@ export type ResolvedConfig = {
118
123
  };
119
124
  auth: BetterAuthConfig | AuthConfigFactory;
120
125
  storage: ResolvedStorageBuckets;
121
- realtime?: boolean | {
122
- bufferSize?: number;
123
- resumeSeconds?: number;
124
- redis?: string | {
125
- url: string;
126
- token?: string;
127
- };
128
- };
126
+ realtime?: RealtimeConfigInput;
129
127
  };
130
- export declare function resolveConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = undefined, TStorage extends StorageConfigInput | undefined = undefined, TEnv extends EnvConfigInput | undefined = undefined, TCustomApiRouter extends AnyRouter | undefined = undefined>(options: BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>, env?: BaseEnv, platformSource?: Record<string, string | undefined>): ResolvedConfig;
128
+ export declare function resolveConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = undefined, TStorage extends StorageConfigInput | undefined = undefined, TEnv extends EnvConfigInput | undefined = undefined, TCustomApiRouter extends AnyRouter | undefined = undefined, TAuthConfig extends BetterAuthConfig = BetterAuthConfig>(options: BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter, TAuthConfig>, env?: BaseEnv, platformSource?: Record<string, string | undefined>): ResolvedConfig;
131
129
  /** Collapse the object/builder union once the db is up. */
132
130
  export declare function resolveAuthConfig(auth: ResolvedConfig['auth'], ctx: {
133
131
  db: AnyDb;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -38,7 +38,7 @@ platformSource = process.env) {
38
38
  const parsed = v.parse(RuntimeOptionsSchema, options);
39
39
  // Self-validate when the caller didn't pass a pre-validated env, so
40
40
  // resolveConfig stays usable standalone.
41
- const resolvedEnv = env ?? validateEnv(options.env);
41
+ const resolvedEnv = env ?? validateEnv(undefined);
42
42
  const adapter = options.database?.adapter;
43
43
  if (!adapter) {
44
44
  throw new Error('[bunderstack] database.adapter is required');
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyMiddleware, AnyRouter } from '@orpc/server'\n\n// src/config.ts\nimport { betterAuth } from 'better-auth'\nimport * as v from 'valibot'\n\nimport type { AuthSessionResolver, TableAccessInput } from './access'\nimport type { BunderstackApiBuilder } from './api/builder'\nimport type { DatabaseAdapter } from './database/adapter'\nimport type { DbFor } from './db'\nimport type { AnyDb } from './dialect'\nimport type { EmailConfigInput } from './email'\nimport type { IdempotencyConfig } from './idempotency'\nimport type { RateLimitConfig } from './rate-limit'\n\nimport {\n validateEnv,\n type BaseEnv,\n type EnvConfigInput,\n type ValidatedEnv,\n} from './env'\nimport {\n resolveBuckets,\n type ResolvedStorageBuckets,\n type StorageConfigInput,\n} from './storage/buckets'\n\nexport type BetterAuthConfig = Omit<\n NonNullable<Parameters<typeof betterAuth>[0]>,\n 'database'\n>\n\n/**\n * What an `auth` builder is handed. `db` is the app's own connection, typed\n * from `schema` alone — that is what keeps the builder cycle-free: better-auth\n * hooks living in another file get a db without importing the app whose type\n * they help produce.\n */\nexport type AuthConfigContext<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n> = {\n db: DbFor<TSchema>\n env: ValidatedEnv<TEnv>\n}\n\nexport type AuthConfigInput<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n> =\n | BetterAuthConfig\n | ((ctx: AuthConfigContext<TSchema, TEnv>) => BetterAuthConfig)\n\n/**\n * Identity helper for defining an auth config in a separate file with full\n * type inference — no explicit generic annotations required.\n *\n * Follows the `defineConfig` / `defineComponent` ecosystem convention:\n * the function does nothing at runtime but gives TypeScript an inference\n * anchor through the `schema` parameter.\n *\n * @example\n * ```ts\n * // Static config (no db/env access needed)\n * export const authConfig = defineAuth({ ... })\n *\n * // Builder with schema-typed db\n * export const authConfig = defineAuth(schema, ({ db }) => ({\n * databaseHooks: {\n * user: { create: { after: async (user) => { await db.insert(...) } } }\n * }\n * }))\n * ```\n */\nexport function defineAuth(config: BetterAuthConfig): BetterAuthConfig\nexport function defineAuth<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n>(\n schema: TSchema,\n builder: (ctx: AuthConfigContext<TSchema, TEnv>) => BetterAuthConfig,\n): (ctx: AuthConfigContext<TSchema, TEnv>) => BetterAuthConfig\nexport function defineAuth(\n schemaOrConfig: Record<string, unknown>,\n builder?: (ctx: any) => BetterAuthConfig,\n) {\n return builder ?? schemaOrConfig\n}\n\n/**\n * The builder as {@link ResolvedConfig} carries it: schema-agnostic, because\n * ResolvedConfig is not generic. {@link resolveAuthConfig} is the only caller.\n */\nexport type AuthConfigFactory = (ctx: {\n db: AnyDb\n env: BaseEnv\n}) => BetterAuthConfig\n\n// Only the union-shaped options need runtime validation: they are the ones a\n// JavaScript caller can plausibly get wrong in a way that fails confusingly\n// downstream. Everything else is either typed-only or read raw from `options`.\nconst RuntimeOptionsSchema = v.object({\n rateLimit: v.optional(\n v.union([\n v.boolean(),\n v.object({\n windowMs: v.optional(v.number()),\n max: v.optional(v.number()),\n }),\n ]),\n ),\n idempotency: v.optional(\n v.union([v.boolean(), v.object({ ttlMs: v.optional(v.number()) })]),\n ),\n realtime: v.optional(\n v.union([\n v.boolean(),\n v.object({\n bufferSize: v.optional(v.number()),\n resumeSeconds: v.optional(v.number()),\n redis: v.optional(\n v.union([\n v.string(),\n v.object({ url: v.string(), token: v.optional(v.string()) }),\n ]),\n ),\n }),\n ]),\n ),\n openapi: v.optional(v.boolean()),\n})\n\nexport type BunderstackConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined =\n | Record<string, TableAccessInput>\n | undefined,\n TStorage extends StorageConfigInput | undefined =\n | StorageConfigInput\n | undefined,\n TEnv extends EnvConfigInput | undefined = EnvConfigInput | undefined,\n TCustomApiRouter extends AnyRouter | undefined = AnyRouter | undefined,\n> = {\n schema: TSchema\n access?: TAccess\n database: {\n adapter: DatabaseAdapter\n url?: string\n authToken?: string\n migrations?: string\n }\n /**\n * better-auth options, or a builder receiving `{ db, env }`. Use the builder\n * form when database hooks need to write: it hands out the app's own\n * connection, so the application never opens a second one just to satisfy a\n * config that is built before the app exists.\n */\n auth?: AuthConfigInput<NoInfer<TSchema>, NoInfer<TEnv>>\n /**\n * Reuse an application-owned session reader for the unified API while\n * keeping Bunderstack's auth handler available.\n */\n authResolver?: AuthSessionResolver\n storage?: TStorage\n env?: TEnv\n background?: { autoStart?: boolean }\n email?: EmailConfigInput\n /**\n * The application's oRPC router. Pass the finished router object, or a\n * callback when the router needs the framework builder at configuration\n * time. Declare the router at module scope with `defineApi` and the object\n * form keeps router modules free of factory wrappers.\n */\n api?:\n | TCustomApiRouter\n | ((\n builder: BunderstackApiBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TCustomApiRouter)\n /**\n * Middleware applied to every procedure in the graph: generated CRUD,\n * storage, realtime, health, and the application's own procedures. Declare\n * each one with `o.middleware(...)` from `defineApi`.\n *\n * It runs before authentication, so `context.user` is not available inside\n * it. Read an already-resolved caller with `context.peekSession()`.\n */\n middleware?: AnyMiddleware[]\n rateLimit?: boolean | RateLimitConfig\n idempotency?: boolean | IdempotencyConfig\n /** Generate and serve `/api/openapi.json`. Disabled by default. */\n openapi?: boolean\n realtime?:\n | boolean\n | {\n bufferSize?: number\n resumeSeconds?: number\n redis?: string | { url: string; token?: string }\n }\n}\n\nexport type ResolvedConfig = {\n database: {\n adapter: DatabaseAdapter\n url: string\n authToken?: string\n migrations: string\n }\n auth: BetterAuthConfig | AuthConfigFactory\n storage: ResolvedStorageBuckets\n realtime?:\n | boolean\n | {\n bufferSize?: number\n resumeSeconds?: number\n redis?: string | { url: string; token?: string }\n }\n}\n\nexport function resolveConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TCustomApiRouter extends AnyRouter | undefined = undefined,\n>(\n options: BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter\n >,\n env?: BaseEnv,\n // Platform-injected overrides (Bunderhost & co.) beat code-level config so\n // apps with hardcoded local urls deploy unchanged.\n platformSource: Record<string, string | undefined> = process.env as Record<\n string,\n string | undefined\n >,\n): ResolvedConfig {\n const parsed = v.parse(RuntimeOptionsSchema, options)\n // Self-validate when the caller didn't pass a pre-validated env, so\n // resolveConfig stays usable standalone.\n const resolvedEnv =\n env ?? validateEnv(options.env as EnvConfigInput | undefined)\n\n const adapter = options.database?.adapter\n if (!adapter) {\n throw new Error('[bunderstack] database.adapter is required')\n }\n\n const defaultUrl =\n adapter.dialect === 'sqlite' ? 'file:./data.db' : 'file:./data.pglite'\n\n return {\n database: {\n adapter,\n url:\n platformSource['BUNDERSTACK_DATABASE_URL'] ??\n options.database?.url ??\n resolvedEnv.DATABASE_URL ??\n defaultUrl,\n authToken:\n platformSource['BUNDERSTACK_DATABASE_AUTH_TOKEN'] ??\n options.database?.authToken ??\n resolvedEnv.DATABASE_AUTH_TOKEN,\n migrations: options.database?.migrations ?? './migrations',\n },\n auth: (() => {\n // The secret default has to survive the builder form too, and the builder\n // can only run once the db exists — so wrap it and default afterwards.\n const withSecret = (cfg: BetterAuthConfig): BetterAuthConfig => ({\n ...cfg,\n secret: cfg.secret ?? resolvedEnv.AUTH_SECRET,\n })\n const authInput = options.auth\n return typeof authInput === 'function'\n ? (((ctx) =>\n withSecret(authInput(ctx as never))) satisfies AuthConfigFactory)\n : withSecret(authInput ?? {})\n })(),\n storage: resolveBuckets(options.storage, platformSource),\n realtime: parsed.realtime,\n }\n}\n\n/** Collapse the object/builder union once the db is up. */\nexport function resolveAuthConfig(\n auth: ResolvedConfig['auth'],\n ctx: { db: AnyDb; env: BaseEnv },\n): BetterAuthConfig {\n return typeof auth === 'function' ? auth(ctx) : auth\n}\n\nexport function resolveRealtimeRedisUrl(\n realtime: ResolvedConfig['realtime'],\n env?: BaseEnv,\n platformSource: Record<string, string | undefined> = process.env as Record<\n string,\n string | undefined\n >,\n): string | undefined {\n const platformRedis = platformSource['REDIS_URL']\n if (platformRedis) return platformRedis\n\n const envRedis = env?.REDIS_URL\n if (envRedis) return envRedis\n\n const fromConfig =\n typeof realtime === 'object' && realtime.redis\n ? typeof realtime.redis === 'string'\n ? realtime.redis\n : realtime.redis.url\n : undefined\n\n return fromConfig ?? undefined\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyMiddleware, AnyRouter } from '@orpc/server'\n\n// src/config.ts\nimport { betterAuth } from 'better-auth'\nimport * as v from 'valibot'\n\nimport type { AuthSessionResolver, TableAccessInput } from './access'\nimport type { BunderstackApiBuilder } from './api/builder'\nimport type { DatabaseAdapter } from './database/adapter'\nimport type { DbFor } from './db'\nimport type { AnyDb } from './dialect'\nimport type { IdempotencyConfig } from './idempotency'\nimport type { MessagingConfig } from './messaging'\nimport type { RateLimitConfig } from './rate-limit'\n\nimport {\n validateEnv,\n type BaseEnv,\n type EnvConfigInput,\n type ValidatedEnv,\n} from './env'\nimport {\n resolveBuckets,\n type ResolvedStorageBuckets,\n type StorageConfigInput,\n} from './storage/buckets'\n\nexport type BetterAuthConfig = Omit<\n NonNullable<Parameters<typeof betterAuth>[0]>,\n 'database'\n>\n\n/**\n * What an `auth` builder is handed. `db` is the app's own connection, typed\n * from `schema` alone — that is what keeps the builder cycle-free: better-auth\n * hooks living in another file get a db without importing the app whose type\n * they help produce.\n */\nexport type AuthConfigContext<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n> = {\n db: DbFor<TSchema>\n env: ValidatedEnv<TEnv>\n}\n\nexport type AuthConfigInput<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n TConfig extends BetterAuthConfig = BetterAuthConfig,\n> = TConfig | ((ctx: AuthConfigContext<TSchema, TEnv>) => TConfig)\n\n/**\n * Identity helper for defining an auth config in a separate file with full\n * type inference — no explicit generic annotations required.\n *\n * Follows the `defineConfig` / `defineComponent` ecosystem convention:\n * the function does nothing at runtime but gives TypeScript an inference\n * anchor through the `schema` parameter.\n *\n * @example\n * ```ts\n * // Static config (no db/env access needed)\n * export const authConfig = defineAuth({ ... })\n *\n * // Builder with schema-typed db\n * export const authConfig = defineAuth(schema, ({ db }) => ({\n * databaseHooks: {\n * user: { create: { after: async (user) => { await db.insert(...) } } }\n * }\n * }))\n * ```\n */\nexport function defineAuth<const TConfig extends BetterAuthConfig>(\n config: TConfig,\n): TConfig\nexport function defineAuth<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined,\n const TConfig extends BetterAuthConfig,\n>(\n context: { schema: TSchema; env: TEnv },\n builder: (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig,\n): (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig\nexport function defineAuth<\n TSchema extends Record<string, unknown>,\n const TConfig extends BetterAuthConfig,\n TEnv extends EnvConfigInput | undefined = undefined,\n>(\n schema: TSchema,\n builder: (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig,\n): (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig\nexport function defineAuth(\n schemaOrConfig: Record<string, unknown>,\n builder?: (ctx: any) => BetterAuthConfig,\n) {\n return builder ?? schemaOrConfig\n}\n\n/**\n * The builder as {@link ResolvedConfig} carries it: schema-agnostic, because\n * ResolvedConfig is not generic. {@link resolveAuthConfig} is the only caller.\n */\nexport type AuthConfigFactory = (ctx: {\n db: AnyDb\n env: BaseEnv\n}) => BetterAuthConfig\n\n// Only the union-shaped options need runtime validation: they are the ones a\n// JavaScript caller can plausibly get wrong in a way that fails confusingly\n// downstream. Everything else is either typed-only or read raw from `options`.\nconst RuntimeOptionsSchema = v.object({\n rateLimit: v.optional(\n v.union([\n v.boolean(),\n v.object({\n windowMs: v.optional(v.number()),\n max: v.optional(v.number()),\n }),\n ]),\n ),\n idempotency: v.optional(\n v.union([v.boolean(), v.object({ ttlMs: v.optional(v.number()) })]),\n ),\n realtime: v.optional(\n v.union([\n v.boolean(),\n v.object({\n bufferSize: v.optional(v.number()),\n resumeSeconds: v.optional(v.number()),\n redis: v.optional(\n v.union([\n v.string(),\n v.object({ url: v.string(), token: v.optional(v.string()) }),\n ]),\n ),\n }),\n ]),\n ),\n openapi: v.optional(v.boolean()),\n})\n\n/** Realtime declaration. Independent of schema, storage, and env. */\nexport type RealtimeConfigInput =\n | boolean\n | {\n bufferSize?: number\n resumeSeconds?: number\n redis?: string | { url: string; token?: string }\n }\n\nexport type BunderstackConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined =\n | Record<string, TableAccessInput>\n | undefined,\n TStorage extends StorageConfigInput | undefined =\n | StorageConfigInput\n | undefined,\n TEnv extends EnvConfigInput | undefined = EnvConfigInput | undefined,\n TCustomApiRouter extends AnyRouter | undefined = AnyRouter | undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n> = {\n schema: TSchema\n access?: TAccess\n database: {\n adapter: DatabaseAdapter\n url?: string\n authToken?: string\n migrations?: string\n }\n /**\n * better-auth options, or a builder receiving `{ db, env }`. Use the builder\n * form when database hooks need to write: it hands out the app's own\n * connection, so the application never opens a second one just to satisfy a\n * config that is built before the app exists.\n */\n auth?: AuthConfigInput<NoInfer<TSchema>, NoInfer<TEnv>, TAuthConfig>\n /**\n * Reuse an application-owned session reader for the unified API while\n * keeping Bunderstack's auth handler available.\n */\n authResolver?: AuthSessionResolver\n storage?: TStorage\n background?: { autoStart?: boolean }\n messaging?: MessagingConfig\n /**\n * The application's oRPC router. Pass the finished router object, or a\n * callback when the router needs the framework builder at configuration\n * time. Declare the router at module scope with `defineApi` and the object\n * form keeps router modules free of factory wrappers.\n */\n api?:\n | TCustomApiRouter\n | ((\n builder: BunderstackApiBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TCustomApiRouter)\n /**\n * Middleware applied to every procedure in the graph: generated CRUD,\n * storage, realtime, health, and the application's own procedures. Declare\n * each one with `o.middleware(...)` from `defineApi`.\n *\n * It runs before authentication, so `context.user` is not available inside\n * it. Read an already-resolved caller with `context.peekSession()`.\n */\n middleware?: AnyMiddleware[]\n rateLimit?: boolean | RateLimitConfig\n idempotency?: boolean | IdempotencyConfig\n /** Generate and serve `/api/openapi.json`. Disabled by default. */\n openapi?: boolean\n realtime?: RealtimeConfigInput\n}\n\nexport type ResolvedConfig = {\n database: {\n adapter: DatabaseAdapter\n url: string\n authToken?: string\n migrations: string\n }\n auth: BetterAuthConfig | AuthConfigFactory\n storage: ResolvedStorageBuckets\n realtime?: RealtimeConfigInput\n}\n\nexport function resolveConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TCustomApiRouter extends AnyRouter | undefined = undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n>(\n options: BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter,\n TAuthConfig\n >,\n env?: BaseEnv,\n // Platform-injected overrides (Bunderhost & co.) beat code-level config so\n // apps with hardcoded local urls deploy unchanged.\n platformSource: Record<string, string | undefined> = process.env as Record<\n string,\n string | undefined\n >,\n): ResolvedConfig {\n const parsed = v.parse(RuntimeOptionsSchema, options)\n // Self-validate when the caller didn't pass a pre-validated env, so\n // resolveConfig stays usable standalone.\n const resolvedEnv = env ?? validateEnv(undefined)\n\n const adapter = options.database?.adapter\n if (!adapter) {\n throw new Error('[bunderstack] database.adapter is required')\n }\n\n const defaultUrl =\n adapter.dialect === 'sqlite' ? 'file:./data.db' : 'file:./data.pglite'\n\n return {\n database: {\n adapter,\n url:\n platformSource['BUNDERSTACK_DATABASE_URL'] ??\n options.database?.url ??\n resolvedEnv.DATABASE_URL ??\n defaultUrl,\n authToken:\n platformSource['BUNDERSTACK_DATABASE_AUTH_TOKEN'] ??\n options.database?.authToken ??\n resolvedEnv.DATABASE_AUTH_TOKEN,\n migrations: options.database?.migrations ?? './migrations',\n },\n auth: (() => {\n // The secret default has to survive the builder form too, and the builder\n // can only run once the db exists — so wrap it and default afterwards.\n const withSecret = (cfg: BetterAuthConfig): BetterAuthConfig => ({\n ...cfg,\n secret: cfg.secret ?? resolvedEnv.AUTH_SECRET,\n })\n const authInput = options.auth\n return typeof authInput === 'function'\n ? (((ctx) =>\n withSecret(authInput(ctx as never))) satisfies AuthConfigFactory)\n : withSecret(authInput ?? {})\n })(),\n storage: resolveBuckets(options.storage, platformSource),\n realtime: parsed.realtime,\n }\n}\n\n/** Collapse the object/builder union once the db is up. */\nexport function resolveAuthConfig(\n auth: ResolvedConfig['auth'],\n ctx: { db: AnyDb; env: BaseEnv },\n): BetterAuthConfig {\n return typeof auth === 'function' ? auth(ctx) : auth\n}\n\nexport function resolveRealtimeRedisUrl(\n realtime: ResolvedConfig['realtime'],\n env?: BaseEnv,\n platformSource: Record<string, string | undefined> = process.env as Record<\n string,\n string | undefined\n >,\n): string | undefined {\n const platformRedis = platformSource['REDIS_URL']\n if (platformRedis) return platformRedis\n\n const envRedis = env?.REDIS_URL\n if (envRedis) return envRedis\n\n const fromConfig =\n typeof realtime === 'object' && realtime.redis\n ? typeof realtime.redis === 'string'\n ? realtime.redis\n : realtime.redis.url\n : undefined\n\n return fromConfig ?? undefined\n}\n"]}
@@ -1,4 +1,6 @@
1
1
  import type { EmailAdapter } from '../email.js';
2
+ import type { EmailMessage, SentEmail } from '../email.js';
3
+ import type { MessagingDescriptor } from '../messaging/types.js';
2
4
  type SmtpTransport = {
3
5
  sendMail(message: Record<string, unknown>): Promise<{
4
6
  messageId?: string;
@@ -9,6 +11,10 @@ export declare function createSmtpAdapter(options: {
9
11
  }, createTransport?: (url: string) => SmtpTransport): EmailAdapter;
10
12
  export declare const smtp: (options: {
11
13
  url: string;
12
- }) => EmailAdapter;
14
+ from?: string;
15
+ }) => MessagingDescriptor<"email", "smtp", EmailMessage, SentEmail, {
16
+ adapter: EmailAdapter;
17
+ from?: string;
18
+ }>;
13
19
  export {};
14
20
  //# sourceMappingURL=smtp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"smtp.d.ts","sourceRoot":"","sources":["../../src/email/smtp.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC5E,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACxB,eAAe,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aACgB,GACjD,YAAY,CA6Bd;AAED,eAAO,MAAM,IAAI,GAAI,SAAS;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,KAAG,YACpB,CAAA"}
1
+ {"version":3,"file":"smtp.d.ts","sourceRoot":"","sources":["../../src/email/smtp.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAI7D,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC5E,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACxB,eAAe,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aACgB,GACjD,YAAY,CA6Bd;AAED,eAAO,MAAM,IAAI,GAAI,SAAS;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,KAAG,mBAAmB,CACrB,OAAO,EACP,MAAM,EACN,YAAY,EACZ,SAAS,EACT;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAKtC,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import nodemailer from 'nodemailer';
2
+ import { descriptor } from '../messaging/types.js';
2
3
  export function createSmtpAdapter(options, createTransport = (url) => nodemailer.createTransport(url)) {
3
4
  const toArray = (v) => v === undefined ? undefined : Array.isArray(v) ? v : [v];
4
5
  let transportPromise = null;
@@ -23,5 +24,8 @@ export function createSmtpAdapter(options, createTransport = (url) => nodemailer
23
24
  },
24
25
  };
25
26
  }
26
- export const smtp = (options) => createSmtpAdapter(options);
27
+ export const smtp = (options) => descriptor('email', 'smtp', {
28
+ adapter: createSmtpAdapter(options),
29
+ from: options.from,
30
+ });
27
31
  //# sourceMappingURL=smtp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"smtp.js","sourceRoot":"","sources":["../../src/email/smtp.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AAQnC,MAAM,UAAU,iBAAiB,CAC/B,OAAwB,EACxB,kBAAkD,CAAC,GAAG,EAAE,EAAE,CACxD,UAAU,CAAC,eAAe,CAAC,GAAG,CAAkB;IAElD,MAAM,OAAO,GAAG,CAAC,CAAgC,EAAE,EAAE,CACnD,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAE1D,IAAI,gBAAgB,GAER,IAAI,CAAA;IAEhB,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QAClE,OAAO,gBAAgB,CAAA;IACzB,CAAC,CAAA;IAED,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAA;YACtC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC;gBACpC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;gBAC/B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;aAClC,CAAC,CAAA;YACF,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAA;QAC/B,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAwB,EAAgB,EAAE,CAC7D,iBAAiB,CAAC,OAAO,CAAC,CAAA","sourcesContent":["import nodemailer from 'nodemailer'\n\nimport type { EmailAdapter } from '../email'\n\ntype SmtpTransport = {\n sendMail(message: Record<string, unknown>): Promise<{ messageId?: string }>\n}\n\nexport function createSmtpAdapter(\n options: { url: string },\n createTransport: (url: string) => SmtpTransport = (url) =>\n nodemailer.createTransport(url) as SmtpTransport,\n): EmailAdapter {\n const toArray = (v: string | string[] | undefined) =>\n v === undefined ? undefined : Array.isArray(v) ? v : [v]\n\n let transportPromise: Promise<{\n sendMail(opts: Record<string, unknown>): Promise<{ messageId?: string }>\n }> | null = null\n\n const getTransport = () => {\n transportPromise ??= Promise.resolve(createTransport(options.url))\n return transportPromise\n }\n\n return {\n async send(msg) {\n const transport = await getTransport()\n const info = await transport.sendMail({\n from: msg.from,\n to: toArray(msg.to)!.join(', '),\n subject: msg.subject,\n html: msg.html,\n text: msg.text,\n replyTo: msg.replyTo,\n cc: toArray(msg.cc)?.join(', '),\n bcc: toArray(msg.bcc)?.join(', '),\n })\n return { id: info.messageId }\n },\n }\n}\n\nexport const smtp = (options: { url: string }): EmailAdapter =>\n createSmtpAdapter(options)\n"]}
1
+ {"version":3,"file":"smtp.js","sourceRoot":"","sources":["../../src/email/smtp.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AAMnC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAM/C,MAAM,UAAU,iBAAiB,CAC/B,OAAwB,EACxB,kBAAkD,CAAC,GAAG,EAAE,EAAE,CACxD,UAAU,CAAC,eAAe,CAAC,GAAG,CAAkB;IAElD,MAAM,OAAO,GAAG,CAAC,CAAgC,EAAE,EAAE,CACnD,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAE1D,IAAI,gBAAgB,GAER,IAAI,CAAA;IAEhB,MAAM,YAAY,GAAG,GAAG,EAAE;QACxB,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QAClE,OAAO,gBAAgB,CAAA;IACzB,CAAC,CAAA;IAED,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAA;YACtC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC;gBACpC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC/B,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;gBAC/B,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;aAClC,CAAC,CAAA;YACF,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAA;QAC/B,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAGpB,EAMC,EAAE,CACF,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE;IAC1B,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;IACnC,IAAI,EAAE,OAAO,CAAC,IAAI;CACnB,CAAC,CAAA","sourcesContent":["import nodemailer from 'nodemailer'\n\nimport type { EmailAdapter } from '../email'\nimport type { EmailMessage, SentEmail } from '../email'\nimport type { MessagingDescriptor } from '../messaging/types'\n\nimport { descriptor } from '../messaging/types'\n\ntype SmtpTransport = {\n sendMail(message: Record<string, unknown>): Promise<{ messageId?: string }>\n}\n\nexport function createSmtpAdapter(\n options: { url: string },\n createTransport: (url: string) => SmtpTransport = (url) =>\n nodemailer.createTransport(url) as SmtpTransport,\n): EmailAdapter {\n const toArray = (v: string | string[] | undefined) =>\n v === undefined ? undefined : Array.isArray(v) ? v : [v]\n\n let transportPromise: Promise<{\n sendMail(opts: Record<string, unknown>): Promise<{ messageId?: string }>\n }> | null = null\n\n const getTransport = () => {\n transportPromise ??= Promise.resolve(createTransport(options.url))\n return transportPromise\n }\n\n return {\n async send(msg) {\n const transport = await getTransport()\n const info = await transport.sendMail({\n from: msg.from,\n to: toArray(msg.to)!.join(', '),\n subject: msg.subject,\n html: msg.html,\n text: msg.text,\n replyTo: msg.replyTo,\n cc: toArray(msg.cc)?.join(', '),\n bcc: toArray(msg.bcc)?.join(', '),\n })\n return { id: info.messageId }\n },\n }\n}\n\nexport const smtp = (options: {\n url: string\n from?: string\n}): MessagingDescriptor<\n 'email',\n 'smtp',\n EmailMessage,\n SentEmail,\n { adapter: EmailAdapter; from?: string }\n> =>\n descriptor('email', 'smtp', {\n adapter: createSmtpAdapter(options),\n from: options.from,\n })\n"]}
package/dist/email.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { AnyDb } from './dialect.js';
2
1
  export type EmailMessage = {
3
2
  to: string | string[];
4
3
  subject: string;
@@ -13,40 +12,15 @@ export type SentEmail = {
13
12
  id?: string;
14
13
  providerId?: string;
15
14
  };
16
- type AdapterSentEmail = {
17
- id?: string;
18
- };
19
15
  /** Adapters receive the message with `from` already resolved. */
20
16
  export type EmailAdapter = {
21
17
  send(msg: EmailMessage & {
22
18
  from: string;
23
- }): Promise<AdapterSentEmail>;
24
- };
25
- export type EmailConfigInput = {
26
- from: string;
27
- provider?: 'resend' | 'console' | EmailAdapter | EmailAdapter['send'];
19
+ }): Promise<{
20
+ id?: string;
21
+ }>;
28
22
  };
29
23
  export type EmailFacade = {
30
24
  send(msg: EmailMessage): Promise<SentEmail>;
31
25
  };
32
- export type CreateEmailOptions = {
33
- env: {
34
- RESEND_API_KEY?: string;
35
- SMTP_URL?: string;
36
- NODE_ENV?: string;
37
- BUNDERSTACK_EMAIL_PROVIDER?: string;
38
- BUNDERSTACK_EMAIL_FROM?: string;
39
- BUNDERHOST_ENVIRONMENT_ID?: string;
40
- };
41
- /** Internal application db. Present when created through bunderstack. */
42
- db?: AnyDb;
43
- /** Test seam for the resend adapter. */
44
- fetchFn?: typeof fetch;
45
- /** Internal runtime substitution used by isolated test fixtures. */
46
- adapterOverride?: EmailAdapter;
47
- };
48
- /** Root string provider tag ('resend' | 'console') or undefined. */
49
- export declare function emailProviderTag(config: EmailConfigInput | undefined): string | undefined;
50
- export declare function createEmail(config: EmailConfigInput | undefined, opts: CreateEmailOptions): EmailFacade;
51
- export {};
52
26
  //# sourceMappingURL=email.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AAItC,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D,KAAK,gBAAgB,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC,iEAAiE;AACjE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE;QACH,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,0BAA0B,CAAC,EAAE,MAAM,CAAA;QACnC,sBAAsB,CAAC,EAAE,MAAM,CAAA;QAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAA;KACnC,CAAA;IACD,yEAAyE;IACzE,EAAE,CAAC,EAAE,KAAK,CAAA;IACV,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;IACtB,oEAAoE;IACpE,eAAe,CAAC,EAAE,YAAY,CAAA;CAC/B,CAAA;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,gBAAgB,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAEpB;AAiJD,wBAAgB,WAAW,CACzB,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,IAAI,EAAE,kBAAkB,GACvB,WAAW,CAmFb"}
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D,iEAAiE;AACjE,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACrE,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;CAC5C,CAAA"}
package/dist/email.js CHANGED
@@ -1,195 +1,2 @@
1
- // src/email.ts — email sending: console / resend / custom adapters.
2
- // SMTP is provided by the static `bunderstack/email-smtp` factory subpath.
3
- import { and, eq } from 'drizzle-orm';
4
- import { emailsTableFor } from './internal-tables.js';
5
- /** Root string provider tag ('resend' | 'console') or undefined. */
6
- export function emailProviderTag(config) {
7
- return typeof config?.provider === 'string' ? config.provider : undefined;
8
- }
9
- const toArray = (v) => v === undefined ? undefined : Array.isArray(v) ? v : [v];
10
- function createConsoleAdapter() {
11
- return {
12
- async send(msg) {
13
- const line = '─'.repeat(60);
14
- console.log([
15
- line,
16
- '📧 email (console provider — not sent)',
17
- `from: ${msg.from}`,
18
- `to: ${toArray(msg.to).join(', ')}`,
19
- `subject: ${msg.subject}`,
20
- line,
21
- msg.text ?? msg.html ?? '',
22
- line,
23
- ].join('\n'));
24
- return { id: '' };
25
- },
26
- };
27
- }
28
- const captureAdapter = { send: async () => ({}) };
29
- function createResendAdapter(apiKey, fetchFn) {
30
- return {
31
- async send(msg) {
32
- const res = await fetchFn('https://api.resend.com/emails', {
33
- method: 'POST',
34
- headers: {
35
- Authorization: `Bearer ${apiKey}`,
36
- 'Content-Type': 'application/json',
37
- },
38
- body: JSON.stringify({
39
- from: msg.from,
40
- to: toArray(msg.to),
41
- subject: msg.subject,
42
- html: msg.html,
43
- text: msg.text,
44
- reply_to: msg.replyTo,
45
- cc: toArray(msg.cc),
46
- bcc: toArray(msg.bcc),
47
- tags: msg.tags,
48
- }),
49
- });
50
- if (!res.ok) {
51
- throw new Error(`resend API error (${res.status}): ${await res.text()}`);
52
- }
53
- const data = (await res.json());
54
- return { id: data.id ?? '' };
55
- },
56
- };
57
- }
58
- function resolveAdapter(config, opts) {
59
- if (opts.adapterOverride) {
60
- return {
61
- adapter: opts.adapterOverride,
62
- provider: 'capture',
63
- capture: true,
64
- };
65
- }
66
- const managedProvider = opts.env.BUNDERSTACK_EMAIL_PROVIDER;
67
- const provider = managedProvider === 'resend' ? 'resend' : config.provider;
68
- if (typeof provider === 'function') {
69
- return { adapter: { send: provider }, provider: 'custom', capture: false };
70
- }
71
- if (typeof provider === 'object') {
72
- return { adapter: provider, provider: 'custom', capture: false };
73
- }
74
- const fetchFn = opts.fetchFn ?? globalThis.fetch.bind(globalThis);
75
- switch (provider) {
76
- case 'resend':
77
- return {
78
- adapter: createResendAdapter(opts.env.RESEND_API_KEY ?? '', fetchFn),
79
- provider: 'resend',
80
- capture: false,
81
- };
82
- case 'console':
83
- return {
84
- adapter: createConsoleAdapter(),
85
- provider: 'capture',
86
- capture: true,
87
- };
88
- case undefined:
89
- return {
90
- adapter: opts.env.NODE_ENV === 'production'
91
- ? captureAdapter
92
- : createConsoleAdapter(),
93
- provider: 'capture',
94
- capture: true,
95
- };
96
- default:
97
- if (provider === 'smtp') {
98
- throw new Error("email provider 'smtp' was removed. Import `smtp` from 'bunderstack/email-smtp' instead.");
99
- }
100
- return {
101
- adapter: createConsoleAdapter(),
102
- provider: 'capture',
103
- capture: true,
104
- };
105
- }
106
- }
107
- async function insertJournal(db, value) {
108
- if (!db)
109
- return;
110
- const table = emailsTableFor(db);
111
- await db.insert(table).values(value);
112
- }
113
- async function updateJournal(db, id, value, expectedStatus) {
114
- if (!db)
115
- return;
116
- const table = emailsTableFor(db);
117
- await db
118
- .update(table)
119
- .set(value)
120
- .where(expectedStatus
121
- ? and(eq(table.id, id), eq(table.status, expectedStatus))
122
- : eq(table.id, id));
123
- }
124
- export function createEmail(config, opts) {
125
- if (!config) {
126
- return {
127
- async send() {
128
- throw new Error('email is not configured — add an email key to your bunderstack config');
129
- },
130
- };
131
- }
132
- const resolved = resolveAdapter(config, opts);
133
- return {
134
- async send(msg) {
135
- if (!msg.html && !msg.text) {
136
- throw new Error('email message needs html or text content');
137
- }
138
- const id = `email_${crypto.randomUUID()}`;
139
- const now = Date.now();
140
- const from = opts.env.BUNDERSTACK_EMAIL_FROM ?? msg.from ?? config.from;
141
- await insertJournal(opts.db, {
142
- id,
143
- provider: resolved.provider,
144
- status: resolved.capture ? 'captured' : 'sending',
145
- from,
146
- toJson: JSON.stringify(toArray(msg.to) ?? []),
147
- ccJson: JSON.stringify(toArray(msg.cc) ?? []),
148
- bccJson: JSON.stringify(toArray(msg.bcc) ?? []),
149
- replyTo: msg.replyTo,
150
- subject: msg.subject,
151
- html: msg.html,
152
- text: msg.text,
153
- createdAt: now,
154
- updatedAt: now,
155
- });
156
- if (resolved.capture) {
157
- await resolved.adapter.send({ ...msg, from });
158
- return { id };
159
- }
160
- try {
161
- const tags = [
162
- { name: 'bunderstack_email_id', value: id },
163
- ...(opts.env.BUNDERHOST_ENVIRONMENT_ID
164
- ? [
165
- {
166
- name: 'bunderhost_environment_id',
167
- value: opts.env.BUNDERHOST_ENVIRONMENT_ID,
168
- },
169
- ]
170
- : []),
171
- ];
172
- const sent = await resolved.adapter.send({
173
- ...msg,
174
- from,
175
- tags,
176
- });
177
- await updateJournal(opts.db, id, {
178
- providerId: sent.id || undefined,
179
- status: 'sent',
180
- updatedAt: Date.now(),
181
- }, 'sending');
182
- return { id, providerId: sent.id || undefined };
183
- }
184
- catch (error) {
185
- await updateJournal(opts.db, id, {
186
- status: 'failed',
187
- error: error instanceof Error ? error.message : String(error),
188
- updatedAt: Date.now(),
189
- }, 'sending');
190
- throw error;
191
- }
192
- },
193
- };
194
- }
1
+ export {};
195
2
  //# sourceMappingURL=email.js.map