@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
@@ -5,9 +5,14 @@
5
5
  // To regenerate: scaffold a scratch app with `dbSchema` as
6
6
  // infra/db/schema/index.ts plus `drizzleConfig` as drizzle.config.ts, run
7
7
  // `drizzle-kit generate --name starter` with the drizzle-kit version pinned in
8
- // externalVersions, then paste drizzle/0000_starter.sql,
9
- // drizzle/meta/_journal.json, and drizzle/meta/0000_snapshot.json back here
10
- // verbatim.
8
+ // externalVersions, paste drizzle/0000_starter.sql, drizzle/meta/_journal.json,
9
+ // and drizzle/meta/0000_snapshot.json back here verbatim, then append the
10
+ // output of createDrizzleSqliteIdempotencySetupStatements() from
11
+ // @beignet/provider-db-drizzle/sqlite to the migration SQL, separated by
12
+ // `--> statement-breakpoint`. The idempotency table is provider-owned and
13
+ // lives outside the app schema, so `db generate` never diffs against it.
14
+
15
+ import type { DbTemplateFiles } from "./index.js";
11
16
 
12
17
  const files = {
13
18
  drizzleConfig: `export default {
@@ -15,8 +20,8 @@ const files = {
15
20
  out: "./drizzle",
16
21
  dialect: "sqlite",
17
22
  dbCredentials: {
18
- url: process.env.TURSO_DB_URL ?? "file:local.db",
19
- authToken: process.env.TURSO_DB_AUTH_TOKEN,
23
+ url: process.env.SQLITE_DB_URL ?? "file:local.db",
24
+ authToken: process.env.SQLITE_DB_AUTH_TOKEN,
20
25
  },
21
26
  };
22
27
  `,
@@ -95,35 +100,10 @@ export const todos = sqliteTable(
95
100
  userIdx: index("todos_user_idx").on(table.userId, table.createdAt),
96
101
  }),
97
102
  );
98
-
99
- export const idempotencyRecords = sqliteTable(
100
- "idempotency_records",
101
- {
102
- storageKey: text("storage_key").primaryKey(),
103
- namespace: text("namespace").notNull(),
104
- idempotencyKey: text("idempotency_key").notNull(),
105
- scopeKey: text("scope_key").notNull(),
106
- fingerprint: text("fingerprint").notNull(),
107
- status: text("status", { enum: ["in-progress", "completed"] }).notNull(),
108
- resultJson: text("result_json"),
109
- reservedAt: text("reserved_at").notNull(),
110
- completedAt: text("completed_at"),
111
- expiresAt: text("expires_at"),
112
- updatedAt: text("updated_at").notNull(),
113
- },
114
- (table) => ({
115
- lookupIdx: index("idempotency_records_lookup_idx").on(
116
- table.namespace,
117
- table.scopeKey,
118
- table.idempotencyKey,
119
- ),
120
- expiresIdx: index("idempotency_records_expires_idx").on(table.expiresAt),
121
- }),
122
- );
123
103
  `,
124
104
  drizzleTodoRepository: `import type { beignetServerOnly } from "@beignet/core/server-only";
125
105
  import { offsetPageResult } from "@beignet/core/pagination";
126
- import type { DrizzleTursoDatabase } from "@beignet/provider-drizzle-turso";
106
+ import type { DrizzleSqliteDatabase } from "@beignet/provider-db-drizzle/sqlite";
127
107
  import { count, desc, eq } from "drizzle-orm";
