@beignet/cli 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +68 -67
  3. package/dist/choices.d.ts +32 -3
  4. package/dist/choices.d.ts.map +1 -1
  5. package/dist/choices.js +54 -3
  6. package/dist/choices.js.map +1 -1
  7. package/dist/create-prompts.d.ts +5 -4
  8. package/dist/create-prompts.d.ts.map +1 -1
  9. package/dist/create-prompts.js +22 -4
  10. package/dist/create-prompts.js.map +1 -1
  11. package/dist/create.d.ts +7 -1
  12. package/dist/create.d.ts.map +1 -1
  13. package/dist/create.js +14 -4
  14. package/dist/create.js.map +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +25 -6
  17. package/dist/index.js.map +1 -1
  18. package/dist/inspect.js +78 -12
  19. package/dist/inspect.js.map +1 -1
  20. package/dist/make.d.ts +21 -1
  21. package/dist/make.d.ts.map +1 -1
  22. package/dist/make.js +210 -37
  23. package/dist/make.js.map +1 -1
  24. package/dist/templates/base.d.ts.map +1 -1
  25. package/dist/templates/base.js +80 -10
  26. package/dist/templates/base.js.map +1 -1
  27. package/dist/templates/db/index.d.ts +21 -0
  28. package/dist/templates/db/index.d.ts.map +1 -0
  29. package/dist/templates/db/index.js +14 -0
  30. package/dist/templates/db/index.js.map +1 -0
  31. package/dist/templates/db/mysql.d.ts +4 -0
  32. package/dist/templates/db/mysql.d.ts.map +1 -0
  33. package/dist/templates/db/mysql.js +972 -0
  34. package/dist/templates/db/mysql.js.map +1 -0
  35. package/dist/templates/db/postgres.d.ts +4 -0
  36. package/dist/templates/db/postgres.d.ts.map +1 -0
  37. package/dist/templates/db/postgres.js +863 -0
  38. package/dist/templates/db/postgres.js.map +1 -0
  39. package/dist/templates/db/sqlite.d.ts +3 -0
  40. package/dist/templates/db/sqlite.d.ts.map +1 -0
  41. package/dist/templates/db/sqlite.js +878 -0
  42. package/dist/templates/db/sqlite.js.map +1 -0
  43. package/dist/templates/db.js +16 -16
  44. package/dist/templates/index.d.ts +5 -5
  45. package/dist/templates/index.d.ts.map +1 -1
  46. package/dist/templates/index.js +21 -20
  47. package/dist/templates/index.js.map +1 -1
  48. package/dist/templates/server.d.ts +3 -3
  49. package/dist/templates/server.d.ts.map +1 -1
  50. package/dist/templates/server.js +149 -97
  51. package/dist/templates/server.js.map +1 -1
  52. package/dist/templates/shared.d.ts +34 -1
  53. package/dist/templates/shared.d.ts.map +1 -1
  54. package/dist/templates/shared.js +45 -0
  55. package/dist/templates/shared.js.map +1 -1
  56. package/package.json +2 -2
  57. package/src/choices.ts +70 -3
  58. package/src/create-prompts.ts +25 -3
  59. package/src/create.ts +25 -3
  60. package/src/index.ts +29 -5
  61. package/src/inspect.ts +137 -19
  62. package/src/make.ts +265 -34
  63. package/src/templates/base.ts +96 -10
  64. package/src/templates/db/index.ts +34 -0
  65. package/src/templates/db/mysql.ts +976 -0
  66. package/src/templates/db/postgres.ts +867 -0
  67. package/src/templates/{db.ts → db/sqlite.ts} +47 -168
  68. package/src/templates/index.ts +24 -19
  69. package/src/templates/server.ts +161 -97
  70. package/src/templates/shared.ts +90 -1
@@ -1,11 +1,17 @@
1
- import { hasIntegration, type TemplateContext } from "./shared.js";
1
+ import { databaseLocalUrl } from "../choices.js";
2
+ import {
3
+ databaseDescriptor,
4
+ hasIntegration,
5
+ type TemplateContext,
6
+ } from "./shared.js";
2
7
 
