bunderstack 0.17.0-beta.2 → 0.17.0-beta.3
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 +227 -0
- package/README.md +11 -1
- package/dist/access.d.ts +116 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +325 -0
- package/dist/access.js.map +1 -0
- package/dist/api/builder.d.ts +145 -0
- package/dist/api/builder.d.ts.map +1 -0
- package/dist/api/builder.js +28 -0
- package/dist/api/builder.js.map +1 -0
- package/dist/api/context.d.ts +34 -0
- package/dist/api/context.d.ts.map +1 -0
- package/dist/api/context.js +32 -0
- package/dist/api/context.js.map +1 -0
- package/dist/api/crud-router.d.ts +290 -0
- package/dist/api/crud-router.d.ts.map +1 -0
- package/dist/api/crud-router.js +249 -0
- package/dist/api/crud-router.js.map +1 -0
- package/dist/api/openapi.d.ts +6 -0
- package/dist/api/openapi.d.ts.map +1 -0
- package/dist/api/openapi.js +146 -0
- package/dist/api/openapi.js.map +1 -0
- package/dist/api/realtime-router.d.ts +76 -0
- package/dist/api/realtime-router.d.ts.map +1 -0
- package/dist/api/realtime-router.js +41 -0
- package/dist/api/realtime-router.js.map +1 -0
- package/dist/api/registry.d.ts +30 -0
- package/dist/api/registry.d.ts.map +1 -0
- package/dist/api/registry.js +239 -0
- package/dist/api/registry.js.map +1 -0
- package/dist/api/router.d.ts +9 -0
- package/dist/api/router.d.ts.map +1 -0
- package/dist/api/router.js +19 -0
- package/dist/api/router.js.map +1 -0
- package/dist/api/storage-router.d.ts +279 -0
- package/dist/api/storage-router.d.ts.map +1 -0
- package/dist/api/storage-router.js +155 -0
- package/dist/api/storage-router.js.map +1 -0
- package/dist/api/types.d.ts +55 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/auth.d.ts +31 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +102 -0
- package/dist/auth.js.map +1 -0
- package/dist/blueprint-generator.d.ts +18 -0
- package/dist/blueprint-generator.d.ts.map +1 -0
- package/dist/blueprint-generator.js +125 -0
- package/dist/blueprint-generator.js.map +1 -0
- package/dist/blueprint.d.ts +46 -0
- package/dist/blueprint.d.ts.map +1 -0
- package/dist/blueprint.js +162 -0
- package/dist/blueprint.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +68 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/cron.d.ts +4 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +3 -0
- package/dist/cron.js.map +1 -0
- package/dist/crud-operations.d.ts +43 -0
- package/dist/crud-operations.d.ts.map +1 -0
- package/dist/crud-operations.js +238 -0
- package/dist/crud-operations.js.map +1 -0
- package/dist/database/adapter.d.ts +20 -0
- package/dist/database/adapter.d.ts.map +1 -0
- package/dist/database/adapter.js +2 -0
- package/dist/database/adapter.js.map +1 -0
- package/dist/database/bun-sql.d.ts +3 -0
- package/dist/database/bun-sql.d.ts.map +1 -0
- package/dist/database/bun-sql.js +21 -0
- package/dist/database/bun-sql.js.map +1 -0
- package/dist/database/libsql.d.ts +3 -0
- package/dist/database/libsql.d.ts.map +1 -0
- package/dist/database/libsql.js +22 -0
- package/dist/database/libsql.js.map +1 -0
- package/dist/database/pglite.d.ts +3 -0
- package/dist/database/pglite.d.ts.map +1 -0
- package/dist/database/pglite.js +27 -0
- package/dist/database/pglite.js.map +1 -0
- package/dist/database/postgres-js.d.ts +3 -0
- package/dist/database/postgres-js.d.ts.map +1 -0
- package/dist/database/postgres-js.js +21 -0
- package/dist/database/postgres-js.js.map +1 -0
- package/dist/db.d.ts +18 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +26 -0
- package/dist/db.js.map +1 -0
- package/dist/dialect.d.ts +17 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +22 -0
- package/dist/dialect.js.map +1 -0
- package/dist/email/smtp.d.ts +14 -0
- package/dist/email/smtp.d.ts.map +1 -0
- package/dist/email/smtp.js +27 -0
- package/dist/email/smtp.js.map +1 -0
- package/dist/email.d.ts +39 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +97 -0
- package/dist/email.js.map +1 -0
- package/dist/env.d.ts +47 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +99 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +125 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +92 -0
- package/dist/errors.js.map +1 -0
- package/dist/handler.d.ts +9 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +22 -0
- package/dist/handler.js.map +1 -0
- package/dist/idempotency.d.ts +17 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +65 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/index.d.ts +134 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +477 -0
- package/dist/index.js.map +1 -0
- package/dist/internal-tables-pg.d.ts +481 -0
- package/dist/internal-tables-pg.d.ts.map +1 -0
- package/{src/internal-tables-pg.ts → dist/internal-tables-pg.js} +10 -34
- package/dist/internal-tables-pg.js.map +1 -0
- package/dist/internal-tables.d.ts +2509 -0
- package/dist/internal-tables.d.ts.map +1 -0
- package/dist/internal-tables.js +108 -0
- package/dist/internal-tables.js.map +1 -0
- package/dist/jobs/cron.d.ts +15 -0
- package/dist/jobs/cron.d.ts.map +1 -0
- package/dist/jobs/cron.js +74 -0
- package/dist/jobs/cron.js.map +1 -0
- package/dist/jobs/define.d.ts +133 -0
- package/dist/jobs/define.d.ts.map +1 -0
- package/dist/jobs/define.js +74 -0
- package/dist/jobs/define.js.map +1 -0
- package/dist/jobs/index.d.ts +11 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +8 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/queue.d.ts +6 -0
- package/dist/jobs/queue.d.ts.map +1 -0
- package/dist/jobs/queue.js +55 -0
- package/dist/jobs/queue.js.map +1 -0
- package/dist/jobs/runtime.d.ts +13 -0
- package/dist/jobs/runtime.d.ts.map +1 -0
- package/dist/jobs/runtime.js +46 -0
- package/dist/jobs/runtime.js.map +1 -0
- package/dist/jobs/slots.d.ts +25 -0
- package/dist/jobs/slots.d.ts.map +1 -0
- package/dist/jobs/slots.js +41 -0
- package/dist/jobs/slots.js.map +1 -0
- package/dist/jobs/worker.d.ts +12 -0
- package/dist/jobs/worker.d.ts.map +1 -0
- package/dist/jobs/worker.js +311 -0
- package/dist/jobs/worker.js.map +1 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +37 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/list-query.d.ts +62 -0
- package/dist/list-query.d.ts.map +1 -0
- package/dist/list-query.js +194 -0
- package/dist/list-query.js.map +1 -0
- package/dist/manifest.d.ts +59 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +167 -0
- package/dist/manifest.js.map +1 -0
- package/dist/provision-internals.d.ts +24 -0
- package/dist/provision-internals.d.ts.map +1 -0
- package/dist/provision-internals.js +7 -0
- package/dist/provision-internals.js.map +1 -0
- package/dist/provision.d.ts +23 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +96 -0
- package/dist/provision.js.map +1 -0
- package/dist/rate-limit.d.ts +7 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +49 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/realtime/facade.d.ts +18 -0
- package/dist/realtime/facade.d.ts.map +1 -0
- package/dist/realtime/facade.js +36 -0
- package/dist/realtime/facade.js.map +1 -0
- package/dist/realtime/filter.d.ts +13 -0
- package/dist/realtime/filter.d.ts.map +1 -0
- package/dist/realtime/filter.js +48 -0
- package/dist/realtime/filter.js.map +1 -0
- package/dist/realtime/heartbeat.d.ts +14 -0
- package/dist/realtime/heartbeat.d.ts.map +1 -0
- package/dist/realtime/heartbeat.js +64 -0
- package/dist/realtime/heartbeat.js.map +1 -0
- package/dist/realtime/publisher.d.ts +21 -0
- package/dist/realtime/publisher.d.ts.map +1 -0
- package/dist/realtime/publisher.js +17 -0
- package/dist/realtime/publisher.js.map +1 -0
- package/dist/schema-export-pg.d.ts +2 -0
- package/dist/schema-export-pg.d.ts.map +1 -0
- package/dist/schema-export-pg.js +4 -0
- package/dist/schema-export-pg.js.map +1 -0
- package/dist/schema-export.d.ts +2 -0
- package/dist/schema-export.d.ts.map +1 -0
- package/dist/schema-export.js +2 -0
- package/dist/schema-export.js.map +1 -0
- package/dist/scope.d.ts +4 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +13 -0
- package/dist/scope.js.map +1 -0
- package/dist/standard-schema.d.ts +13 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +34 -0
- package/dist/standard-schema.js.map +1 -0
- package/dist/storage/buckets.d.ts +80 -0
- package/dist/storage/buckets.d.ts.map +1 -0
- package/dist/storage/buckets.js +178 -0
- package/dist/storage/buckets.js.map +1 -0
- package/dist/storage/delete.d.ts +11 -0
- package/dist/storage/delete.d.ts.map +1 -0
- package/dist/storage/delete.js +18 -0
- package/dist/storage/delete.js.map +1 -0
- package/dist/storage/file-meta.d.ts +37 -0
- package/dist/storage/file-meta.d.ts.map +1 -0
- package/dist/storage/file-meta.js +166 -0
- package/dist/storage/file-meta.js.map +1 -0
- package/dist/storage/index.d.ts +28 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +15 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +56 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/operations.d.ts +54 -0
- package/dist/storage/operations.d.ts.map +1 -0
- package/dist/storage/operations.js +250 -0
- package/dist/storage/operations.js.map +1 -0
- package/dist/storage/registry.d.ts +10 -0
- package/dist/storage/registry.d.ts.map +1 -0
- package/dist/storage/registry.js +23 -0
- package/dist/storage/registry.js.map +1 -0
- package/dist/storage/s3.d.ts +28 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +71 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/sweep.d.ts +9 -0
- package/dist/storage/sweep.d.ts.map +1 -0
- package/dist/storage/sweep.js +17 -0
- package/dist/storage/sweep.js.map +1 -0
- package/dist/storage/thumbnails.d.ts +11 -0
- package/dist/storage/thumbnails.d.ts.map +1 -0
- package/dist/storage/thumbnails.js +60 -0
- package/dist/storage/thumbnails.js.map +1 -0
- package/dist/testing.d.ts +27 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +11 -0
- package/dist/testing.js.map +1 -0
- package/dist/typeid-pg.d.ts +19 -0
- package/dist/typeid-pg.d.ts.map +1 -0
- package/{src/typeid-pg.ts → dist/typeid-pg.js} +10 -13
- package/dist/typeid-pg.js.map +1 -0
- package/dist/typeid.d.ts +45 -0
- package/dist/typeid.d.ts.map +1 -0
- package/dist/typeid.js +100 -0
- package/dist/typeid.js.map +1 -0
- package/package.json +77 -28
- package/src/access.ts +0 -532
- package/src/api/builder.ts +0 -52
- package/src/api/context.ts +0 -83
- package/src/api/crud-router.ts +0 -386
- package/src/api/openapi.ts +0 -184
- package/src/api/realtime-router.ts +0 -75
- package/src/api/registry.ts +0 -338
- package/src/api/router.ts +0 -34
- package/src/api/storage-router.ts +0 -224
- package/src/api/types.ts +0 -105
- package/src/auth.ts +0 -117
- package/src/blueprint-generator.ts +0 -203
- package/src/blueprint.ts +0 -264
- package/src/cli.ts +0 -90
- package/src/config.ts +0 -215
- package/src/cron.ts +0 -3
- package/src/crud-operations.ts +0 -478
- package/src/database/adapter.ts +0 -26
- package/src/database/bun-sql.ts +0 -23
- package/src/database/libsql.ts +0 -28
- package/src/database/pglite.ts +0 -33
- package/src/database/postgres-js.ts +0 -33
- package/src/db.ts +0 -61
- package/src/dialect.ts +0 -38
- package/src/email/smtp.ts +0 -45
- package/src/email.ts +0 -154
- package/src/env.ts +0 -180
- package/src/errors.ts +0 -123
- package/src/handler.ts +0 -30
- package/src/idempotency.ts +0 -97
- package/src/index.ts +0 -834
- package/src/internal-tables.ts +0 -149
- package/src/jobs/cron.ts +0 -87
- package/src/jobs/define.ts +0 -283
- package/src/jobs/index.ts +0 -37
- package/src/jobs/queue.ts +0 -67
- package/src/jobs/runtime.ts +0 -58
- package/src/jobs/slots.ts +0 -52
- package/src/jobs/worker.ts +0 -369
- package/src/lifecycle.ts +0 -49
- package/src/list-query.ts +0 -333
- package/src/manifest.ts +0 -297
- package/src/provision-internals.ts +0 -29
- package/src/provision.ts +0 -128
- package/src/rate-limit.ts +0 -71
- package/src/realtime/facade.ts +0 -74
- package/src/realtime/filter.ts +0 -81
- package/src/realtime/heartbeat.ts +0 -80
- package/src/realtime/publisher.ts +0 -46
- package/src/schema-export-pg.ts +0 -7
- package/src/schema-export.ts +0 -5
- package/src/scope.ts +0 -17
- package/src/standard-schema.ts +0 -59
- package/src/storage/buckets.ts +0 -308
- package/src/storage/delete.ts +0 -26
- package/src/storage/file-meta.ts +0 -222
- package/src/storage/index.ts +0 -39
- package/src/storage/local.ts +0 -69
- package/src/storage/operations.ts +0 -398
- package/src/storage/registry.ts +0 -40
- package/src/storage/s3.ts +0 -99
- package/src/storage/sweep.ts +0 -25
- package/src/storage/thumbnails.ts +0 -65
- package/src/testing.ts +0 -31
- package/src/typeid.ts +0 -116
package/src/database/adapter.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { DbFor, Driver } from '../db'
|
|
2
|
-
import type { AnyDb, Dialect } from '../dialect'
|
|
3
|
-
|
|
4
|
-
export type DatabaseConnection = {
|
|
5
|
-
url: string
|
|
6
|
-
authToken?: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type DatabaseConnectOptions = { introspect: boolean }
|
|
10
|
-
|
|
11
|
-
export type DatabaseConnectionResult<TSchema extends Record<string, unknown>> =
|
|
12
|
-
{
|
|
13
|
-
db: DbFor<TSchema>
|
|
14
|
-
close?: () => void | Promise<void>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type DatabaseAdapter = {
|
|
18
|
-
readonly dialect: Dialect
|
|
19
|
-
readonly driver: Driver
|
|
20
|
-
connect<TSchema extends Record<string, unknown>>(
|
|
21
|
-
schema: TSchema,
|
|
22
|
-
connection: DatabaseConnection,
|
|
23
|
-
options: DatabaseConnectOptions,
|
|
24
|
-
): Promise<DatabaseConnectionResult<TSchema>>
|
|
25
|
-
migrate(db: AnyDb, migrationsFolder: string): Promise<void>
|
|
26
|
-
}
|
package/src/database/bun-sql.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { drizzle } from 'drizzle-orm/bun-sql'
|
|
2
|
-
import { migrate } from 'drizzle-orm/bun-sql/migrator'
|
|
3
|
-
|
|
4
|
-
import type { DatabaseAdapter } from './adapter'
|
|
5
|
-
|
|
6
|
-
export function bunSql(): DatabaseAdapter {
|
|
7
|
-
return {
|
|
8
|
-
dialect: 'pg',
|
|
9
|
-
driver: 'bun-sql',
|
|
10
|
-
async connect(schema, { url }, { introspect }) {
|
|
11
|
-
if (introspect) return { db: drizzle.mock({ schema }) as never }
|
|
12
|
-
|
|
13
|
-
if (!url.startsWith('postgres://') && !url.startsWith('postgresql://')) {
|
|
14
|
-
throw new Error('[bunderstack] bunSql adapter requires a Postgres URL')
|
|
15
|
-
}
|
|
16
|
-
const db = drizzle(url, { schema })
|
|
17
|
-
return { db: db as never, close: () => db.$client.close() }
|
|
18
|
-
},
|
|
19
|
-
async migrate(db, migrationsFolder) {
|
|
20
|
-
await migrate(db as never, { migrationsFolder })
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
}
|
package/src/database/libsql.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { drizzle } from 'drizzle-orm/libsql'
|
|
2
|
-
import { migrate } from 'drizzle-orm/libsql/migrator'
|
|
3
|
-
|
|
4
|
-
import type { DatabaseAdapter } from './adapter'
|
|
5
|
-
|
|
6
|
-
export function libsql(): DatabaseAdapter {
|
|
7
|
-
return {
|
|
8
|
-
dialect: 'sqlite',
|
|
9
|
-
driver: 'libsql',
|
|
10
|
-
async connect(schema, connection, { introspect }) {
|
|
11
|
-
if (introspect) return { db: drizzle.mock({ schema }) as never }
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
connection.url.startsWith('postgres://') ||
|
|
15
|
-
connection.url.startsWith('postgresql://')
|
|
16
|
-
) {
|
|
17
|
-
throw new Error(
|
|
18
|
-
'[bunderstack] libsql adapter cannot connect to a Postgres URL',
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
const db = drizzle({ connection, schema })
|
|
22
|
-
return { db: db as never, close: () => db.$client.close() }
|
|
23
|
-
},
|
|
24
|
-
async migrate(db, migrationsFolder) {
|
|
25
|
-
await migrate(db as never, { migrationsFolder })
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
}
|
package/src/database/pglite.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { drizzle } from 'drizzle-orm/pglite'
|
|
2
|
-
import { migrate } from 'drizzle-orm/pglite/migrator'
|
|
3
|
-
import { mkdir } from 'node:fs/promises'
|
|
4
|
-
|
|
5
|
-
import type { DatabaseAdapter } from './adapter'
|
|
6
|
-
|
|
7
|
-
const rawPath = (url: string) =>
|
|
8
|
-
url.startsWith('file:') ? url.slice('file:'.length) : url
|
|
9
|
-
|
|
10
|
-
export function pglite(): DatabaseAdapter {
|
|
11
|
-
return {
|
|
12
|
-
dialect: 'pg',
|
|
13
|
-
driver: 'pglite',
|
|
14
|
-
async connect(schema, { url }, { introspect }) {
|
|
15
|
-
if (introspect) return { db: drizzle.mock({ schema }) as never }
|
|
16
|
-
|
|
17
|
-
if (url.startsWith('postgres://') || url.startsWith('postgresql://')) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
'[bunderstack] pglite adapter cannot connect to a Postgres URL',
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
const raw = rawPath(url)
|
|
23
|
-
const dataDir = raw === ':memory:' ? 'memory://' : raw
|
|
24
|
-
if (!dataDir.startsWith('memory://'))
|
|
25
|
-
await mkdir(dataDir, { recursive: true })
|
|
26
|
-
const db = drizzle(dataDir, { schema })
|
|
27
|
-
return { db: db as never, close: () => db.$client.close() }
|
|
28
|
-
},
|
|
29
|
-
async migrate(db, migrationsFolder) {
|
|
30
|
-
await migrate(db as never, { migrationsFolder })
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { drizzle } from 'drizzle-orm/postgres-js'
|
|
2
|
-
import { migrate } from 'drizzle-orm/postgres-js/migrator'
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
DatabaseAdapter,
|
|
6
|
-
DatabaseConnection,
|
|
7
|
-
DatabaseConnectOptions,
|
|
8
|
-
} from './adapter'
|
|
9
|
-
|
|
10
|
-
export function postgresJs(): DatabaseAdapter {
|
|
11
|
-
return {
|
|
12
|
-
dialect: 'pg',
|
|
13
|
-
driver: 'postgres-js',
|
|
14
|
-
async connect<TSchema extends Record<string, unknown>>(
|
|
15
|
-
schema: TSchema,
|
|
16
|
-
{ url }: DatabaseConnection,
|
|
17
|
-
{ introspect }: DatabaseConnectOptions,
|
|
18
|
-
) {
|
|
19
|
-
if (introspect) return { db: drizzle.mock({ schema }) as never }
|
|
20
|
-
|
|
21
|
-
if (!url.startsWith('postgres://') && !url.startsWith('postgresql://')) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
'[bunderstack] postgresJs adapter requires a Postgres URL',
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
const db = drizzle<TSchema>({ connection: url, schema })
|
|
27
|
-
return { db: db as never, close: () => db.$client.end() }
|
|
28
|
-
},
|
|
29
|
-
async migrate(db, migrationsFolder) {
|
|
30
|
-
await migrate(db as never, { migrationsFolder })
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
}
|
package/src/db.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
|
2
|
-
import type { PgDatabase, PgQueryResultHKT, PgTable } from 'drizzle-orm/pg-core'
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
DatabaseAdapter,
|
|
6
|
-
DatabaseConnection,
|
|
7
|
-
DatabaseConnectionResult,
|
|
8
|
-
} from './database/adapter'
|
|
9
|
-
import type { Dialect } from './dialect'
|
|
10
|
-
|
|
11
|
-
export type Driver = 'libsql' | 'pglite' | 'bun-sql' | 'postgres-js'
|
|
12
|
-
|
|
13
|
-
/** Per-dialect public db type, computed from the schema's table brands. */
|
|
14
|
-
export type DbFor<TSchema extends Record<string, unknown>> = [
|
|
15
|
-
Extract<TSchema[keyof TSchema], PgTable>,
|
|
16
|
-
] extends [never]
|
|
17
|
-
? LibSQLDatabase<TSchema>
|
|
18
|
-
: PgDatabase<PgQueryResultHKT, TSchema>
|
|
19
|
-
|
|
20
|
-
const PG_SERVER_RE = /^postgres(ql)?:\/\//
|
|
21
|
-
const LIBSQL_REMOTE_RE = /^(libsql|wss?|https?):\/\//
|
|
22
|
-
|
|
23
|
-
export function validateDatabaseUrl(url: string, dialect: Dialect) {
|
|
24
|
-
if (dialect === 'sqlite') {
|
|
25
|
-
if (PG_SERVER_RE.test(url)) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
'[bunderstack] DATABASE_URL is a Postgres URL but the schema uses sqliteTable. ' +
|
|
28
|
-
'Define the schema with drizzle-orm/pg-core, or point DATABASE_URL at a SQLite database.',
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
} else if (dialect === 'pg') {
|
|
32
|
-
if (LIBSQL_REMOTE_RE.test(url)) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
'[bunderstack] DATABASE_URL looks like a libsql/Turso URL but the schema uses pgTable. ' +
|
|
35
|
-
'Set DATABASE_URL=postgres://… (or leave it unset for local PGlite).',
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export async function createDb<TSchema extends Record<string, unknown>>(
|
|
42
|
-
schema: TSchema,
|
|
43
|
-
cfg: DatabaseConnection & {
|
|
44
|
-
adapter: DatabaseAdapter
|
|
45
|
-
dialect: Dialect
|
|
46
|
-
introspect?: boolean
|
|
47
|
-
},
|
|
48
|
-
): Promise<DatabaseConnectionResult<TSchema> & { driver: Driver }> {
|
|
49
|
-
if (cfg.adapter.dialect !== cfg.dialect) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`[bunderstack] database adapter dialect ${cfg.adapter.dialect} does not match ${cfg.dialect} schema`,
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
if (!cfg.introspect) validateDatabaseUrl(cfg.url, cfg.dialect)
|
|
55
|
-
const result = await cfg.adapter.connect(
|
|
56
|
-
schema,
|
|
57
|
-
{ url: cfg.url, authToken: cfg.authToken },
|
|
58
|
-
{ introspect: cfg.introspect ?? false },
|
|
59
|
-
)
|
|
60
|
-
return { ...result, driver: cfg.adapter.driver }
|
|
61
|
-
}
|
package/src/dialect.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// src/dialect.ts — schema-driven dialect detection. Imports only dialect-core
|
|
2
|
-
// drizzle entrypoints (no drivers), safe in every module graph.
|
|
3
|
-
import { is } from 'drizzle-orm'
|
|
4
|
-
import { PgTable } from 'drizzle-orm/pg-core'
|
|
5
|
-
import { SQLiteTable } from 'drizzle-orm/sqlite-core'
|
|
6
|
-
|
|
7
|
-
export type Dialect = 'sqlite' | 'pg'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Minimal structural view of a drizzle db shared by both dialects. Internal
|
|
11
|
-
* modules run dynamic tables (Record<string, unknown> schemas) where drizzle's
|
|
12
|
-
* generics add no safety, so they accept this instead of a per-dialect union.
|
|
13
|
-
* The public surface (`app.db`, API context) keeps full per-dialect typing via
|
|
14
|
-
* `DbFor` in db.ts.
|
|
15
|
-
*/
|
|
16
|
-
export type AnyDb = {
|
|
17
|
-
select: (...args: any[]) => any
|
|
18
|
-
insert: (...args: any[]) => any
|
|
19
|
-
update: (...args: any[]) => any
|
|
20
|
-
delete: (...args: any[]) => any
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Classify a schema by its table brands. Mixed dialects are a config error. */
|
|
24
|
-
export function detectDialect(schema: Record<string, unknown>): Dialect {
|
|
25
|
-
let pgKey: string | undefined
|
|
26
|
-
let sqliteKey: string | undefined
|
|
27
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
28
|
-
if (is(value, PgTable)) pgKey ??= key
|
|
29
|
-
else if (is(value, SQLiteTable)) sqliteKey ??= key
|
|
30
|
-
}
|
|
31
|
-
if (pgKey !== undefined && sqliteKey !== undefined) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
`[bunderstack] schema mixes dialects: "${pgKey}" is a Postgres table while "${sqliteKey}" is a SQLite table. ` +
|
|
34
|
-
'Define every table with the same dialect (drizzle-orm/pg-core or drizzle-orm/sqlite-core).',
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
return pgKey !== undefined ? 'pg' : 'sqlite'
|
|
38
|
-
}
|
package/src/email/smtp.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import nodemailer from 'nodemailer'
|
|
2
|
-
|
|
3
|
-
import type { EmailAdapter } from '../email'
|
|
4
|
-
|
|
5
|
-
type SmtpTransport = {
|
|
6
|
-
sendMail(message: Record<string, unknown>): Promise<{ messageId?: string }>
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function createSmtpAdapter(
|
|
10
|
-
options: { url: string },
|
|
11
|
-
createTransport: (url: string) => SmtpTransport = (url) =>
|
|
12
|
-
nodemailer.createTransport(url) as SmtpTransport,
|
|
13
|
-
): EmailAdapter {
|
|
14
|
-
const toArray = (v: string | string[] | undefined) =>
|
|
15
|
-
v === undefined ? undefined : Array.isArray(v) ? v : [v]
|
|
16
|
-
|
|
17
|
-
let transportPromise: Promise<{
|
|
18
|
-
sendMail(opts: Record<string, unknown>): Promise<{ messageId?: string }>
|
|
19
|
-
}> | null = null
|
|
20
|
-
|
|
21
|
-
const getTransport = () => {
|
|
22
|
-
transportPromise ??= Promise.resolve(createTransport(options.url))
|
|
23
|
-
return transportPromise
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
async send(msg) {
|
|
28
|
-
const transport = await getTransport()
|
|
29
|
-
const info = await transport.sendMail({
|
|
30
|
-
from: msg.from,
|
|
31
|
-
to: toArray(msg.to)!.join(', '),
|
|
32
|
-
subject: msg.subject,
|
|
33
|
-
html: msg.html,
|
|
34
|
-
text: msg.text,
|
|
35
|
-
replyTo: msg.replyTo,
|
|
36
|
-
cc: toArray(msg.cc)?.join(', '),
|
|
37
|
-
bcc: toArray(msg.bcc)?.join(', '),
|
|
38
|
-
})
|
|
39
|
-
return { id: info.messageId }
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const smtp = (options: { url: string }): EmailAdapter =>
|
|
45
|
-
createSmtpAdapter(options)
|
package/src/email.ts
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
// src/email.ts — email sending: console / resend / custom adapters.
|
|
2
|
-
// SMTP is provided by the static `bunderstack/email/smtp` factory subpath.
|
|
3
|
-
|
|
4
|
-
export type EmailMessage = {
|
|
5
|
-
to: string | string[]
|
|
6
|
-
subject: string
|
|
7
|
-
html?: string
|
|
8
|
-
text?: string
|
|
9
|
-
from?: string
|
|
10
|
-
replyTo?: string
|
|
11
|
-
cc?: string | string[]
|
|
12
|
-
bcc?: string | string[]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type SentEmail = { id?: string }
|
|
16
|
-
|
|
17
|
-
/** Adapters receive the message with `from` already resolved. */
|
|
18
|
-
export type EmailAdapter = {
|
|
19
|
-
send(msg: EmailMessage & { from: string }): Promise<SentEmail>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export type EmailConfigInput = {
|
|
23
|
-
from: string
|
|
24
|
-
provider?: 'resend' | 'console' | EmailAdapter | EmailAdapter['send']
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export type EmailFacade = {
|
|
28
|
-
send(msg: EmailMessage): Promise<SentEmail>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type CreateEmailOptions = {
|
|
32
|
-
env: { RESEND_API_KEY?: string; SMTP_URL?: string; NODE_ENV?: string }
|
|
33
|
-
/** Test seam for the resend adapter. */
|
|
34
|
-
fetchFn?: typeof fetch
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Root string provider tag ('resend' | 'console') or undefined. */
|
|
38
|
-
export function emailProviderTag(
|
|
39
|
-
config: EmailConfigInput | undefined,
|
|
40
|
-
): string | undefined {
|
|
41
|
-
return typeof config?.provider === 'string' ? config.provider : undefined
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const toArray = (v: string | string[] | undefined) =>
|
|
45
|
-
v === undefined ? undefined : Array.isArray(v) ? v : [v]
|
|
46
|
-
|
|
47
|
-
function createConsoleAdapter(): EmailAdapter {
|
|
48
|
-
return {
|
|
49
|
-
async send(msg) {
|
|
50
|
-
const line = '─'.repeat(60)
|
|
51
|
-
console.log(
|
|
52
|
-
[
|
|
53
|
-
line,
|
|
54
|
-
'📧 email (console provider — not sent)',
|
|
55
|
-
`from: ${msg.from}`,
|
|
56
|
-
`to: ${toArray(msg.to)!.join(', ')}`,
|
|
57
|
-
`subject: ${msg.subject}`,
|
|
58
|
-
line,
|
|
59
|
-
msg.text ?? msg.html ?? '',
|
|
60
|
-
line,
|
|
61
|
-
].join('\n'),
|
|
62
|
-
)
|
|
63
|
-
return {}
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function createResendAdapter(
|
|
69
|
-
apiKey: string,
|
|
70
|
-
fetchFn: typeof fetch,
|
|
71
|
-
): EmailAdapter {
|
|
72
|
-
return {
|
|
73
|
-
async send(msg) {
|
|
74
|
-
const res = await fetchFn('https://api.resend.com/emails', {
|
|
75
|
-
method: 'POST',
|
|
76
|
-
headers: {
|
|
77
|
-
Authorization: `Bearer ${apiKey}`,
|
|
78
|
-
'Content-Type': 'application/json',
|
|
79
|
-
},
|
|
80
|
-
body: JSON.stringify({
|
|
81
|
-
from: msg.from,
|
|
82
|
-
to: toArray(msg.to),
|
|
83
|
-
subject: msg.subject,
|
|
84
|
-
html: msg.html,
|
|
85
|
-
text: msg.text,
|
|
86
|
-
reply_to: msg.replyTo,
|
|
87
|
-
cc: toArray(msg.cc),
|
|
88
|
-
bcc: toArray(msg.bcc),
|
|
89
|
-
}),
|
|
90
|
-
})
|
|
91
|
-
if (!res.ok) {
|
|
92
|
-
throw new Error(`resend API error (${res.status}): ${await res.text()}`)
|
|
93
|
-
}
|
|
94
|
-
const data = (await res.json()) as { id?: string }
|
|
95
|
-
return { id: data.id }
|
|
96
|
-
},
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function resolveAdapter(
|
|
101
|
-
config: EmailConfigInput,
|
|
102
|
-
opts: CreateEmailOptions,
|
|
103
|
-
): EmailAdapter {
|
|
104
|
-
const provider = config.provider
|
|
105
|
-
if (typeof provider === 'function') return { send: provider }
|
|
106
|
-
if (typeof provider === 'object') return provider
|
|
107
|
-
const fetchFn = opts.fetchFn ?? globalThis.fetch.bind(globalThis)
|
|
108
|
-
switch (provider) {
|
|
109
|
-
case 'resend':
|
|
110
|
-
return createResendAdapter(opts.env.RESEND_API_KEY ?? '', fetchFn)
|
|
111
|
-
|
|
112
|
-
case 'console':
|
|
113
|
-
return createConsoleAdapter()
|
|
114
|
-
case undefined:
|
|
115
|
-
if (opts.env.NODE_ENV === 'production') {
|
|
116
|
-
throw new Error(
|
|
117
|
-
'email is configured without a provider — set email.provider ' +
|
|
118
|
-
"('resend' | a custom adapter) for production",
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
return createConsoleAdapter()
|
|
122
|
-
default:
|
|
123
|
-
if (provider === 'smtp') {
|
|
124
|
-
throw new Error(
|
|
125
|
-
"email provider 'smtp' was removed. Import `smtp` from 'bunderstack/email/smtp' instead.",
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
return createConsoleAdapter()
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function createEmail(
|
|
133
|
-
config: EmailConfigInput | undefined,
|
|
134
|
-
opts: CreateEmailOptions,
|
|
135
|
-
): EmailFacade {
|
|
136
|
-
if (!config) {
|
|
137
|
-
return {
|
|
138
|
-
async send() {
|
|
139
|
-
throw new Error(
|
|
140
|
-
'email is not configured — add an email key to your bunderstack config',
|
|
141
|
-
)
|
|
142
|
-
},
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const adapter = resolveAdapter(config, opts)
|
|
146
|
-
return {
|
|
147
|
-
async send(msg) {
|
|
148
|
-
if (!msg.html && !msg.text) {
|
|
149
|
-
throw new Error('email message needs html or text content')
|
|
150
|
-
}
|
|
151
|
-
return adapter.send({ ...msg, from: msg.from ?? config.from })
|
|
152
|
-
},
|
|
153
|
-
}
|
|
154
|
-
}
|
package/src/env.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
// src/env.ts — env validation. Browser-safe: type-only Standard Schema import.
|
|
2
|
-
import type { StandardSchemaV1 } from '@standard-schema/spec'
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
StandardSchemaValidationError,
|
|
6
|
-
validateStandardSchema,
|
|
7
|
-
} from './standard-schema'
|
|
8
|
-
|
|
9
|
-
export const CLIENT_PREFIX = 'PUBLIC_' as const
|
|
10
|
-
|
|
11
|
-
export type EnvConfigInput = {
|
|
12
|
-
server?: Record<string, StandardSchemaV1>
|
|
13
|
-
client?: Record<string, StandardSchemaV1>
|
|
14
|
-
/** Explicit value source for client vars (e.g. Vite's import.meta.env). */
|
|
15
|
-
runtimeEnv?: Record<string, unknown>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type BunderstackRole = 'all' | 'web' | 'worker'
|
|
19
|
-
|
|
20
|
-
const ROLES: readonly BunderstackRole[] = ['all', 'web', 'worker']
|
|
21
|
-
|
|
22
|
-
/** Vars bunderstack itself consumes, always validated. */
|
|
23
|
-
export type BaseEnv = {
|
|
24
|
-
NODE_ENV?: string
|
|
25
|
-
DATABASE_URL: string
|
|
26
|
-
DATABASE_AUTH_TOKEN?: string
|
|
27
|
-
AUTH_SECRET: string
|
|
28
|
-
REDIS_URL?: string
|
|
29
|
-
RESEND_API_KEY?: string
|
|
30
|
-
SMTP_URL?: string
|
|
31
|
-
BUNDERSTACK_ROLE: BunderstackRole
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type InferVars<T> =
|
|
35
|
-
T extends Record<string, StandardSchemaV1>
|
|
36
|
-
? { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]> }
|
|
37
|
-
: unknown
|
|
38
|
-
|
|
39
|
-
// Non-distributive so `ValidatedEnv<undefined>` is BaseEnv, not `never`.
|
|
40
|
-
export type ValidatedEnv<TEnv extends EnvConfigInput | undefined> = [
|
|
41
|
-
TEnv,
|
|
42
|
-
] extends [EnvConfigInput]
|
|
43
|
-
? BaseEnv &
|
|
44
|
-
InferVars<NonNullable<TEnv>['server']> &
|
|
45
|
-
InferVars<NonNullable<TEnv>['client']>
|
|
46
|
-
: BaseEnv
|
|
47
|
-
|
|
48
|
-
export class BunderstackEnvError extends Error {
|
|
49
|
-
readonly issues: string[]
|
|
50
|
-
|
|
51
|
-
constructor(issues: string[]) {
|
|
52
|
-
super(`Invalid environment:\n - ${issues.join('\n - ')}`)
|
|
53
|
-
this.name = 'BunderstackEnvError'
|
|
54
|
-
this.issues = issues
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type ValidateEnvOptions = {
|
|
59
|
-
/** String tag of the configured email provider ('resend' | 'smtp'), if any. */
|
|
60
|
-
emailProvider?: string
|
|
61
|
-
/** Value source; defaults to process.env. Tests pass this explicitly. */
|
|
62
|
-
source?: Record<string, string | undefined>
|
|
63
|
-
/** Dialect-aware DATABASE_URL fallback; createBunderstack passes it. */
|
|
64
|
-
defaultDatabaseUrl?: string
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const DEV_AUTH_SECRET = 'dev-secret-change-in-prod'
|
|
68
|
-
|
|
69
|
-
function validateSection(
|
|
70
|
-
section: Record<string, StandardSchemaV1> | undefined,
|
|
71
|
-
kind: 'server' | 'client',
|
|
72
|
-
source: Record<string, unknown>,
|
|
73
|
-
issues: string[],
|
|
74
|
-
out: Record<string, unknown>,
|
|
75
|
-
) {
|
|
76
|
-
for (const [key, schema] of Object.entries(section ?? {})) {
|
|
77
|
-
const isPublic = key.startsWith(CLIENT_PREFIX)
|
|
78
|
-
if (kind === 'server' && isPublic) {
|
|
79
|
-
issues.push(
|
|
80
|
-
`${key}: server vars must not start with ${CLIENT_PREFIX} (move it to env.client)`,
|
|
81
|
-
)
|
|
82
|
-
continue
|
|
83
|
-
}
|
|
84
|
-
if (kind === 'client' && !isPublic) {
|
|
85
|
-
issues.push(
|
|
86
|
-
`${key}: client vars must start with ${CLIENT_PREFIX} (rename it or move it to env.server)`,
|
|
87
|
-
)
|
|
88
|
-
continue
|
|
89
|
-
}
|
|
90
|
-
try {
|
|
91
|
-
out[key] = validateStandardSchema(schema, source[key], 'env')
|
|
92
|
-
} catch (error) {
|
|
93
|
-
if (!(error instanceof StandardSchemaValidationError)) throw error
|
|
94
|
-
for (const issue of error.issues) {
|
|
95
|
-
const path = issue.path.map(String).join('.')
|
|
96
|
-
issues.push(`${key}${path ? `.${path}` : ''}: ${issue.message}`)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function validateEnv<TEnv extends EnvConfigInput | undefined>(
|
|
103
|
-
envConfig: TEnv,
|
|
104
|
-
options: ValidateEnvOptions = {},
|
|
105
|
-
): ValidatedEnv<TEnv> {
|
|
106
|
-
const source =
|
|
107
|
-
options.source ?? (process.env as Record<string, string | undefined>)
|
|
108
|
-
const issues: string[] = []
|
|
109
|
-
const isProduction = source.NODE_ENV === 'production'
|
|
110
|
-
|
|
111
|
-
const base: BaseEnv = {
|
|
112
|
-
NODE_ENV: source.NODE_ENV,
|
|
113
|
-
DATABASE_URL:
|
|
114
|
-
source.DATABASE_URL ?? options.defaultDatabaseUrl ?? 'file:./data.db',
|
|
115
|
-
DATABASE_AUTH_TOKEN: source.DATABASE_AUTH_TOKEN,
|
|
116
|
-
AUTH_SECRET: source.AUTH_SECRET ?? DEV_AUTH_SECRET,
|
|
117
|
-
REDIS_URL: source.REDIS_URL,
|
|
118
|
-
RESEND_API_KEY: source.RESEND_API_KEY,
|
|
119
|
-
SMTP_URL: source.SMTP_URL,
|
|
120
|
-
BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all') as BunderstackRole,
|
|
121
|
-
}
|
|
122
|
-
if (isProduction && !source.AUTH_SECRET) {
|
|
123
|
-
issues.push('AUTH_SECRET: required in production')
|
|
124
|
-
}
|
|
125
|
-
if (
|
|
126
|
-
source.BUNDERSTACK_ROLE !== undefined &&
|
|
127
|
-
!ROLES.includes(source.BUNDERSTACK_ROLE as BunderstackRole)
|
|
128
|
-
) {
|
|
129
|
-
issues.push(
|
|
130
|
-
`BUNDERSTACK_ROLE: must be one of ${ROLES.join(', ')} (got "${String(source.BUNDERSTACK_ROLE)}")`,
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
if (options.emailProvider === 'resend' && !source.RESEND_API_KEY) {
|
|
134
|
-
issues.push("RESEND_API_KEY: required when email provider is 'resend'")
|
|
135
|
-
}
|
|
136
|
-
if (options.emailProvider === 'smtp' && !source.SMTP_URL) {
|
|
137
|
-
issues.push("SMTP_URL: required when email provider is 'smtp'")
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const userVars: Record<string, unknown> = {}
|
|
141
|
-
validateSection(envConfig?.server, 'server', source, issues, userVars)
|
|
142
|
-
validateSection(envConfig?.client, 'client', source, issues, userVars)
|
|
143
|
-
|
|
144
|
-
// Introspection (Bunderhost builder) imports the app declaration to read
|
|
145
|
-
// its manifest; missing user env must not kill the boot there.
|
|
146
|
-
const lenient = source.BUNDERSTACK_INTROSPECT === '1'
|
|
147
|
-
if (issues.length > 0 && !lenient) throw new BunderstackEnvError(issues)
|
|
148
|
-
return { ...base, ...userVars } as ValidatedEnv<TEnv>
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Browser-side companion (t3-env style): validates ONLY the client section.
|
|
153
|
-
* Server keys exist on the returned object as traps that throw on access, so
|
|
154
|
-
* a leaked import fails loudly instead of silently reading undefined.
|
|
155
|
-
*/
|
|
156
|
-
export function createClientEnv<TEnv extends EnvConfigInput>(
|
|
157
|
-
envConfig: TEnv,
|
|
158
|
-
): InferVars<TEnv['client']> {
|
|
159
|
-
const source =
|
|
160
|
-
envConfig.runtimeEnv ??
|
|
161
|
-
(typeof process !== 'undefined'
|
|
162
|
-
? (process.env as Record<string, unknown>)
|
|
163
|
-
: {})
|
|
164
|
-
const issues: string[] = []
|
|
165
|
-
const values: Record<string, unknown> = {}
|
|
166
|
-
validateSection(envConfig.client, 'client', source, issues, values)
|
|
167
|
-
if (issues.length > 0) throw new BunderstackEnvError(issues)
|
|
168
|
-
|
|
169
|
-
const serverKeys = new Set(Object.keys(envConfig.server ?? {}))
|
|
170
|
-
return new Proxy(values, {
|
|
171
|
-
get(target, prop) {
|
|
172
|
-
if (typeof prop === 'string' && serverKeys.has(prop)) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`${prop} is server-only and not available in client env`,
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
return Reflect.get(target, prop)
|
|
178
|
-
},
|
|
179
|
-
}) as InferVars<TEnv['client']>
|
|
180
|
-
}
|