128
108
  import type {
129
109
  NewTodo,
@@ -146,7 +126,7 @@ function toTodo(row: TodoRow): Todo {
146
126
  }
147
127
 
148
128
  export function createDrizzleTodoRepository(
149
- db: DrizzleTursoDatabase<typeof schema>,
129
+ db: DrizzleSqliteDatabase<typeof schema>,
150
130
  ): TodoRepository {
151
131
  return {
152
132
  async list(userId, page) {
@@ -208,13 +188,13 @@ export function createDrizzleTodoRepository(
208
188
  };
209
189
  }
210
190
  `,
211
- dbRepositories: `import type { DrizzleTursoDatabase } from "@beignet/provider-drizzle-turso";
191
+ dbRepositories: `import type { DrizzleSqliteDatabase } from "@beignet/provider-db-drizzle/sqlite";
212
192
  import { createDrizzleTodoRepository } from "@/infra/todos/drizzle-todo-repository";
213
193
  import type { AppTransactionPorts } from "@/ports";
214
194
  import * as schema from "./schema";
215
195
 
216
196
  export function createRepositories(
217
- db: DrizzleTursoDatabase<typeof schema>,
197
+ db: DrizzleSqliteDatabase<typeof schema>,
218
198
  ): Omit<AppTransactionPorts, "idempotency"> {
219
199
  return {
220
200
  todos: createDrizzleTodoRepository(db),
@@ -265,10 +245,10 @@ async function prepare(client: Client): Promise<void> {
265
245
  dbProvider: `import type { beignetServerOnly } from "@beignet/core/server-only";
266
246
  import { createProvider } from "@beignet/core/providers";
267
247
  import {
268
- createDrizzleTursoIdempotencyPort,
269
- createDrizzleTursoUnitOfWork,
248
+ createDrizzleSqliteIdempotencyPort,
249
+ createDrizzleSqliteUnitOfWork,
270
250
  type DbPort,
271
- } from "@beignet/provider-drizzle-turso";
251
+ } from "@beignet/provider-db-drizzle/sqlite";
272
252
  import type { AppPorts } from "@/ports";
273
253
  import { ensureDatabaseReady } from "./database-ready";
274
254
  import { createRepositories } from "./repositories";
@@ -283,21 +263,21 @@ export const starterDatabaseProvider = createProvider<{
283
263
  const dbPort = ports.db;
284
264
  if (!dbPort) {
285
265
  throw new Error(
286
- "starterDatabaseProvider requires a db port. Register createDrizzleTursoProvider({ schema }) before it.",
266
+ "starterDatabaseProvider requires a db port. Register createDrizzleSqliteProvider({ schema }) before it.",
287
267
  );
288
268
  }
289
269
 
290
270
  const repositories = createRepositories(dbPort.db);
291
- const idempotency = createDrizzleTursoIdempotencyPort(dbPort.db);
271
+ const idempotency = createDrizzleSqliteIdempotencyPort(dbPort.db);
292
272
 
293
273
  const providedPorts: Pick<AppPorts, "idempotency" | "todos" | "uow"> = {
294
274
  ...repositories,
295
275
  idempotency,
296
- uow: createDrizzleTursoUnitOfWork({
276
+ uow: createDrizzleSqliteUnitOfWork({
297
277
  db: dbPort.db,
298
278
  createTransactionPorts: (tx) => ({
299
279
  ...createRepositories(tx),
300
- idempotency: createDrizzleTursoIdempotencyPort(tx),
280
+ idempotency: createDrizzleSqliteIdempotencyPort(tx),
301
281
  }),
302
282
  }),
303
283
  };
@@ -317,7 +297,7 @@ import { migrate } from "drizzle-orm/libsql/migrator";
317
297
  import { env } from "@/lib/env";
318
298
 
319
299
  if (
320
- !env.TURSO_DB_URL.startsWith("file:") &&
300
+ !env.SQLITE_DB_URL.startsWith("file:") &&
321
301
  process.env.BEIGNET_ALLOW_DATABASE_RESET !== "true"
322
302
  ) {
323
303
  throw new Error(
@@ -336,8 +316,8 @@ const tables = [
336
316
  ] as const;
337
317
 
338
318
  const client = createClient({
339
- url: env.TURSO_DB_URL,
340
- authToken: env.TURSO_DB_AUTH_TOKEN,
319
+ url: env.SQLITE_DB_URL,
320
+ authToken: env.SQLITE_DB_AUTH_TOKEN,
341
321
  });
342
322
 
343
323
  try {
@@ -393,7 +373,8 @@ export async function createTestDatabase(): Promise<TestDatabase> {
393
373
  };
394
374
  }
395
375
  `,
396
- // Generated by drizzle-kit generate --name starter against dbSchema above.
376
+ // Generated by drizzle-kit generate --name starter against dbSchema
377
+ // above, with the provider idempotency setup statements appended.
397
378
  starterMigrationSql: `CREATE TABLE \`account\` (
398
379
  \`id\` text PRIMARY KEY NOT NULL,
399
380
  \`account_id\` text NOT NULL,
@@ -411,22 +392,6 @@ export async function createTestDatabase(): Promise<TestDatabase> {
411
392
  FOREIGN KEY (\`user_id\`) REFERENCES \`user\`(\`id\`) ON UPDATE no action ON DELETE cascade
412
393
  );
413
394
  --> statement-breakpoint
414
- CREATE TABLE \`idempotency_records\` (
415
- \`storage_key\` text PRIMARY KEY NOT NULL,
416
- \`namespace\` text NOT NULL,
417
- \`idempotency_key\` text NOT NULL,
418
- \`scope_key\` text NOT NULL,
419
- \`fingerprint\` text NOT NULL,
420
- \`status\` text NOT NULL,
421
- \`result_json\` text,
422
- \`reserved_at\` text NOT NULL,
423
- \`completed_at\` text,
424
- \`expires_at\` text,
425
- \`updated_at\` text NOT NULL
426
- );
427
- --> statement-breakpoint
428
- CREATE INDEX \`idempotency_records_lookup_idx\` ON \`idempotency_records\` (\`namespace\`,\`scope_key\`,\`idempotency_key\`);--> statement-breakpoint
429
- CREATE INDEX \`idempotency_records_expires_idx\` ON \`idempotency_records\` (\`expires_at\`);--> statement-breakpoint
430
395
  CREATE TABLE \`session\` (
431
396
  \`id\` text PRIMARY KEY NOT NULL,
432
397
  \`expires_at\` integer NOT NULL,
@@ -469,6 +434,22 @@ CREATE TABLE \`verification\` (
469
434
  \`created_at\` integer,
470
435
  \`updated_at\` integer
471
436
  );
437
+ --> statement-breakpoint
438
+ CREATE TABLE IF NOT EXISTS "idempotency_records" (
439
+ storage_key text PRIMARY KEY NOT NULL,
440
+ namespace text NOT NULL,
441
+ idempotency_key text NOT NULL,
442
+ scope_key text NOT NULL,
443
+ fingerprint text NOT NULL,
444
+ status text NOT NULL,
445
+ result_json text,
446
+ reserved_at text NOT NULL,
447
+ completed_at text,
448
+ expires_at text,
449
+ updated_at text NOT NULL
450
+ );--> statement-breakpoint
451
+ CREATE INDEX IF NOT EXISTS "idempotency_records_lookup_idx" ON "idempotency_records" (namespace, scope_key, idempotency_key);--> statement-breakpoint
452
+ CREATE INDEX IF NOT EXISTS "idempotency_records_expires_idx" ON "idempotency_records" (expires_at);
472
453
  `,
473
454
  starterMigrationJournal: `{
474
455
  "version": "7",
@@ -477,7 +458,7 @@ CREATE TABLE \`verification\` (
477
458
  {
478
459
  "idx": 0,
479
460
  "version": "6",
480
- "when": 1781185557868,
461
+ "when": 1781285554238,
481
462
  "tag": "0000_starter",
482
463
  "breakpoints": true
483
464
  }
@@ -486,7 +467,7 @@ CREATE TABLE \`verification\` (
486
467
  starterMigrationSnapshot: `{
487
468
  "version": "6",
488
469
  "dialect": "sqlite",
489
- "id": "5131bbf1-cff0-42de-a5f4-298187fdafd1",
470
+ "id": "1fa6ba12-5a4d-4815-bda1-225734f18921",
490
471
  "prevId": "00000000-0000-0000-0000-000000000000",
491
472
  "tables": {
492
473
  "account": {
@@ -604,110 +585,6 @@ CREATE TABLE \`verification\` (
604
585
  "uniqueConstraints": {},
605
586
  "checkConstraints": {}
606
587
  },
607
- "idempotency_records": {
608
- "name": "idempotency_records",
609
- "columns": {
610
- "storage_key": {
611
- "name": "storage_key",
612
- "type": "text",
613
- "primaryKey": true,
614
- "notNull": true,
615
- "autoincrement": false
616
- },
617
- "namespace": {
618
- "name": "namespace",
619
- "type": "text",
620
- "primaryKey": false,
621
- "notNull": true,
622
- "autoincrement": false
623
- },
624
- "idempotency_key": {
625
- "name": "idempotency_key",
626
- "type": "text",
627
- "primaryKey": false,
628
- "notNull": true,
629
- "autoincrement": false
630
- },
631
- "scope_key": {
632
- "name": "scope_key",
633
- "type": "text",
634
- "primaryKey": false,
635
- "notNull": true,
636
- "autoincrement": false
637
- },
638
- "fingerprint": {
639
- "name": "fingerprint",
640
- "type": "text",
641
- "primaryKey": false,
642
- "notNull": true,
643
- "autoincrement": false
644
- },
645
- "status": {
646
- "name": "status",
647
- "type": "text",
648
- "primaryKey": false,
649
- "notNull": true,
650
- "autoincrement": false
651
- },
652
- "result_json": {
653
- "name": "result_json",
654
- "type": "text",
655
- "primaryKey": false,
656
- "notNull": false,
657
- "autoincrement": false
658
- },
659
- "reserved_at": {
660
- "name": "reserved_at",
661
- "type": "text",
662
- "primaryKey": false,
663
- "notNull": true,
664
- "autoincrement": false
665
- },
666
- "completed_at": {
667
- "name": "completed_at",
668
- "type": "text",
669
- "primaryKey": false,
670
- "notNull": false,
671
- "autoincrement": false
672
- },
673
- "expires_at": {
674
- "name": "expires_at",
675
- "type": "text",
676
- "primaryKey": false,
677
- "notNull": false,
678
- "autoincrement": false
679
- },
680
- "updated_at": {
681
- "name": "updated_at",
682
- "type": "text",
683
- "primaryKey": false,
684
- "notNull": true,
685
- "autoincrement": false
686
- }
687
- },
688
- "indexes": {
689
- "idempotency_records_lookup_idx": {
690
- "name": "idempotency_records_lookup_idx",
691
- "columns": [
692
- "namespace",
693
- "scope_key",
694
- "idempotency_key"
695
- ],
696
- "isUnique": false
697
- },
698
- "idempotency_records_expires_idx": {
699
- "name": "idempotency_records_expires_idx",
700
- "columns": [
701
- "expires_at"
702
- ],
703
- "isUnique": false
704
- }
705
- },
706
- "foreignKeys": {},
707
- "compositePrimaryKeys": {},
708
- "uniqueConstraints": {},
709
- "checkConstraints": {}
710
- },
711
588
  "session": {
712
589
  "name": "session",
713
590
  "columns": {
@@ -999,4 +876,6 @@ CREATE TABLE \`verification\` (
999
876
  }`,
1000
877
  };
1001
878
 
1002
- export { files as dbFiles };
879
+ export function sqliteDbFiles(): DbTemplateFiles {
880
+ return files;
881
+ }
@@ -1,9 +1,10 @@
1
1
  export type {
2
+ DatabaseName,
2
3
  IntegrationName,
3
4
  PackageManager,
4
5
  TemplateName,
5
6
  } from "../choices.js";
6
- export { integrationChoices } from "../choices.js";
7
+ export { databaseChoices, integrationChoices } from "../choices.js";
7
8
  export type { TemplateContext, TemplateFile } from "./shared.js";
8
9
 
9
10
  import {
@@ -13,10 +14,13 @@ import {
13
14
  packageJson,
14
15
  readme,
15
16
  } from "./base.js";
16
- import { dbFiles } from "./db.js";
17
+ import { dbFiles } from "./db/index.js";
17
18
  import {
19
+ betterAuth,
20
+ env,
18
21
  infrastructurePorts,
19
22
  ports,
23
+ server,
20
24
  serverFiles,
21
25
  serverProviders,
22
26
  } from "./server.js";
@@ -29,11 +33,12 @@ import { todosFiles } from "./todos.js";
29
33
  * Render all template files for a new Beignet app.
30
34
  *
31
35
  * There is one starter: a full-stack app with a typed API, Better Auth,
32
- * Drizzle/libSQL persistence, pino logging, devtools, and a shadcn UI shell.
33
- * `ctx.api` swaps the UI shell for a minimal API landing page and a reduced
34
- * typed client.
36
+ * Drizzle persistence (SQLite, Postgres, or MySQL via `ctx.database`), pino
37
+ * logging, devtools, and a shadcn UI shell. `ctx.api` swaps the UI shell for
38
+ * a minimal API landing page and a reduced typed client.
35
39
  */
36
40
  export function getTemplateFiles(ctx: TemplateContext): TemplateFile[] {
41
+ const db = dbFiles(ctx);
37
42
  const templateFiles: TemplateFile[] = [
38
43
  { path: "package.json", content: packageJson(ctx) },
39
44
  { path: "README.md", content: readme(ctx) },
@@ -98,31 +103,31 @@ export function getTemplateFiles(ctx: TemplateContext): TemplateFile[] {
98
103
  { path: "ports/index.ts", content: ports(ctx) },
99
104
  { path: "ports/auth.ts", content: serverFiles.authPort },
100
105
  { path: "infra/app-ports.ts", content: infrastructurePorts(ctx) },
101
- { path: "infra/db/database-ready.ts", content: dbFiles.databaseReady },
102
- { path: "infra/db/provider.ts", content: dbFiles.dbProvider },
103
- { path: "infra/db/repositories.ts", content: dbFiles.dbRepositories },
104
- { path: "infra/db/reset.ts", content: dbFiles.dbReset },
105
- { path: "infra/db/test-database.ts", content: dbFiles.dbTestDatabase },
106
- { path: "infra/db/schema/index.ts", content: dbFiles.dbSchema },
106
+ { path: "infra/db/database-ready.ts", content: db.databaseReady },
107
+ { path: "infra/db/provider.ts", content: db.dbProvider },
108
+ { path: "infra/db/repositories.ts", content: db.dbRepositories },
109
+ { path: "infra/db/reset.ts", content: db.dbReset },
110
+ { path: "infra/db/test-database.ts", content: db.dbTestDatabase },
111
+ { path: "infra/db/schema/index.ts", content: db.dbSchema },
107
112
  {
108
113
  path: "infra/todos/drizzle-todo-repository.ts",
109
- content: dbFiles.drizzleTodoRepository,
114
+ content: db.drizzleTodoRepository,
110
115
  },
111
- { path: "drizzle.config.ts", content: dbFiles.drizzleConfig },
112
- { path: "drizzle/0000_starter.sql", content: dbFiles.starterMigrationSql },
116
+ { path: "drizzle.config.ts", content: db.drizzleConfig },
117
+ { path: "drizzle/0000_starter.sql", content: db.starterMigrationSql },
113
118
  {
114
119
  path: "drizzle/meta/_journal.json",
115
- content: dbFiles.starterMigrationJournal,
120
+ content: db.starterMigrationJournal,
116
121
  },
117
122
  {
118
123
  path: "drizzle/meta/0000_snapshot.json",
119
- content: dbFiles.starterMigrationSnapshot,
124
+ content: db.starterMigrationSnapshot,
120
125
  },
121
- { path: "lib/env.ts", content: serverFiles.env },
126
+ { path: "lib/env.ts", content: env(ctx) },
122
127
  { path: "lib/auth.ts", content: serverFiles.authHelpers },
123
- { path: "lib/better-auth.ts", content: serverFiles.betterAuth },
128
+ { path: "lib/better-auth.ts", content: betterAuth(ctx) },
124
129
  { path: "lib/use-case.ts", content: serverFiles.useCaseBuilder },
125
- { path: "server/index.ts", content: serverFiles.server },
130
+ { path: "server/index.ts", content: server(ctx) },
126
131
  { path: "server/context.ts", content: serverFiles.serverContext },
127
132
  { path: "server/routes.ts", content: serverFiles.serverRoutes },
128
133
  { path: "server/providers.ts", content: serverProviders(ctx) },