3
8
  export function serverProviders(ctx: TemplateContext): string {
9
+ const db = databaseDescriptor(ctx);
4
10
  const imports = [
5
11
  'import type { beignetServerOnly } from "@beignet/core/server-only";',
6
12
  'import { createDevtoolsProvider } from "@beignet/devtools";',
7
13
  'import { createAuthBetterAuthProvider } from "@beignet/provider-auth-better-auth";',
8
- 'import { createDrizzleTursoProvider } from "@beignet/provider-drizzle-turso";',
14
+ `import { ${db.providerFactory} } from "@beignet/provider-db-drizzle/${db.subpath}";`,
9
15
  hasIntegration(ctx, "inngest")
10
16
  ? 'import { inngestProvider } from "@beignet/provider-inngest";'
11
17
  : undefined,
@@ -26,7 +32,7 @@ export function serverProviders(ctx: TemplateContext): string {
26
32
  "\tcreateDevtoolsProvider(),",
27
33
  "\tcreateAuthBetterAuthProvider<AuthUser, AuthSessionMetadata>(auth),",
28
34
  "\tloggerPinoProvider,",
29
- "\tdrizzleTursoProvider,",
35
+ `\t${db.providerInstance},`,
30
36
  "\tstarterDatabaseProvider,",
31
37
  hasIntegration(ctx, "inngest") ? "\tinngestProvider," : undefined,
32
38
  hasIntegration(ctx, "resend") ? "\tmailResendProvider," : undefined,
@@ -37,7 +43,7 @@ export function serverProviders(ctx: TemplateContext): string {
37
43
 
38
44
  return `${imports.join("\n")}
39
45
 
40
- const drizzleTursoProvider = createDrizzleTursoProvider({ schema });
46
+ const ${db.providerInstance} = ${db.providerFactory}({ schema });
41
47
 
42
48
  export const providers = [
43
49
  ${entries.join("\n")}
@@ -132,8 +138,16 @@ ${deferred.join("\n")}
132
138
  `;
133
139
  }
134
140
 
135
- const files = {
136
- env: `import { createEnv } from "@beignet/core/config";
141
+ export function env(ctx: TemplateContext): string {
142
+ const databaseEnvLines =
143
+ ctx.database === "sqlite"
144
+ ? `\t\tSQLITE_DB_URL: z.string().default("file:local.db"),
145
+ \t\tSQLITE_DB_AUTH_TOKEN: z.string().optional(),`
146
+ : `\t\t${databaseDescriptor(ctx).urlEnvVar}: z
147
+ \t\t\t.string()
148
+ \t\t\t.default("${databaseLocalUrl(ctx.database, ctx.name)}"),`;
149
+
150
+ return `import { createEnv } from "@beignet/core/config";
137
151
  import { z } from "zod";
138
152
 
139
153
  const BooleanEnv = z.enum(["true", "false"]).transform((value) => value === "true");
@@ -151,8 +165,7 @@ export const env = createEnv({
151
165
  .default("local-dev-better-auth-secret-change-me"),
152
166
  BETTER_AUTH_URL: z.string().url().default("http://localhost:3000"),
153
167
  BETTER_AUTH_TRUSTED_ORIGINS: z.string().optional(),
154
- TURSO_DB_URL: z.string().default("file:local.db"),
155
- TURSO_DB_AUTH_TOKEN: z.string().optional(),
168
+ ${databaseEnvLines}
156
169
  LOG_LEVEL: z
157
170
  .enum(["trace", "debug", "info", "warn", "error", "fatal"])
158
171
  .default("info"),
@@ -166,7 +179,146 @@ export const isProduction = env.NODE_ENV === "production";
166
179
 
167
180
  export const isDevtoolsEnabled =
168
181
  env.DEVTOOLS_ENABLED ?? process.env.NODE_ENV !== "production";
169
- `,
182
+ `;
183
+ }
184
+
185
+ export function betterAuth(ctx: TemplateContext): string {
186
+ const connectionByDatabase = {
187
+ sqlite: `import { createClient } from "@libsql/client";
188
+ import { betterAuth } from "better-auth";
189
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
190
+ import { drizzle } from "drizzle-orm/libsql";
191
+ import { ensureDatabaseReady } from "@/infra/db/database-ready";
192
+ import * as schema from "@/infra/db/schema";
193
+ import { env } from "@/lib/env";
194
+
195
+ const client = createClient({
196
+ url: env.SQLITE_DB_URL,
197
+ authToken: env.SQLITE_DB_AUTH_TOKEN,
198
+ });
199
+
200
+ // Auth routes can be the first thing to touch the database, before any
201
+ // Beignet provider boots, so readiness is enforced here too.
202
+ await ensureDatabaseReady(client);
203
+
204
+ const db = drizzle(client, { schema });`,
205
+ postgres: `import { betterAuth } from "better-auth";
206
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
207
+ import { drizzle } from "drizzle-orm/node-postgres";
208
+ import pg from "pg";
209
+ import { ensureDatabaseReady } from "@/infra/db/database-ready";
210
+ import * as schema from "@/infra/db/schema";
211
+ import { env } from "@/lib/env";
212
+
213
+ const pool = new pg.Pool({ connectionString: env.POSTGRES_DB_URL });
214
+
215
+ // Auth routes can be the first thing to touch the database, before any
216
+ // Beignet provider boots, so readiness is enforced here too.
217
+ await ensureDatabaseReady(pool);
218
+
219
+ const db = drizzle(pool, { schema });`,
220
+ mysql: `import { betterAuth } from "better-auth";
221
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
222
+ import { drizzle } from "drizzle-orm/mysql2";
223
+ import mysql from "mysql2/promise";
224
+ import { ensureDatabaseReady } from "@/infra/db/database-ready";
225
+ import * as schema from "@/infra/db/schema";
226
+ import { env } from "@/lib/env";
227
+
228
+ const pool = mysql.createPool({ uri: env.MYSQL_DB_URL });
229
+
230
+ // Auth routes can be the first thing to touch the database, before any
231
+ // Beignet provider boots, so readiness is enforced here too.
232
+ await ensureDatabaseReady(pool);
233
+
234
+ const db = drizzle(pool, { schema, mode: "default" });`,
235
+ } as const;
236
+
237
+ return `${connectionByDatabase[ctx.database]}
238
+
239
+ const trustedOrigins = [
240
+ env.APP_URL,
241
+ env.BETTER_AUTH_URL,
242
+ ...(env.BETTER_AUTH_TRUSTED_ORIGINS?.split(",")
243
+ .map((origin) => origin.trim())
244
+ .filter(Boolean) ?? []),
245
+ ];
246
+
247
+ export const auth = betterAuth({
248
+ baseURL: env.BETTER_AUTH_URL,
249
+ secret: env.BETTER_AUTH_SECRET,
250
+ trustedOrigins: [...new Set(trustedOrigins)],
251
+ database: drizzleAdapter(db, {
252
+ provider: "${databaseDescriptor(ctx).betterAuthProvider}",
253
+ schema,
254
+ }),
255
+ emailAndPassword: {
256
+ enabled: true,
257
+ },
258
+ user: {
259
+ changeEmail: {
260
+ enabled: true,
261
+ },
262
+ deleteUser: {
263
+ enabled: true,
264
+ },
265
+ },
266
+ });
267
+ `;
268
+ }
269
+
270
+ export function server(ctx: TemplateContext): string {
271
+ const db = databaseDescriptor(ctx);
272
+ const providerConfigEntries =
273
+ ctx.database === "sqlite"
274
+ ? `\t\t"drizzle-sqlite": {
275
+ \t\t\tDB_URL: env.SQLITE_DB_URL,
276
+ \t\t\tDB_AUTH_TOKEN: env.SQLITE_DB_AUTH_TOKEN,
277
+ \t\t},`
278
+ : `\t\t"${db.providerName}": {
279
+ \t\t\tDB_URL: env.${db.urlEnvVar},
280
+ \t\t},`;
281
+
282
+ return `import type { beignetServerOnly } from "@beignet/core/server-only";
283
+ import { createNextServer } from "@beignet/next";
284
+ import { createIdempotencyHooks } from "@beignet/core/server";
285
+ import type { AppContext } from "@/app-context";
286
+ import { appPorts } from "@/infra/app-ports";
287
+ import { env } from "@/lib/env";
288
+ import { appContext } from "./context";
289
+ import { providers } from "./providers";
290
+ import { routes } from "./routes";
291
+
292
+ export const server = await createNextServer({
293
+ ports: appPorts,
294
+ providers,
295
+ providerConfig: {
296
+ ${providerConfigEntries}
297
+ },
298
+ hooks: [createIdempotencyHooks<AppContext>()],
299
+ context: appContext,
300
+ routes,
301
+ mapUnhandledError: ({ err, ctx }) => {
302
+ ctx?.ports.logger.error("Unhandled API error", {
303
+ error: err,
304
+ requestId: ctx?.requestId,
305
+ environment: env.NODE_ENV,
306
+ });
307
+
308
+ return {
309
+ status: 500,
310
+ body: {
311
+ code: "INTERNAL_SERVER_ERROR",
312
+ message: "Internal server error",
313
+ requestId: ctx?.requestId,
314
+ },
315
+ };
316
+ },
317
+ });
318
+ `;
319
+ }
320
+
321
+ const files = {
170
322
  appContext: `import type { ActivityActor, ActivityTenant } from "@beignet/core/ports";
171
323
  import type { InferProviderPorts } from "@beignet/core/providers";
172
324
  import type { TraceContext } from "@beignet/core/tracing";
@@ -329,46 +481,6 @@ export const appContext = defineServerContext<AppContext, AppRuntimePorts>()({
329
481
  tenant: createTenant(input?.tenantId ?? "tenant_default"),
330
482
  }),
331
483
  });
332
- `,
333
- server: `import type { beignetServerOnly } from "@beignet/core/server-only";
334
- import { createNextServer } from "@beignet/next";
335
- import { createIdempotencyHooks } from "@beignet/core/server";
336
- import type { AppContext } from "@/app-context";
337
- import { appPorts } from "@/infra/app-ports";
338
- import { env } from "@/lib/env";
339
- import { appContext } from "./context";
340
- import { providers } from "./providers";
341
- import { routes } from "./routes";
342
-
343
- export const server = await createNextServer({
344
- ports: appPorts,
345
- providers,
346
- providerConfig: {
347
- "drizzle-turso": {
348
- DB_URL: env.TURSO_DB_URL,
349
- DB_AUTH_TOKEN: env.TURSO_DB_AUTH_TOKEN,
350
- },
351
- },
352
- hooks: [createIdempotencyHooks<AppContext>()],
353
- context: appContext,
354
- routes,
355
- mapUnhandledError: ({ err, ctx }) => {
356
- ctx?.ports.logger.error("Unhandled API error", {
357
- error: err,
358
- requestId: ctx?.requestId,
359
- environment: env.NODE_ENV,
360
- });
361
-
362
- return {
363
- status: 500,
364
- body: {
365
- code: "INTERNAL_SERVER_ERROR",
366
- message: "Internal server error",
367
- requestId: ctx?.requestId,
368
- },
369
- };
370
- },
371
- });
372
484
  `,
373
485
  apiCatchAllRoute: `import { server } from "@/server";
374
486
 
@@ -418,54 +530,6 @@ export const GET = createOpenAPIHandler(server.contracts, {
418
530
  import { auth } from "@/lib/better-auth";
419
531
 
420
532
  export const { GET, POST } = toNextJsHandler(auth);
421
- `,
422
- betterAuth: `import { createClient } from "@libsql/client";
423
- import { betterAuth } from "better-auth";
424
- import { drizzleAdapter } from "better-auth/adapters/drizzle";
425
- import { drizzle } from "drizzle-orm/libsql";
426
- import { ensureDatabaseReady } from "@/infra/db/database-ready";
427
- import * as schema from "@/infra/db/schema";
428
- import { env } from "@/lib/env";
429
-
430
- const client = createClient({
431
- url: env.TURSO_DB_URL,
432
- authToken: env.TURSO_DB_AUTH_TOKEN,
433
- });
434
-
435
- // Auth routes can be the first thing to touch the database, before any
436
- // Beignet provider boots, so readiness is enforced here too.
437
- await ensureDatabaseReady(client);
438
-
439
- const db = drizzle(client, { schema });
440
-
441
- const trustedOrigins = [
442
- env.APP_URL,
443
- env.BETTER_AUTH_URL,
444
- ...(env.BETTER_AUTH_TRUSTED_ORIGINS?.split(",")
445
- .map((origin) => origin.trim())
446
- .filter(Boolean) ?? []),
447
- ];
448
-
449
- export const auth = betterAuth({
450
- baseURL: env.BETTER_AUTH_URL,
451
- secret: env.BETTER_AUTH_SECRET,
452
- trustedOrigins: [...new Set(trustedOrigins)],
453
- database: drizzleAdapter(db, {
454
- provider: "sqlite",
455
- schema,
456
- }),
457
- emailAndPassword: {
458
- enabled: true,
459
- },
460
- user: {
461
- changeEmail: {
462
- enabled: true,
463
- },
464
- deleteUser: {
465
- enabled: true,
466
- },
467
- },
468
- });
469
533
  `,
470
534
  // API-only scaffold frontend: a minimal layout and landing page that point
471
535
  // at the contract-backed API surface, plus a typed client without React
@@ -1,4 +1,8 @@
1
- import type { IntegrationName, PackageManager } from "../choices.js";
1
+ import type {
2
+ DatabaseName,
3
+ IntegrationName,
4
+ PackageManager,
5
+ } from "../choices.js";
2
6
 
3
7
  /**
4
8
  * One generated file emitted by the template generator.
@@ -20,6 +24,10 @@ export type TemplateContext = {
20
24
  */
21
25
  api: boolean;
22
26
  integrations: IntegrationName[];
27
+ /**
28
+ * Database backing the starter's Drizzle persistence layer.
29
+ */
30
+ database: DatabaseName;
23
31
  };
24
32
 
25
33
  export const externalVersions = {
@@ -40,12 +48,93 @@ export const externalVersions = {
40
48
  drizzleOrm: "^0.45.2",
41
49
  inngest: "^3.0.0",
42
50
  libsqlClient: "^0.14.0",
51
+ mysql2: "^3.12.0",
52
+ pg: "^8.13.0",
53
+ pglite: "^0.5.2",
43
54
  pino: "^9.7.0",
44
55
  resend: "^4.0.1",
56
+ typesPg: "^8.11.0",
45
57
  upstashRateLimit: "^2.0.0",
46
58
  upstashRedis: "^1.0.0",
47
59
  };
48
60
 
61
+ /**
62
+ * Dialect-specific names used when rendering database-aware templates.
63
+ */
64
+ export type DatabaseDescriptor = {
65
+ name: DatabaseName;
66
+ /** `@beignet/provider-db-drizzle` subpath for this dialect. */
67
+ subpath: "sqlite" | "postgres" | "mysql";
68
+ /** Pascal-case dialect token in provider API names, e.g. `createDrizzle${apiToken}UnitOfWork`. */
69
+ apiToken: "Sqlite" | "Postgres" | "Mysql";
70
+ /** Drizzle database seam type exported from the provider subpath. */
71
+ databaseType:
72
+ | "DrizzleSqliteDatabase"
73
+ | "DrizzlePostgresDatabase"
74
+ | "DrizzleMysqlDatabase";
75
+ /** Provider factory exported from the provider subpath. */
76
+ providerFactory:
77
+ | "createDrizzleSqliteProvider"
78
+ | "createDrizzlePostgresProvider"
79
+ | "createDrizzleMysqlProvider";
80
+ /** Provider instance const wired in `server/providers.ts`. */
81
+ providerInstance:
82
+ | "drizzleSqliteProvider"
83
+ | "drizzlePostgresProvider"
84
+ | "drizzleMysqlProvider";
85
+ /** Runtime provider name used as the `providerConfig` key. */
86
+ providerName: "drizzle-sqlite" | "drizzle-postgres" | "drizzle-mysql";
87
+ /** Connection URL env var read by the provider and app env schema. */
88
+ urlEnvVar: "SQLITE_DB_URL" | "POSTGRES_DB_URL" | "MYSQL_DB_URL";
89
+ /** `drizzle.config.ts` dialect string. */
90
+ drizzleKitDialect: "sqlite" | "postgresql" | "mysql";
91
+ /** Better Auth drizzle adapter provider string. */
92
+ betterAuthProvider: "sqlite" | "pg" | "mysql";
93
+ };
94
+
95
+ const databaseDescriptors: Record<DatabaseName, DatabaseDescriptor> = {
96
+ sqlite: {
97
+ name: "sqlite",
98
+ subpath: "sqlite",
99
+ apiToken: "Sqlite",
100
+ databaseType: "DrizzleSqliteDatabase",
101
+ providerFactory: "createDrizzleSqliteProvider",
102
+ providerInstance: "drizzleSqliteProvider",
103
+ providerName: "drizzle-sqlite",
104
+ urlEnvVar: "SQLITE_DB_URL",
105
+ drizzleKitDialect: "sqlite",
106
+ betterAuthProvider: "sqlite",
107
+ },
108
+ postgres: {
109
+ name: "postgres",
110
+ subpath: "postgres",
111
+ apiToken: "Postgres",
112
+ databaseType: "DrizzlePostgresDatabase",
113
+ providerFactory: "createDrizzlePostgresProvider",
114
+ providerInstance: "drizzlePostgresProvider",
115
+ providerName: "drizzle-postgres",
116
+ urlEnvVar: "POSTGRES_DB_URL",
117
+ drizzleKitDialect: "postgresql",
118
+ betterAuthProvider: "pg",
119
+ },
120
+ mysql: {
121
+ name: "mysql",
122
+ subpath: "mysql",
123
+ apiToken: "Mysql",
124
+ databaseType: "DrizzleMysqlDatabase",
125
+ providerFactory: "createDrizzleMysqlProvider",
126
+ providerInstance: "drizzleMysqlProvider",
127
+ providerName: "drizzle-mysql",
128
+ urlEnvVar: "MYSQL_DB_URL",
129
+ drizzleKitDialect: "mysql",
130
+ betterAuthProvider: "mysql",
131
+ },
132
+ };
133
+
134
+ export function databaseDescriptor(ctx: TemplateContext): DatabaseDescriptor {
135
+ return databaseDescriptors[ctx.database];
136
+ }
137
+
49
138
  export function packageRunner(ctx: TemplateContext): string {
50
139
  switch (ctx.packageManager) {
51
140
  case "npm":