@beignet/cli 0.0.36 → 0.0.38

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 (95) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +36 -5
  3. package/dist/config.d.ts +15 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +30 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/inspect.js +21 -17
  8. package/dist/inspect.js.map +1 -1
  9. package/dist/lint.d.ts.map +1 -1
  10. package/dist/lint.js +27 -6
  11. package/dist/lint.js.map +1 -1
  12. package/dist/make/inbox.js +7 -7
  13. package/dist/make/inbox.js.map +1 -1
  14. package/dist/make/payments.d.ts +2 -1
  15. package/dist/make/payments.d.ts.map +1 -1
  16. package/dist/make/payments.js +15 -11
  17. package/dist/make/payments.js.map +1 -1
  18. package/dist/make/shared.d.ts +1 -1
  19. package/dist/make/shared.d.ts.map +1 -1
  20. package/dist/make/shared.js +49 -37
  21. package/dist/make/shared.js.map +1 -1
  22. package/dist/make/tenancy.js +8 -8
  23. package/dist/make/tenancy.js.map +1 -1
  24. package/dist/make.d.ts.map +1 -1
  25. package/dist/make.js +27 -16
  26. package/dist/make.js.map +1 -1
  27. package/dist/operational-error-reporting.d.ts +15 -0
  28. package/dist/operational-error-reporting.d.ts.map +1 -0
  29. package/dist/operational-error-reporting.js +42 -0
  30. package/dist/operational-error-reporting.js.map +1 -0
  31. package/dist/outbox.d.ts.map +1 -1
  32. package/dist/outbox.js +73 -0
  33. package/dist/outbox.js.map +1 -1
  34. package/dist/provider-add.js +17 -17
  35. package/dist/provider-add.js.map +1 -1
  36. package/dist/provider-audit.d.ts.map +1 -1
  37. package/dist/provider-audit.js +11 -6
  38. package/dist/provider-audit.js.map +1 -1
  39. package/dist/schedule.d.ts.map +1 -1
  40. package/dist/schedule.js +28 -1
  41. package/dist/schedule.js.map +1 -1
  42. package/dist/task.d.ts.map +1 -1
  43. package/dist/task.js +37 -4
  44. package/dist/task.js.map +1 -1
  45. package/dist/templates/base.d.ts.map +1 -1
  46. package/dist/templates/base.js +6 -1
  47. package/dist/templates/base.js.map +1 -1
  48. package/dist/templates/db/index.d.ts +1 -0
  49. package/dist/templates/db/index.d.ts.map +1 -1
  50. package/dist/templates/db/index.js.map +1 -1
  51. package/dist/templates/db/mysql.d.ts.map +1 -1
  52. package/dist/templates/db/mysql.js +16 -0
  53. package/dist/templates/db/mysql.js.map +1 -1
  54. package/dist/templates/db/postgres.d.ts.map +1 -1
  55. package/dist/templates/db/postgres.js +16 -0
  56. package/dist/templates/db/postgres.js.map +1 -1
  57. package/dist/templates/db/sqlite.d.ts.map +1 -1
  58. package/dist/templates/db/sqlite.js +16 -0
  59. package/dist/templates/db/sqlite.js.map +1 -1
  60. package/dist/templates/index.d.ts.map +1 -1
  61. package/dist/templates/index.js +4 -3
  62. package/dist/templates/index.js.map +1 -1
  63. package/dist/templates/server.d.ts +2 -2
  64. package/dist/templates/server.d.ts.map +1 -1
  65. package/dist/templates/server.js +56 -40
  66. package/dist/templates/server.js.map +1 -1
  67. package/dist/templates/shared.js +3 -3
  68. package/dist/templates/shared.js.map +1 -1
  69. package/dist/templates/testing.js +1 -1
  70. package/dist/templates/todos.js +6 -6
  71. package/package.json +4 -4
  72. package/src/config.ts +66 -2
  73. package/src/inspect.ts +31 -19
  74. package/src/lint.ts +35 -4
  75. package/src/make/inbox.ts +7 -7
  76. package/src/make/payments.ts +23 -15
  77. package/src/make/shared.ts +56 -40
  78. package/src/make/tenancy.ts +8 -8
  79. package/src/make.ts +30 -16
  80. package/src/operational-error-reporting.ts +60 -0
  81. package/src/outbox.ts +77 -0
  82. package/src/provider-add.ts +21 -17
  83. package/src/provider-audit.ts +28 -11
  84. package/src/schedule.ts +32 -1
  85. package/src/task.ts +41 -4
  86. package/src/templates/base.ts +6 -1
  87. package/src/templates/db/index.ts +1 -0
  88. package/src/templates/db/mysql.ts +16 -0
  89. package/src/templates/db/postgres.ts +16 -0
  90. package/src/templates/db/sqlite.ts +16 -0
  91. package/src/templates/index.ts +4 -3
  92. package/src/templates/server.ts +56 -42
  93. package/src/templates/shared.ts +3 -3
  94. package/src/templates/testing.ts +1 -1
  95. package/src/templates/todos.ts +6 -6
