bunderstack 0.23.3 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +6 -4
- package/dist/api/builder.d.ts +13 -11
- package/dist/api/builder.d.ts.map +1 -1
- package/dist/api/builder.js.map +1 -1
- package/dist/api/context.d.ts +6 -6
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +1 -1
- package/dist/api/context.js.map +1 -1
- package/dist/api/crud-router.d.ts +6 -6
- package/dist/api/realtime-router.d.ts +1 -1
- package/dist/api/storage-router.d.ts +5 -5
- package/dist/backend-internals.d.ts +5 -13
- package/dist/backend-internals.d.ts.map +1 -1
- package/dist/backend-internals.js.map +1 -1
- package/dist/backend.d.ts +39 -5
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +37 -46
- package/dist/backend.js.map +1 -1
- package/dist/blueprint-generator.d.ts +1 -0
- package/dist/blueprint-generator.d.ts.map +1 -1
- package/dist/blueprint-generator.js +29 -1
- package/dist/blueprint-generator.js.map +1 -1
- package/dist/blueprint.d.ts +2 -1
- package/dist/blueprint.d.ts.map +1 -1
- package/dist/blueprint.js +9 -2
- package/dist/blueprint.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +13 -19
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/email/smtp.d.ts +7 -1
- package/dist/email/smtp.d.ts.map +1 -1
- package/dist/email/smtp.js +5 -1
- package/dist/email/smtp.js.map +1 -1
- package/dist/email.d.ts +3 -29
- package/dist/email.d.ts.map +1 -1
- package/dist/email.js +1 -194
- package/dist/email.js.map +1 -1
- package/dist/env-probe.d.ts +9 -0
- package/dist/env-probe.d.ts.map +1 -0
- package/dist/env-probe.js +73 -0
- package/dist/env-probe.js.map +1 -0
- package/dist/env.d.ts +2 -5
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +2 -9
- package/dist/env.js.map +1 -1
- package/dist/hosted-contract.d.ts +11 -0
- package/dist/hosted-contract.d.ts.map +1 -0
- package/dist/hosted-contract.js +46 -0
- package/dist/hosted-contract.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +15 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +43 -0
- package/dist/inspect.js.map +1 -0
- package/dist/internal-tables-pg.d.ts +43 -94
- package/dist/internal-tables-pg.d.ts.map +1 -1
- package/dist/internal-tables-pg.js +15 -17
- package/dist/internal-tables-pg.js.map +1 -1
- package/dist/internal-tables.d.ts +213 -488
- package/dist/internal-tables.d.ts.map +1 -1
- package/dist/internal-tables.js +34 -33
- package/dist/internal-tables.js.map +1 -1
- package/dist/jobs/define.d.ts +20 -20
- package/dist/jobs/define.d.ts.map +1 -1
- package/dist/jobs/define.js.map +1 -1
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +34 -22
- package/dist/jobs/worker.js.map +1 -1
- package/dist/manifest-diff.d.ts +6 -0
- package/dist/manifest-diff.d.ts.map +1 -0
- package/dist/manifest-diff.js +42 -0
- package/dist/manifest-diff.js.map +1 -0
- package/dist/manifest.d.ts +10 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +27 -29
- package/dist/manifest.js.map +1 -1
- package/dist/messaging/email.d.ts +16 -0
- package/dist/messaging/email.d.ts.map +1 -0
- package/dist/messaging/email.js +8 -0
- package/dist/messaging/email.js.map +1 -0
- package/dist/messaging/index.d.ts +8 -0
- package/dist/messaging/index.d.ts.map +1 -0
- package/dist/messaging/index.js +4 -0
- package/dist/messaging/index.js.map +1 -0
- package/dist/messaging/journal.d.ts +4 -0
- package/dist/messaging/journal.d.ts.map +1 -0
- package/dist/messaging/journal.js +19 -0
- package/dist/messaging/journal.js.map +1 -0
- package/dist/messaging/runtime.d.ts +16 -0
- package/dist/messaging/runtime.d.ts.map +1 -0
- package/dist/messaging/runtime.js +213 -0
- package/dist/messaging/runtime.js.map +1 -0
- package/dist/messaging/standalone.d.ts +27 -0
- package/dist/messaging/standalone.d.ts.map +1 -0
- package/dist/messaging/standalone.js +18 -0
- package/dist/messaging/standalone.js.map +1 -0
- package/dist/messaging/telegram.d.ts +16 -0
- package/dist/messaging/telegram.d.ts.map +1 -0
- package/dist/messaging/telegram.js +5 -0
- package/dist/messaging/telegram.js.map +1 -0
- package/dist/messaging/types.d.ts +22 -0
- package/dist/messaging/types.d.ts.map +1 -0
- package/dist/messaging/types.js +17 -0
- package/dist/messaging/types.js.map +1 -0
- package/dist/runtime.d.ts +11 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +19 -31
- package/dist/runtime.js.map +1 -1
- package/dist/schema-export-pg.d.ts +1 -1
- package/dist/schema-export-pg.d.ts.map +1 -1
- package/dist/schema-export-pg.js +1 -1
- package/dist/schema-export-pg.js.map +1 -1
- package/dist/schema-export.d.ts +1 -1
- package/dist/schema-export.d.ts.map +1 -1
- package/dist/schema-export.js +1 -1
- package/dist/schema-export.js.map +1 -1
- package/dist/testing/fixture.d.ts +2 -2
- package/dist/testing/fixture.d.ts.map +1 -1
- package/dist/testing/fixture.js +11 -9
- package/dist/testing/fixture.js.map +1 -1
- package/dist/testing/messaging.d.ts +21 -0
- package/dist/testing/messaging.d.ts.map +1 -0
- package/dist/testing/messaging.js +34 -0
- package/dist/testing/messaging.js.map +1 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +6 -2
- package/skills/creating-bunderstack-apps/references/application-structure.md +16 -7
- package/skills/migrating-to-bunderstack/SKILL.md +72 -50
- package/skills/migrating-to-bunderstack/references/audit-checklist.md +2 -2
- package/skills/migrating-to-bunderstack/references/runtime-replacements.md +16 -12
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG5D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AACrE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,MAAM,CAAA;AACjC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAE,
|
|
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(
|
|
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');
|
package/dist/config.js.map
CHANGED
|
@@ -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":"AAEA,gBAAgB;AAChB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAW5B,OAAO,EACL,WAAW,GAIZ,MAAM,OAAO,CAAA;AACd,OAAO,EACL,cAAc,GAGf,MAAM,mBAAmB,CAAA;AAyD1B,MAAM,UAAU,UAAU,CACxB,cAAuC,EACvC,OAAwC;IAExC,OAAO,OAAO,IAAI,cAAc,CAAA;AAClC,CAAC;AAWD,6EAA6E;AAC7E,4EAA4E;AAC5E,+EAA+E;AAC/E,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,SAAS,EAAE,CAAC,CAAC,QAAQ,CACnB,CAAC,CAAC,KAAK,CAAC;QACN,CAAC,CAAC,OAAO,EAAE;QACX,CAAC,CAAC,MAAM,CAAC;YACP,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAChC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SAC5B,CAAC;KACH,CAAC,CACH;IACD,WAAW,EAAE,CAAC,CAAC,QAAQ,CACrB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CACpE;IACD,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAClB,CAAC,CAAC,KAAK,CAAC;QACN,CAAC,CAAC,OAAO,EAAE;QACX,CAAC,CAAC,MAAM,CAAC;YACP,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAClC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YACrC,KAAK,EAAE,CAAC,CAAC,QAAQ,CACf,CAAC,CAAC,KAAK,CAAC;gBACN,CAAC,CAAC,MAAM,EAAE;gBACV,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;aAC7D,CAAC,CACH;SACF,CAAC;KACH,CAAC,CACH;IACD,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;CACjC,CAAC,CAAA;AAoFF,MAAM,UAAU,aAAa,CAO3B,OAMC,EACD,GAAa;AACb,2EAA2E;AAC3E,mDAAmD;AACnD,iBAAqD,OAAO,CAAC,GAG5D;IAED,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAA;IACrD,oEAAoE;IACpE,yCAAyC;IACzC,MAAM,WAAW,GAAG,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAA;IAEjD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAA;IACzC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IAED,MAAM,UAAU,GACd,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,oBAAoB,CAAA;IAExE,OAAO;QACL,QAAQ,EAAE;YACR,OAAO;YACP,GAAG,EACD,cAAc,CAAC,0BAA0B,CAAC;gBAC1C,OAAO,CAAC,QAAQ,EAAE,GAAG;gBACrB,WAAW,CAAC,YAAY;gBACxB,UAAU;YACZ,SAAS,EACP,cAAc,CAAC,iCAAiC,CAAC;gBACjD,OAAO,CAAC,QAAQ,EAAE,SAAS;gBAC3B,WAAW,CAAC,mBAAmB;YACjC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,IAAI,cAAc;SAC3D;QACD,IAAI,EAAE,CAAC,GAAG,EAAE;YACV,0EAA0E;YAC1E,uEAAuE;YACvE,MAAM,UAAU,GAAG,CAAC,GAAqB,EAAoB,EAAE,CAAC,CAAC;gBAC/D,GAAG,GAAG;gBACN,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,WAAW,CAAC,WAAW;aAC9C,CAAC,CAAA;YACF,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAA;YAC9B,OAAO,OAAO,SAAS,KAAK,UAAU;gBACpC,CAAC,CAAE,CAAC,CAAC,GAAG,EAAE,EAAE,CACR,UAAU,CAAC,SAAS,CAAC,GAAY,CAAC,CAAC,CAA8B;gBACrE,CAAC,CAAC,UAAU,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;QACjC,CAAC,CAAC,EAAE;QACJ,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC;QACxD,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAA;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,iBAAiB,CAC/B,IAA4B,EAC5B,GAAgC;IAEhC,OAAO,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,QAAoC,EACpC,GAAa,EACb,iBAAqD,OAAO,CAAC,GAG5D;IAED,MAAM,aAAa,GAAG,cAAc,CAAC,WAAW,CAAC,CAAA;IACjD,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,QAAQ,GAAG,GAAG,EAAE,SAAS,CAAA;IAC/B,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAE7B,MAAM,UAAU,GACd,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,KAAK;QAC5C,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ;YAClC,CAAC,CAAC,QAAQ,CAAC,KAAK;YAChB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;QACtB,CAAC,CAAC,SAAS,CAAA;IAEf,OAAO,UAAU,IAAI,SAAS,CAAA;AAChC,CAAC","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> =\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\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> = {\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 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>(\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 = 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"]}
|
package/dist/email/smtp.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/email/smtp.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/email/smtp.js
CHANGED
|
@@ -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) =>
|
|
27
|
+
export const smtp = (options) => descriptor('email', 'smtp', {
|
|
28
|
+
adapter: createSmtpAdapter(options),
|
|
29
|
+
from: options.from,
|
|
30
|
+
});
|
|
27
31
|
//# sourceMappingURL=smtp.js.map
|
package/dist/email/smtp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smtp.js","sourceRoot":"","sources":["../../src/email/smtp.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;
|
|
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<
|
|
24
|
-
|
|
25
|
-
|
|
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
|
package/dist/email.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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
|
package/dist/email.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"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","sourcesContent":["// src/email.ts — email sending: console / resend / custom adapters.\n// SMTP is provided by the static `bunderstack/email-smtp` factory subpath.\n\nimport { and, eq } from 'drizzle-orm'\n\nimport type { AnyDb } from './dialect'\n\nimport { emailsTableFor } from './internal-tables'\n\nexport type EmailMessage = {\n to: string | string[]\n subject: string\n html?: string\n text?: string\n from?: string\n replyTo?: string\n cc?: string | string[]\n bcc?: string | string[]\n}\n\nexport type SentEmail = { id?: string; providerId?: string }\n\ntype AdapterSentEmail = { id?: string }\n\n/** Adapters receive the message with `from` already resolved. */\nexport type EmailAdapter = {\n send(msg: EmailMessage & { from: string }): Promise<AdapterSentEmail>\n}\n\nexport type EmailConfigInput = {\n from: string\n provider?: 'resend' | 'console' | EmailAdapter | EmailAdapter['send']\n}\n\nexport type EmailFacade = {\n send(msg: EmailMessage): Promise<SentEmail>\n}\n\nexport type CreateEmailOptions = {\n env: {\n RESEND_API_KEY?: string\n SMTP_URL?: string\n NODE_ENV?: string\n BUNDERSTACK_EMAIL_PROVIDER?: string\n BUNDERSTACK_EMAIL_FROM?: string\n BUNDERHOST_ENVIRONMENT_ID?: string\n }\n /** Internal application db. Present when created through bunderstack. */\n db?: AnyDb\n /** Test seam for the resend adapter. */\n fetchFn?: typeof fetch\n /** Internal runtime substitution used by isolated test fixtures. */\n adapterOverride?: EmailAdapter\n}\n\n/** Root string provider tag ('resend' | 'console') or undefined. */\nexport function emailProviderTag(\n config: EmailConfigInput | undefined,\n): string | undefined {\n return typeof config?.provider === 'string' ? config.provider : undefined\n}\n\nconst toArray = (v: string | string[] | undefined) =>\n v === undefined ? undefined : Array.isArray(v) ? v : [v]\n\nfunction createConsoleAdapter(): EmailAdapter {\n return {\n async send(msg) {\n const line = '─'.repeat(60)\n console.log(\n [\n line,\n '📧 email (console provider — not sent)',\n `from: ${msg.from}`,\n `to: ${toArray(msg.to)!.join(', ')}`,\n `subject: ${msg.subject}`,\n line,\n msg.text ?? msg.html ?? '',\n line,\n ].join('\\n'),\n )\n return { id: '' }\n },\n }\n}\n\nconst captureAdapter: EmailAdapter = { send: async () => ({}) }\n\nfunction createResendAdapter(\n apiKey: string,\n fetchFn: typeof fetch,\n): EmailAdapter {\n return {\n async send(msg) {\n const res = await fetchFn('https://api.resend.com/emails', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n from: msg.from,\n to: toArray(msg.to),\n subject: msg.subject,\n html: msg.html,\n text: msg.text,\n reply_to: msg.replyTo,\n cc: toArray(msg.cc),\n bcc: toArray(msg.bcc),\n tags: (msg as EmailMessage & { tags?: unknown }).tags,\n }),\n })\n if (!res.ok) {\n throw new Error(`resend API error (${res.status}): ${await res.text()}`)\n }\n const data = (await res.json()) as { id?: string }\n return { id: data.id ?? '' }\n },\n }\n}\n\nfunction resolveAdapter(\n config: EmailConfigInput,\n opts: CreateEmailOptions,\n): { adapter: EmailAdapter; provider: string; capture: boolean } {\n if (opts.adapterOverride) {\n return {\n adapter: opts.adapterOverride,\n provider: 'capture',\n capture: true,\n }\n }\n const managedProvider = opts.env.BUNDERSTACK_EMAIL_PROVIDER\n const provider = managedProvider === 'resend' ? 'resend' : config.provider\n if (typeof provider === 'function') {\n return { adapter: { send: provider }, provider: 'custom', capture: false }\n }\n if (typeof provider === 'object') {\n return { adapter: provider, provider: 'custom', capture: false }\n }\n const fetchFn = opts.fetchFn ?? globalThis.fetch.bind(globalThis)\n switch (provider) {\n case 'resend':\n return {\n adapter: createResendAdapter(opts.env.RESEND_API_KEY ?? '', fetchFn),\n provider: 'resend',\n capture: false,\n }\n\n case 'console':\n return {\n adapter: createConsoleAdapter(),\n provider: 'capture',\n capture: true,\n }\n case undefined:\n return {\n adapter:\n opts.env.NODE_ENV === 'production'\n ? captureAdapter\n : createConsoleAdapter(),\n provider: 'capture',\n capture: true,\n }\n default:\n if (provider === 'smtp') {\n throw new Error(\n \"email provider 'smtp' was removed. Import `smtp` from 'bunderstack/email-smtp' instead.\",\n )\n }\n return {\n adapter: createConsoleAdapter(),\n provider: 'capture',\n capture: true,\n }\n }\n}\n\nasync function insertJournal(\n db: AnyDb | undefined,\n value: Record<string, unknown>,\n) {\n if (!db) return\n const table = emailsTableFor(db)\n await db.insert(table).values(value)\n}\n\nasync function updateJournal(\n db: AnyDb | undefined,\n id: string,\n value: Record<string, unknown>,\n expectedStatus?: string,\n) {\n if (!db) return\n const table = emailsTableFor(db)\n await db\n .update(table)\n .set(value)\n .where(\n expectedStatus\n ? and(eq(table.id, id), eq(table.status, expectedStatus))\n : eq(table.id, id),\n )\n}\n\nexport function createEmail(\n config: EmailConfigInput | undefined,\n opts: CreateEmailOptions,\n): EmailFacade {\n if (!config) {\n return {\n async send() {\n throw new Error(\n 'email is not configured — add an email key to your bunderstack config',\n )\n },\n }\n }\n const resolved = resolveAdapter(config, opts)\n return {\n async send(msg) {\n if (!msg.html && !msg.text) {\n throw new Error('email message needs html or text content')\n }\n const id = `email_${crypto.randomUUID()}`\n const now = Date.now()\n const from = opts.env.BUNDERSTACK_EMAIL_FROM ?? msg.from ?? config.from\n await insertJournal(opts.db, {\n id,\n provider: resolved.provider,\n status: resolved.capture ? 'captured' : 'sending',\n from,\n toJson: JSON.stringify(toArray(msg.to) ?? []),\n ccJson: JSON.stringify(toArray(msg.cc) ?? []),\n bccJson: JSON.stringify(toArray(msg.bcc) ?? []),\n replyTo: msg.replyTo,\n subject: msg.subject,\n html: msg.html,\n text: msg.text,\n createdAt: now,\n updatedAt: now,\n })\n if (resolved.capture) {\n await resolved.adapter.send({ ...msg, from })\n return { id }\n }\n try {\n const tags = [\n { name: 'bunderstack_email_id', value: id },\n ...(opts.env.BUNDERHOST_ENVIRONMENT_ID\n ? [\n {\n name: 'bunderhost_environment_id',\n value: opts.env.BUNDERHOST_ENVIRONMENT_ID,\n },\n ]\n : []),\n ]\n const sent = await resolved.adapter.send({\n ...msg,\n from,\n tags,\n } as EmailMessage & {\n from: string\n })\n await updateJournal(\n opts.db,\n id,\n {\n providerId: sent.id || undefined,\n status: 'sent',\n updatedAt: Date.now(),\n },\n 'sending',\n )\n return { id, providerId: sent.id || undefined }\n } catch (error) {\n await updateJournal(\n opts.db,\n id,\n {\n status: 'failed',\n error: error instanceof Error ? error.message : String(error),\n updatedAt: Date.now(),\n },\n 'sending',\n )\n throw error\n }\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"","sourcesContent":["export type EmailMessage = {\n to: string | string[]\n subject: string\n html?: string\n text?: string\n from?: string\n replyTo?: string\n cc?: string | string[]\n bcc?: string | string[]\n}\n\nexport type SentEmail = { id?: string; providerId?: string }\n\n/** Adapters receive the message with `from` already resolved. */\nexport type EmailAdapter = {\n send(msg: EmailMessage & { from: string }): Promise<{ id?: string }>\n}\n\nexport type EmailFacade = {\n send(msg: EmailMessage): Promise<SentEmail>\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EnvConfigInput } from './env.js';
|
|
2
|
+
export declare class BlueprintProbeError extends Error {
|
|
3
|
+
constructor(key: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function createEnvProbeSources(envSchema: EnvConfigInput | undefined, configured: Record<string, string | undefined>): readonly [
|
|
6
|
+
Record<string, string | undefined>,
|
|
7
|
+
Record<string, string | undefined>
|
|
8
|
+
];
|
|
9
|
+
//# sourceMappingURL=env-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-probe.d.ts","sourceRoot":"","sources":["../src/env-probe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAyB3C,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,GAAG,EAAE,MAAM;CAOxB;AAmCD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,cAAc,GAAG,SAAS,EACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC7C,SAAS;IACV,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,CAUA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { validateStandardSchema } from './standard-schema.js';
|
|
2
|
+
const CANDIDATES = [
|
|
3
|
+
undefined,
|
|
4
|
+
'',
|
|
5
|
+
'probe',
|
|
6
|
+
'true',
|
|
7
|
+
'false',
|
|
8
|
+
'0',
|
|
9
|
+
'1',
|
|
10
|
+
'https://example.invalid',
|
|
11
|
+
'file:./bunderstack-probe.db',
|
|
12
|
+
'probe@example.invalid',
|
|
13
|
+
'00000000-0000-4000-8000-000000000000',
|
|
14
|
+
];
|
|
15
|
+
const BASE_SOURCE = {
|
|
16
|
+
NODE_ENV: 'production',
|
|
17
|
+
AUTH_SECRET: 'bunderstack-blueprint-probe-secret',
|
|
18
|
+
DATABASE_URL: 'file:./bunderstack-blueprint-probe.db',
|
|
19
|
+
BUNDERSTACK_ROLE: 'all',
|
|
20
|
+
};
|
|
21
|
+
export class BlueprintProbeError extends Error {
|
|
22
|
+
constructor(key) {
|
|
23
|
+
super(`[bunderstack] cannot synthesize a valid blueprint probe for env key ${key}; ` +
|
|
24
|
+
'provide a valid value while generating the blueprint');
|
|
25
|
+
this.name = 'BlueprintProbeError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function identity(value) {
|
|
29
|
+
if (value === undefined)
|
|
30
|
+
return 'undefined';
|
|
31
|
+
if (typeof value === 'number' && Number.isNaN(value))
|
|
32
|
+
return 'number:NaN';
|
|
33
|
+
try {
|
|
34
|
+
return `${typeof value}:${JSON.stringify(value)}`;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return `${typeof value}:${String(value)}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function acceptedValues(key, schema, configured) {
|
|
41
|
+
const accepted = [];
|
|
42
|
+
const outputs = new Set();
|
|
43
|
+
for (const candidate of [configured, ...CANDIDATES]) {
|
|
44
|
+
try {
|
|
45
|
+
const output = validateStandardSchema(schema, candidate, 'env');
|
|
46
|
+
const id = identity(output);
|
|
47
|
+
if (outputs.has(id))
|
|
48
|
+
continue;
|
|
49
|
+
outputs.add(id);
|
|
50
|
+
accepted.push(candidate);
|
|
51
|
+
if (accepted.length === 2)
|
|
52
|
+
return accepted;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Candidate failures are expected. The eventual error names only the key.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (accepted.length === 0)
|
|
59
|
+
throw new BlueprintProbeError(key);
|
|
60
|
+
return accepted;
|
|
61
|
+
}
|
|
62
|
+
export function createEnvProbeSources(envSchema, configured) {
|
|
63
|
+
const first = { ...BASE_SOURCE };
|
|
64
|
+
const second = { ...BASE_SOURCE };
|
|
65
|
+
const variables = { ...envSchema?.server, ...envSchema?.client };
|
|
66
|
+
for (const [key, schema] of Object.entries(variables)) {
|
|
67
|
+
const values = acceptedValues(key, schema, configured[key]);
|
|
68
|
+
first[key] = values[0];
|
|
69
|
+
second[key] = values[1] ?? values[0];
|
|
70
|
+
}
|
|
71
|
+
return [first, second];
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=env-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-probe.js","sourceRoot":"","sources":["../src/env-probe.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { EnvConfigInput } from './env'\n\nimport { validateStandardSchema } from './standard-schema'\n\nconst CANDIDATES: readonly (string | undefined)[] = [\n undefined,\n '',\n 'probe',\n 'true',\n 'false',\n '0',\n '1',\n 'https://example.invalid',\n 'file:./bunderstack-probe.db',\n 'probe@example.invalid',\n '00000000-0000-4000-8000-000000000000',\n]\n\nconst BASE_SOURCE = {\n NODE_ENV: 'production',\n AUTH_SECRET: 'bunderstack-blueprint-probe-secret',\n DATABASE_URL: 'file:./bunderstack-blueprint-probe.db',\n BUNDERSTACK_ROLE: 'all',\n} satisfies Record<string, string | undefined>\n\nexport class BlueprintProbeError extends Error {\n constructor(key: string) {\n super(\n `[bunderstack] cannot synthesize a valid blueprint probe for env key ${key}; ` +\n 'provide a valid value while generating the blueprint',\n )\n this.name = 'BlueprintProbeError'\n }\n}\n\nfunction identity(value: unknown): string {\n if (value === undefined) return 'undefined'\n if (typeof value === 'number' && Number.isNaN(value)) return 'number:NaN'\n try {\n return `${typeof value}:${JSON.stringify(value)}`\n } catch {\n return `${typeof value}:${String(value)}`\n }\n}\n\nfunction acceptedValues(\n key: string,\n schema: StandardSchemaV1,\n configured: string | undefined,\n): (string | undefined)[] {\n const accepted: (string | undefined)[] = []\n const outputs = new Set<string>()\n for (const candidate of [configured, ...CANDIDATES]) {\n try {\n const output = validateStandardSchema(schema, candidate, 'env')\n const id = identity(output)\n if (outputs.has(id)) continue\n outputs.add(id)\n accepted.push(candidate)\n if (accepted.length === 2) return accepted\n } catch {\n // Candidate failures are expected. The eventual error names only the key.\n }\n }\n if (accepted.length === 0) throw new BlueprintProbeError(key)\n return accepted\n}\n\nexport function createEnvProbeSources(\n envSchema: EnvConfigInput | undefined,\n configured: Record<string, string | undefined>,\n): readonly [\n Record<string, string | undefined>,\n Record<string, string | undefined>,\n] {\n const first: Record<string, string | undefined> = { ...BASE_SOURCE }\n const second: Record<string, string | undefined> = { ...BASE_SOURCE }\n const variables = { ...envSchema?.server, ...envSchema?.client }\n for (const [key, schema] of Object.entries(variables)) {\n const values = acceptedValues(key, schema, configured[key])\n first[key] = values[0]\n second[key] = values[1] ?? values[0]\n }\n return [first, second]\n}\n"]}
|
package/dist/env.d.ts
CHANGED
|
@@ -31,9 +31,8 @@ export type BaseEnv = {
|
|
|
31
31
|
AUTH_SECRET: string;
|
|
32
32
|
REDIS_URL?: string;
|
|
33
33
|
RESEND_API_KEY?: string;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
BUNDERSTACK_EMAIL_FROM?: string;
|
|
34
|
+
BUNDERSTACK_MESSAGING_CONFIG?: string;
|
|
35
|
+
BUNDERSTACK_BLUEPRINT_PATH?: string;
|
|
37
36
|
BUNDERHOST_ENVIRONMENT_ID?: string;
|
|
38
37
|
/** Deployed commit SHA, injected by the platform. Reported by readiness. */
|
|
39
38
|
BUNDERSTACK_REVISION?: string;
|
|
@@ -50,8 +49,6 @@ export declare class BunderstackEnvError extends Error {
|
|
|
50
49
|
constructor(issues: string[]);
|
|
51
50
|
}
|
|
52
51
|
export type ValidateEnvOptions = {
|
|
53
|
-
/** String tag of the configured email provider ('resend' | 'smtp'), if any. */
|
|
54
|
-
emailProvider?: string;
|
|
55
52
|
/** Value source; defaults to process.env. Tests pass this explicitly. */
|
|
56
53
|
source?: Record<string, string | undefined>;
|
|
57
54
|
/** Dialect-aware DATABASE_URL fallback; bunderstack passes it. */
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO7D,eAAO,MAAM,aAAa,EAAG,SAAkB,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AAItD,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO7D,eAAO,MAAM,aAAa,EAAG,SAAkB,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AAItD,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,gBAAgB,EAAE,eAAe,CAAA;CAClC,CAAA;AAED,KAAK,SAAS,CAAC,CAAC,IACd,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACtC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtD,OAAO,CAAA;AAGb,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,IAAI;IAClE,IAAI;CACL,SAAS,CAAC,cAAc,CAAC,GACtB,OAAO,GACL,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACtC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACxC,OAAO,CAAA;AAEX,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;gBAEb,MAAM,EAAE,MAAM,EAAE;CAK7B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC3C,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAqCD,wBAAgB,WAAW,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,EACjE,SAAS,EAAE,IAAI,EACf,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAAC,IAAI,CAAC,CAqCpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,EACzD,SAAS,EAAE,IAAI,GACd,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAsB3B"}
|
package/dist/env.js
CHANGED
|
@@ -45,9 +45,8 @@ export function validateEnv(envConfig, options = {}) {
|
|
|
45
45
|
AUTH_SECRET: source.AUTH_SECRET ?? DEV_AUTH_SECRET,
|
|
46
46
|
REDIS_URL: source.REDIS_URL,
|
|
47
47
|
RESEND_API_KEY: source.RESEND_API_KEY,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,
|
|
48
|
+
BUNDERSTACK_MESSAGING_CONFIG: source.BUNDERSTACK_MESSAGING_CONFIG,
|
|
49
|
+
BUNDERSTACK_BLUEPRINT_PATH: source.BUNDERSTACK_BLUEPRINT_PATH,
|
|
51
50
|
BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,
|
|
52
51
|
BUNDERSTACK_REVISION: source.BUNDERSTACK_REVISION,
|
|
53
52
|
BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all'),
|
|
@@ -59,12 +58,6 @@ export function validateEnv(envConfig, options = {}) {
|
|
|
59
58
|
!ROLES.includes(source.BUNDERSTACK_ROLE)) {
|
|
60
59
|
issues.push(`BUNDERSTACK_ROLE: must be one of ${ROLES.join(', ')} (got "${String(source.BUNDERSTACK_ROLE)}")`);
|
|
61
60
|
}
|
|
62
|
-
if (options.emailProvider === 'resend' && !source.RESEND_API_KEY) {
|
|
63
|
-
issues.push("RESEND_API_KEY: required when email provider is 'resend'");
|
|
64
|
-
}
|
|
65
|
-
if (options.emailProvider === 'smtp' && !source.SMTP_URL) {
|
|
66
|
-
issues.push("SMTP_URL: required when email provider is 'smtp'");
|
|
67
|
-
}
|
|
68
61
|
const userVars = {};
|
|
69
62
|
validateSection(envConfig?.server, 'server', source, issues, userVars);
|
|
70
63
|
validateSection(envConfig?.client, 'client', source, issues, userVars);
|