@@ -23,6 +23,7 @@ export function serverProviders(ctx: TemplateContext): string {
23
23
  ? 'import { createUpstashRateLimitProvider } from "@beignet/provider-rate-limit-upstash";'
24
24
  : undefined,
25
25
  'import * as schema from "@/infra/db/schema";',
26
+ 'import { databaseClient } from "@/infra/db/client";',
26
27
  'import { starterDatabaseProvider } from "@/infra/db/provider";',
27
28
  'import { auth } from "@/lib/better-auth";',
28
29
  'import type { AuthSessionMetadata, AuthUser } from "@/ports/auth";',
@@ -45,7 +46,10 @@ export function serverProviders(ctx: TemplateContext): string {
45
46
 
46
47
  return `${imports.join("\n")}
47
48
 
48
- const ${db.providerInstance} = ${db.providerFactory}({ schema });
49
+ const ${db.providerInstance} = ${db.providerFactory}({
50
+ schema,
51
+ client: databaseClient,
52
+ });
49
53
 
50
54
  export const providers = [
51
55
  ${entries.join("\n")}
@@ -97,7 +101,7 @@ ${usesResend ? "\tmailer: MailerPort;\n" : ""}${usesUpstash ? "\trateLimit: Rate
97
101
  `;
98
102
  }
99
103
 
100
- export function infrastructurePorts(ctx: TemplateContext): string {
104
+ export function portWiring(ctx: TemplateContext): string {
101
105
  const deferred = [
102
106
  '\t\t"auth",',
103
107
  '\t\t"idempotency",',
@@ -126,13 +130,13 @@ const gate = createGate({
126
130
  });
127
131
 
128
132
  /**
129
- * App ports the server boots with.
133
+ * Initial port values the server boots with.
130
134
  *
131
135
  * Bound ports are app-owned. Every deferred key is contributed by a provider
132
136
  * in server/providers.ts during startup; createNextServer(...) fails boot if
133
137
  * any of them is still unbound after providers have started.
134
138
  */
135
- export const appPorts = definePorts<AppPorts>()({
139
+ export const initialPorts = definePorts<AppPorts>()({
136
140
  bound: {
137
141
  errorReporter: createNoopErrorReporter(),
138
142
  gate,
@@ -151,7 +155,8 @@ export function env(ctx: TemplateContext): string {
151
155
  \t\tSQLITE_DB_AUTH_TOKEN: z.string().optional(),`
152
156
  : `\t\t${databaseDescriptor(ctx).urlEnvVar}: z
153
157
  \t\t\t.string()
154
- \t\t\t.default("${databaseLocalUrl(ctx.database, ctx.name)}"),`;
158
+ \t\t\t.default("${databaseLocalUrl(ctx.database, ctx.name)}"),
159
+ \t\t${ctx.database === "postgres" ? "POSTGRES" : "MYSQL"}_DB_POOL_MAX: z.coerce.number().int().positive().default(5),`;
155
160
 
156
161
  return `import { createEnv } from "@beignet/core/config";
157
162
  import { z } from "zod";
@@ -202,54 +207,45 @@ export const isDevtoolsEnabled =
202
207
 
203
208
  export function betterAuth(ctx: TemplateContext): string {
204
209
  const connectionByDatabase = {
205
- sqlite: `import { createClient } from "@libsql/client";
206
- import { betterAuth } from "better-auth";
210
+ sqlite: `import { betterAuth } from "better-auth";
207
211
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
208
212
  import { drizzle } from "drizzle-orm/libsql";
213
+ import { databaseClient } from "@/infra/db/client";
209
214
  import { ensureDatabaseReady } from "@/infra/db/database-ready";
210
215
  import * as schema from "@/infra/db/schema";
211
216
  import { env } from "@/lib/env";
212
217
 
213
- const client = createClient({
214
- url: env.SQLITE_DB_URL,
215
- authToken: env.SQLITE_DB_AUTH_TOKEN,
216
- });
217
-
218
218
  // Auth routes can be the first thing to touch the database, before any
219
219
  // Beignet provider boots, so readiness is enforced here too.
220
- await ensureDatabaseReady(client);
220
+ await ensureDatabaseReady(databaseClient);
221
221
 
222
- const db = drizzle(client, { schema });`,
222
+ const db = drizzle(databaseClient, { schema });`,
223
223
  postgres: `import { betterAuth } from "better-auth";
224
224
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
225
225
  import { drizzle } from "drizzle-orm/node-postgres";
226
- import pg from "pg";
226
+ import { databaseClient } from "@/infra/db/client";
227
227
  import { ensureDatabaseReady } from "@/infra/db/database-ready";
228
228
  import * as schema from "@/infra/db/schema";
229
229
  import { env } from "@/lib/env";
230
230
 
231
- const pool = new pg.Pool({ connectionString: env.POSTGRES_DB_URL });
232
-
233
231
  // Auth routes can be the first thing to touch the database, before any
234
232
  // Beignet provider boots, so readiness is enforced here too.
235
- await ensureDatabaseReady(pool);
233
+ await ensureDatabaseReady(databaseClient);
236
234
 
237
- const db = drizzle(pool, { schema });`,
235
+ const db = drizzle(databaseClient, { schema });`,
238
236
  mysql: `import { betterAuth } from "better-auth";
239
237
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
240
238
  import { drizzle } from "drizzle-orm/mysql2";
241
- import mysql from "mysql2/promise";
239
+ import { databaseClient } from "@/infra/db/client";
242
240
  import { ensureDatabaseReady } from "@/infra/db/database-ready";
243
241
  import * as schema from "@/infra/db/schema";
244
242
  import { env } from "@/lib/env";
245
243
 
246
- const pool = mysql.createPool({ uri: env.MYSQL_DB_URL });
247
-
248
244
  // Auth routes can be the first thing to touch the database, before any
249
245
  // Beignet provider boots, so readiness is enforced here too.
250
- await ensureDatabaseReady(pool);
246
+ await ensureDatabaseReady(databaseClient);
251
247
 
252
- const db = drizzle(pool, { schema, mode: "default" });`,
248
+ const db = drizzle(databaseClient, { schema, mode: "default" });`,
253
249
  } as const;
254
250
 
255
251
  return `${connectionByDatabase[ctx.database]}
@@ -285,18 +281,7 @@ export const auth = betterAuth({
285
281
  `;
286
282
  }
287
283
 
288
- export function server(ctx: TemplateContext): string {
289
- const db = databaseDescriptor(ctx);
290
- const providerConfigEntries =
291
- ctx.database === "sqlite"
292
- ? `\t\t"drizzle-sqlite": {
293
- \t\t\tDB_URL: env.SQLITE_DB_URL,
294
- \t\t\tDB_AUTH_TOKEN: env.SQLITE_DB_AUTH_TOKEN,
295
- \t\t},`
296
- : `\t\t"${db.providerName}": {
297
- \t\t\tDB_URL: env.${db.urlEnvVar},
298
- \t\t},`;
299
-
284
+ export function server(): string {
300
285
  return `import "@beignet/core/server-only";
301
286
  import { createNextServer, createNextServerLoader } from "@beignet/next";
302
287
  import {
@@ -305,7 +290,8 @@ import {
305
290
  createSecurityHeadersHooks,
306
291
  } from "@beignet/core/server";
307
292
  import type { AppContext } from "@/app-context";
308
- import { appPorts } from "@/infra/app-ports";
293
+ import { initialPorts } from "@/infra/port-wiring";
294
+ import { closeDatabaseClient } from "@/infra/db/client";
309
295
  import { env } from "@/lib/env";
310
296
  import { appContext } from "./context";
311
297
  import { routes } from "./routes";
@@ -313,12 +299,9 @@ import { routes } from "./routes";
313
299
  export const getServer = createNextServerLoader(async () => {
314
300
  const { providers } = await import("./providers");
315
301
 
316
- return createNextServer({
317
- ports: appPorts,
302
+ const server = await createNextServer({
303
+ ports: initialPorts,
318
304
  providers,
319
- providerConfig: {
320
- ${providerConfigEntries}
321
- },
322
305
  hooks: [
323
306
  createSecurityHeadersHooks<AppContext>(),
324
307
  createErrorReportingHooks<AppContext>(),
@@ -343,7 +326,38 @@ ${providerConfigEntries}
343
326
  };
344
327
  },
345
328
  });
329
+ let stopPromise: Promise<void> | undefined;
330
+
331
+ return {
332
+ ...server,
333
+ stop() {
334
+ stopPromise ??= stopServerAndDatabase(server);
335
+ return stopPromise;
336
+ },
337
+ };
346
338
  });
339
+
340
+ async function stopServerAndDatabase(server: {
341
+ stop(): Promise<void>;
342
+ }): Promise<void> {
343
+ const errors: unknown[] = [];
344
+ try {
345
+ await server.stop();
346
+ } catch (error) {
347
+ errors.push(error);
348
+ }
349
+
350
+ try {
351
+ await closeDatabaseClient();
352
+ } catch (error) {
353
+ errors.push(error);
354
+ }
355
+
356
+ if (errors.length === 1) throw errors[0];
357
+ if (errors.length > 1) {
358
+ throw new AggregateError(errors, "Server and database shutdown failed");
359
+ }
360
+ }
347
361
  `;
348
362
  }
349
363
 
@@ -32,7 +32,7 @@ export type TemplateContext = {
32
32
 
33
33
  export const externalVersions = {
34
34
  biome: "^2.4.14",
35
- next: "^16.2.4",
35
+ next: "^16.2.10",
36
36
  react: "^19.2.5",
37
37
  reactDom: "^19.2.5",
38
38
  tsx: "^4.22.4",
@@ -48,7 +48,7 @@ export const externalVersions = {
48
48
  bullmq: "^5.0.0",
49
49
  drizzleKit: "^0.31.10",
50
50
  drizzleOrm: "^0.45.2",
51
- inngest: "^3.0.0",
51
+ inngest: "^3.54.0",
52
52
  libsqlClient: "^0.14.0",
53
53
  vercelBlob: "^2.5.0",
54
54
  mysql2: "^3.12.0",
@@ -58,7 +58,7 @@ export const externalVersions = {
58
58
  resend: "^6.14.0",
59
59
  sentryNode: "^10.58.0",
60
60
  stripe: "^22.2.1",
61
- nodemailer: "^8.0.5",
61
+ nodemailer: "^9.0.1",
62
62
  openFeatureServerSdk: "^1.22.0",
63
63
  typesPg: "^8.11.0",
64
64
  ioredis: "^5.0.0",
@@ -536,7 +536,7 @@ async function configuredRoots(cwd: string): Promise<string[]> {
536
536
  paths.features,
537
537
  parentPath(paths.routes),
538
538
  parentPath(paths.server),
539
- parentPath(paths.infrastructurePorts),
539
+ parentPath(paths.portWiring),
540
540
  parentPath(paths.ports),
541
541
  parentPath(paths.useCaseBuilder),
542
542
  parentPath(paths.routesBuilder),
@@ -368,7 +368,7 @@ import {
368
368
  import { createInMemoryDevtools } from "@beignet/devtools";
369
369
  import type { AppContext } from "@/app-context";
370
370
  import type { TodoRepository } from "@/features/todos/ports";
371
- import { appPorts } from "@/infra/app-ports";
371
+ import { initialPorts } from "@/infra/port-wiring";
372
372
  import type { AppTransactionPorts } from "@/ports";
373
373
  import {
374
374
  createTodoUseCase,
@@ -388,9 +388,9 @@ function createTester(userId: string, todos: TodoRepository) {
388
388
  session: { id: \`session_\${userId}\` },
389
389
  };
390
390
  const testFixture = createTestPorts<AppContext["ports"], AppTransactionPorts>({
391
- base: appPorts,
391
+ base: initialPorts,
392
392
  overrides: {
393
- gate: appPorts.gate,
393
+ gate: initialPorts.gate,
394
394
  todos,
395
395
  devtools: createInMemoryDevtools(),
396
396
  },
@@ -483,7 +483,7 @@ import { createInMemoryDevtools } from "@beignet/devtools";
483
483
  import { createTestApp, createTestRequester } from "@beignet/web/testing";
484
484
  import type { AppContext } from "@/app-context";
485
485
  import type { TodoRepository } from "@/features/todos/ports";
486
- import { appPorts } from "@/infra/app-ports";
486
+ import { initialPorts } from "@/infra/port-wiring";
487
487
  import type { AppPorts, AppTransactionPorts } from "@/ports";
488
488
  import type { AuthRequest, AuthSessionMetadata, AuthUser } from "@/ports/auth";
489
489
  import { type AppServiceContextInput, appContext } from "@/server/context";
@@ -507,9 +507,9 @@ async function createTodosTestApp(
507
507
  ) {
508
508
  const todos = options.todos ?? createTestTodoRepository();
509
509
  const fixture = createTestPorts<AppContext["ports"], AppTransactionPorts>({
510
- base: appPorts,
510
+ base: initialPorts,
511
511
  overrides: {
512
- gate: appPorts.gate,
512
+ gate: initialPorts.gate,
513
513
  todos,
514
514
  devtools: createInMemoryDevtools(),
515
515
  auth: options.auth ?? createSignedOutAuth(),