@beignet/cli 0.0.33 → 0.0.35

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 (69) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +34 -7
  3. package/dist/config.d.ts +1 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +1 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/inspect.js +54 -17
  8. package/dist/inspect.js.map +1 -1
  9. package/dist/lint.js +10 -3
  10. package/dist/lint.js.map +1 -1
  11. package/dist/make/inbox.d.ts.map +1 -1
  12. package/dist/make/inbox.js +4 -5
  13. package/dist/make/inbox.js.map +1 -1
  14. package/dist/make/payments.d.ts.map +1 -1
  15. package/dist/make/payments.js +5 -7
  16. package/dist/make/payments.js.map +1 -1
  17. package/dist/make/shared.js +1 -1
  18. package/dist/make/shared.js.map +1 -1
  19. package/dist/make/tenancy.d.ts.map +1 -1
  20. package/dist/make/tenancy.js +4 -6
  21. package/dist/make/tenancy.js.map +1 -1
  22. package/dist/make.js +8 -9
  23. package/dist/make.js.map +1 -1
  24. package/dist/provider-add.d.ts.map +1 -1
  25. package/dist/provider-add.js +52 -69
  26. package/dist/provider-add.js.map +1 -1
  27. package/dist/registry-edits.js +2 -1
  28. package/dist/registry-edits.js.map +1 -1
  29. package/dist/schedule.d.ts.map +1 -1
  30. package/dist/schedule.js +1 -1
  31. package/dist/schedule.js.map +1 -1
  32. package/dist/templates/agents.d.ts.map +1 -1
  33. package/dist/templates/agents.js +2 -0
  34. package/dist/templates/agents.js.map +1 -1
  35. package/dist/templates/base.js +3 -3
  36. package/dist/templates/base.js.map +1 -1
  37. package/dist/templates/index.d.ts.map +1 -1
  38. package/dist/templates/index.js +1 -0
  39. package/dist/templates/index.js.map +1 -1
  40. package/dist/templates/server.d.ts +1 -0
  41. package/dist/templates/server.d.ts.map +1 -1
  42. package/dist/templates/server.js +19 -11
  43. package/dist/templates/server.js.map +1 -1
  44. package/dist/templates/testing.d.ts.map +1 -1
  45. package/dist/templates/testing.js +55 -26
  46. package/dist/templates/testing.js.map +1 -1
  47. package/dist/templates/todos.d.ts.map +1 -1
  48. package/dist/templates/todos.js +3 -4
  49. package/dist/templates/todos.js.map +1 -1
  50. package/package.json +3 -2
  51. package/skills/app-structure/SKILL.md +14 -1
  52. package/src/config.ts +2 -0
  53. package/src/inspect.ts +87 -21
  54. package/src/lint.ts +15 -3
  55. package/src/make/inbox.ts +4 -5
  56. package/src/make/payments.ts +4 -8
  57. package/src/make/shared.ts +1 -1
  58. package/src/make/tenancy.ts +4 -10
  59. package/src/make.ts +8 -9
  60. package/src/provider-add.ts +52 -69
  61. package/src/registry-edits.ts +2 -1
  62. package/src/schedule.ts +4 -3
  63. package/src/templates/agents.ts +2 -0
  64. package/src/templates/base.ts +3 -3
  65. package/src/templates/index.ts +1 -0
  66. package/src/templates/server.ts +19 -11
  67. package/src/templates/testing.ts +55 -26
  68. package/src/templates/todos.ts +3 -4
  69. package/src/test-helpers/generated-app.ts +1 -1
@@ -76,9 +76,9 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
76
76
  "flags-openfeature": {
77
77
  name: "flags-openfeature",
78
78
  displayName: "OpenFeature flags",
79
- importName: "openFeatureFlagsProvider",
79
+ importName: "createOpenFeatureFlagsProvider",
80
80
  importFrom: "@beignet/provider-flags-openfeature",
81
- providerEntry: "openFeatureFlagsProvider",
81
+ providerEntry: "createOpenFeatureFlagsProvider()",
82
82
  dependencies: (packageJson) => ({
83
83
  "@beignet/provider-flags-openfeature":
84
84
  beignetDependencyVersion(packageJson),
@@ -97,7 +97,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
97
97
  "",
98
98
  "- Package: `@beignet/provider-flags-openfeature`",
99
99
  "- Peer dependency: `@openfeature/server-sdk`",
100
- "- The preset wires `openFeatureFlagsProvider` in `server/providers.ts` and defers `flags` in `infra/app-ports.ts`.",
100
+ "- The preset wires `createOpenFeatureFlagsProvider()` in `server/providers.ts` and defers `flags` in `infra/app-ports.ts`.",
101
101
  "- Configure the OpenFeature provider globally or replace this preset with `createOpenFeatureFlagsProvider(...)` when the app needs provider-specific setup.",
102
102
  "- Use `ctx.ports.flags` in app code; `ctx.ports.openFeature` is available as an escape hatch.",
103
103
  "",
@@ -111,9 +111,9 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
111
111
  "jobs-bullmq": {
112
112
  name: "jobs-bullmq",
113
113
  displayName: "BullMQ jobs",
114
- importName: "bullMQJobsProvider",
114
+ importName: "createBullMQJobsProvider",
115
115
  importFrom: "@beignet/provider-jobs-bullmq",
116
- providerEntry: "bullMQJobsProvider",
116
+ providerEntry: "createBullMQJobsProvider()",
117
117
  dependencies: (packageJson) => ({
118
118
  "@beignet/provider-jobs-bullmq": beignetDependencyVersion(packageJson),
119
119
  bullmq: externalVersions.bullmq,
@@ -138,7 +138,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
138
138
  "",
139
139
  "- Package: `@beignet/provider-jobs-bullmq`",
140
140
  "- Peer dependency: `bullmq`",
141
- "- The preset wires `bullMQJobsProvider` in `server/providers.ts` and defers `jobs` in `infra/app-ports.ts`.",
141
+ "- The preset wires `createBullMQJobsProvider()` in `server/providers.ts` and defers `jobs` in `infra/app-ports.ts`.",
142
142
  "- Set `BULLMQ_REDIS_URL` before booting the app; the default points at local Redis.",
143
143
  "- Dispatching enqueues jobs, but nothing executes them until a worker entrypoint runs `createBullMQJobWorker(...)` in a worker process. See the jobs docs page.",
144
144
  "- Use `ctx.ports.jobs.dispatch(...)` in app code; `ctx.ports.bullMQJobs` is available as an escape hatch.",
@@ -150,18 +150,14 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
150
150
  "Add a worker entrypoint that runs createBullMQJobWorker(...) in a worker process; dispatched jobs do not execute without one. See the jobs docs page.",
151
151
  "Run beignet providers audit and beignet doctor --strict.",
152
152
  ],
153
- conflictingProviderEntries: [
154
- "inngestJobsProvider",
155
- "createInngestJobsProvider",
156
- "createBullMQJobsProvider",
157
- ],
153
+ conflictingProviderEntries: ["createInngestJobsProvider"],
158
154
  },
159
155
  "jobs-inngest": {
160
156
  name: "jobs-inngest",
161
157
  displayName: "Inngest jobs",
162
- importName: "inngestJobsProvider",
158
+ importName: "createInngestJobsProvider",
163
159
  importFrom: "@beignet/provider-jobs-inngest",
164
- providerEntry: "inngestJobsProvider",
160
+ providerEntry: "createInngestJobsProvider()",
165
161
  dependencies: (packageJson) => ({
166
162
  "@beignet/provider-jobs-inngest": beignetDependencyVersion(packageJson),
167
163
  inngest: externalVersions.inngest,
@@ -184,7 +180,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
184
180
  "",
185
181
  "- Package: `@beignet/provider-jobs-inngest`",
186
182
  "- Peer dependency: `inngest`",
187
- "- The preset wires `inngestJobsProvider` in `server/providers.ts` and defers `jobs` in `infra/app-ports.ts`.",
183
+ "- The preset wires `createInngestJobsProvider()` in `server/providers.ts` and defers `jobs` in `infra/app-ports.ts`.",
188
184
  "- Define jobs with `defineJob(...)` and dispatch them through `ctx.ports.jobs.dispatch(...)`.",
189
185
  "- Execution needs an Inngest serve route built from `createInngestJobFunction(...)` and `serve(...)`. See the jobs docs page.",
190
186
  "- Use `ctx.ports.inngest` only as an escape hatch for Inngest-specific workflow APIs; prefer `ctx.ports.jobs.dispatch(...)` for Beignet jobs.",
@@ -196,17 +192,14 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
196
192
  "Expose an Inngest serve route with createInngestJobFunction(...) so dispatched jobs execute. See the jobs docs page.",
197
193
  "Run beignet providers audit and beignet doctor --strict.",
198
194
  ],
199
- conflictingProviderEntries: [
200
- "bullMQJobsProvider",
201
- "createBullMQJobsProvider",
202
- ],
195
+ conflictingProviderEntries: ["createBullMQJobsProvider"],
203
196
  },
204
197
  "mail-resend": {
205
198
  name: "mail-resend",
206
199
  displayName: "Resend mail",
207
- importName: "resendMailProvider",
200
+ importName: "createResendMailProvider",
208
201
  importFrom: "@beignet/provider-mail-resend",
209
- providerEntry: "resendMailProvider",
202
+ providerEntry: "createResendMailProvider()",
210
203
  dependencies: (packageJson) => ({
211
204
  "@beignet/provider-mail-resend": beignetDependencyVersion(packageJson),
212
205
  resend: externalVersions.resend,
@@ -229,7 +222,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
229
222
  "",
230
223
  "- Package: `@beignet/provider-mail-resend`",
231
224
  "- Peer dependency: `resend`",
232
- "- The preset wires `resendMailProvider` in `server/providers.ts` and defers `mailer` in `infra/app-ports.ts`.",
225
+ "- The preset wires `createResendMailProvider()` in `server/providers.ts` and defers `mailer` in `infra/app-ports.ts`.",
233
226
  "- `.env.example` ships dev placeholders so the app boots; set real `RESEND_API_KEY` and `RESEND_FROM` values before sending mail.",
234
227
  "- Use `ctx.ports.mailer` in app code; `ctx.ports.resend` is available as an escape hatch.",
235
228
  "",
@@ -240,16 +233,16 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
240
233
  "Run beignet providers audit and beignet doctor --strict.",
241
234
  ],
242
235
  conflictingProviderEntries: [
243
- "smtpMailProvider",
236
+ "createSmtpMailProvider",
244
237
  "createMemoryMailerProvider",
245
238
  ],
246
239
  },
247
240
  "mail-smtp": {
248
241
  name: "mail-smtp",
249
242
  displayName: "SMTP mail",
250
- importName: "smtpMailProvider",
243
+ importName: "createSmtpMailProvider",
251
244
  importFrom: "@beignet/provider-mail-smtp",
252
- providerEntry: "smtpMailProvider",
245
+ providerEntry: "createSmtpMailProvider()",
253
246
  dependencies: (packageJson) => ({
254
247
  "@beignet/provider-mail-smtp": beignetDependencyVersion(packageJson),
255
248
  nodemailer: externalVersions.nodemailer,
@@ -275,7 +268,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
275
268
  "",
276
269
  "- Package: `@beignet/provider-mail-smtp`",
277
270
  "- Peer dependency: `nodemailer`",
278
- "- The preset wires `smtpMailProvider` in `server/providers.ts` and defers `mailer` in `infra/app-ports.ts`.",
271
+ "- The preset wires `createSmtpMailProvider()` in `server/providers.ts` and defers `mailer` in `infra/app-ports.ts`.",
279
272
  "- `.env.example` ships dev placeholders so the app boots; set real `MAIL_*` values before sending mail.",
280
273
  "- Use `ctx.ports.mailer` in app code; `ctx.ports.smtp` is available as an escape hatch.",
281
274
  "",
@@ -286,16 +279,16 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
286
279
  "Run beignet providers audit and beignet doctor --strict.",
287
280
  ],
288
281
  conflictingProviderEntries: [
289
- "resendMailProvider",
282
+ "createResendMailProvider",
290
283
  "createMemoryMailerProvider",
291
284
  ],
292
285
  },
293
286
  "payments-stripe": {
294
287
  name: "payments-stripe",
295
288
  displayName: "Stripe payments",
296
- importName: "stripePaymentsProvider",
289
+ importName: "createStripePaymentsProvider",
297
290
  importFrom: "@beignet/provider-payments-stripe",
298
- providerEntry: "stripePaymentsProvider",
291
+ providerEntry: "createStripePaymentsProvider()",
299
292
  dependencies: (packageJson) => ({
300
293
  "@beignet/provider-payments-stripe":
301
294
  beignetDependencyVersion(packageJson),
@@ -324,7 +317,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
324
317
  "",
325
318
  "- Package: `@beignet/provider-payments-stripe`",
326
319
  "- Peer dependency: `stripe`",
327
- "- The preset wires `stripePaymentsProvider` in `server/providers.ts` and defers `payments` in `infra/app-ports.ts`.",
320
+ "- The preset wires `createStripePaymentsProvider()` in `server/providers.ts` and defers `payments` in `infra/app-ports.ts`.",
328
321
  "- `.env.example` ships dev placeholders so the app boots; set real `STRIPE_SECRET_KEY` and `STRIPE_WEBHOOK_SECRET` values before taking payments.",
329
322
  "- Stripe requires a webhook route: run `beignet make payments` for the full billing slice, including a `createPaymentWebhookRoute(...)` route.",
330
323
  "- Use `ctx.ports.payments` in app code; `ctx.ports.stripe` is available as an escape hatch.",
@@ -336,17 +329,14 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
336
329
  "Run beignet make payments for the full billing slice; Stripe needs its webhook route before subscription state can sync.",
337
330
  "Run beignet providers audit and beignet doctor --strict.",
338
331
  ],
339
- conflictingProviderEntries: [
340
- "createMemoryPaymentsProvider",
341
- "createStripePaymentsProvider",
342
- ],
332
+ conflictingProviderEntries: ["createMemoryPaymentsProvider"],
343
333
  },
344
334
  "search-meilisearch": {
345
335
  name: "search-meilisearch",
346
336
  displayName: "Meilisearch search",
347
- importName: "meilisearchSearchProvider",
337
+ importName: "createMeilisearchSearchProvider",
348
338
  importFrom: "@beignet/provider-search-meilisearch",
349
- providerEntry: "meilisearchSearchProvider",
339
+ providerEntry: "createMeilisearchSearchProvider()",
350
340
  dependencies: (packageJson) => ({
351
341
  "@beignet/provider-search-meilisearch":
352
342
  beignetDependencyVersion(packageJson),
@@ -370,7 +360,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
370
360
  "## Meilisearch search",
371
361
  "",
372
362
  "- Package: `@beignet/provider-search-meilisearch`",
373
- "- The preset wires `meilisearchSearchProvider` in `server/providers.ts` and defers `search` in `infra/app-ports.ts`.",
363
+ "- The preset wires `createMeilisearchSearchProvider()` in `server/providers.ts` and defers `search` in `infra/app-ports.ts`.",
374
364
  "- Set `MEILISEARCH_HOST` before booting the app; set `MEILISEARCH_API_KEY` when your Meilisearch server requires one.",
375
365
  "- Use `ctx.ports.search` in app code; `ctx.ports.meilisearch` is available as an escape hatch.",
376
366
  "",
@@ -384,9 +374,9 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
384
374
  sentry: {
385
375
  name: "sentry",
386
376
  displayName: "Sentry error reporting",
387
- importName: "sentryErrorReportingProvider",
377
+ importName: "createSentryErrorReportingProvider",
388
378
  importFrom: "@beignet/provider-error-reporting-sentry",
389
- providerEntry: "sentryErrorReportingProvider",
379
+ providerEntry: "createSentryErrorReportingProvider()",
390
380
  dependencies: (packageJson) => ({
391
381
  "@beignet/provider-error-reporting-sentry":
392
382
  beignetDependencyVersion(packageJson),
@@ -405,7 +395,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
405
395
  "",
406
396
  "- Package: `@beignet/provider-error-reporting-sentry`",
407
397
  "- Peer dependency: `@sentry/node`",
408
- "- The preset wires `sentryErrorReportingProvider` in `server/providers.ts`.",
398
+ "- The preset wires `createSentryErrorReportingProvider()` in `server/providers.ts`.",
409
399
  "- Set `SENTRY_DSN` before deploying when captures should be sent to Sentry.",
410
400
  "- Use `ctx.ports.errorReporter` in app code; `ctx.ports.sentry` is available as an escape hatch.",
411
401
  "",
@@ -419,9 +409,9 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
419
409
  "redis-cache": {
420
410
  name: "redis-cache",
421
411
  displayName: "Redis cache",
422
- importName: "redisCacheProvider",
412
+ importName: "createRedisCacheProvider",
423
413
  importFrom: "@beignet/provider-cache-redis",
424
- providerEntry: "redisCacheProvider",
414
+ providerEntry: "createRedisCacheProvider()",
425
415
  dependencies: (packageJson) => ({
426
416
  "@beignet/provider-cache-redis": beignetDependencyVersion(packageJson),
427
417
  ioredis: externalVersions.ioredis,
@@ -444,7 +434,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
444
434
  "",
445
435
  "- Package: `@beignet/provider-cache-redis`",
446
436
  "- Peer dependency: `ioredis`",
447
- "- The preset wires `redisCacheProvider` in `server/providers.ts` and defers `cache` in `infra/app-ports.ts`.",
437
+ "- The preset wires `createRedisCacheProvider()` in `server/providers.ts` and defers `cache` in `infra/app-ports.ts`.",
448
438
  "- Set `REDIS_URL` before booting the app.",
449
439
  "- Use `ctx.ports.cache` in app code; `ctx.ports.redis` is available as an escape hatch.",
450
440
  "",
@@ -458,9 +448,9 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
458
448
  "event-bus-redis": {
459
449
  name: "event-bus-redis",
460
450
  displayName: "Redis event bus",
461
- importName: "redisEventBusProvider",
451
+ importName: "createRedisEventBusProvider",
462
452
  importFrom: "@beignet/provider-event-bus-redis",
463
- providerEntry: "redisEventBusProvider",
453
+ providerEntry: "createRedisEventBusProvider()",
464
454
  dependencies: (packageJson) => ({
465
455
  "@beignet/provider-event-bus-redis":
466
456
  beignetDependencyVersion(packageJson),
@@ -485,7 +475,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
485
475
  "",
486
476
  "- Package: `@beignet/provider-event-bus-redis`",
487
477
  "- Peer dependency: `ioredis`",
488
- "- The preset wires `redisEventBusProvider` in `server/providers.ts` and defers `eventBus` in `infra/app-ports.ts`.",
478
+ "- The preset wires `createRedisEventBusProvider()` in `server/providers.ts` and defers `eventBus` in `infra/app-ports.ts`.",
489
479
  "- Set `REDIS_EVENT_BUS_URL` before booting the app.",
490
480
  "- Redis Pub/Sub delivery is best-effort and cross-process, but not durable: use outbox/jobs for side effects that need retry, replay, or dead-letter handling.",
491
481
  "- Use `ctx.ports.eventBus` in app code; `ctx.ports.redisEventBus` is available as an escape hatch.",
@@ -497,17 +487,14 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
497
487
  "Keep durable side effects on the outbox/jobs path; Redis Pub/Sub does not replay missed events.",
498
488
  "Run beignet providers audit and beignet doctor --strict.",
499
489
  ],
500
- conflictingProviderEntries: [
501
- "createInMemoryEventBusProvider",
502
- "createRedisEventBusProvider",
503
- ],
490
+ conflictingProviderEntries: ["createMemoryEventBusProvider"],
504
491
  },
505
492
  "redis-locks": {
506
493
  name: "redis-locks",
507
494
  displayName: "Redis locks",
508
- importName: "redisLocksProvider",
495
+ importName: "createRedisLocksProvider",
509
496
  importFrom: "@beignet/provider-locks-redis",
510
- providerEntry: "redisLocksProvider",
497
+ providerEntry: "createRedisLocksProvider()",
511
498
  dependencies: (packageJson) => ({
512
499
  "@beignet/provider-locks-redis": beignetDependencyVersion(packageJson),
513
500
  ioredis: externalVersions.ioredis,
@@ -531,7 +518,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
531
518
  "",
532
519
  "- Package: `@beignet/provider-locks-redis`",
533
520
  "- Peer dependency: `ioredis`",
534
- "- The preset wires `redisLocksProvider` in `server/providers.ts` and defers `locks` in `infra/app-ports.ts`.",
521
+ "- The preset wires `createRedisLocksProvider()` in `server/providers.ts` and defers `locks` in `infra/app-ports.ts`.",
535
522
  "- Set `REDIS_LOCKS_URL` before booting the app.",
536
523
  "- Use `ctx.ports.locks` in app code; `ctx.ports.redisLocks` is available as an escape hatch.",
537
524
  "",
@@ -545,9 +532,9 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
545
532
  "s3-storage": {
546
533
  name: "s3-storage",
547
534
  displayName: "S3-compatible storage",
548
- importName: "s3StorageProvider",
535
+ importName: "createS3StorageProvider",
549
536
  importFrom: "@beignet/provider-storage-s3",
550
- providerEntry: "s3StorageProvider",
537
+ providerEntry: "createS3StorageProvider()",
551
538
  dependencies: (packageJson) => ({
552
539
  "@beignet/provider-storage-s3": beignetDependencyVersion(packageJson),
553
540
  "@aws-sdk/client-s3": externalVersions.awsSdkS3,
@@ -592,7 +579,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
592
579
  "",
593
580
  "- Package: `@beignet/provider-storage-s3`",
594
581
  "- Peer dependencies: `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner`",
595
- "- The preset wires `s3StorageProvider` in `server/providers.ts` and defers `storage` in `infra/app-ports.ts`.",
582
+ "- The preset wires `createS3StorageProvider()` in `server/providers.ts` and defers `storage` in `infra/app-ports.ts`.",
596
583
  "- Set `STORAGE_S3_BUCKET` before booting the app; set endpoint and credentials when using an S3-compatible host outside AWS defaults.",
597
584
  "- Use `ctx.ports.storage` in app code; `ctx.ports.s3Storage` is available as an escape hatch.",
598
585
  "",
@@ -603,18 +590,16 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
603
590
  "Run beignet providers audit and beignet doctor --strict.",
604
591
  ],
605
592
  conflictingProviderEntries: [
606
- "localStorageProvider",
607
593
  "createLocalStorageProvider",
608
- "vercelBlobStorageProvider",
609
594
  "createVercelBlobStorageProvider",
610
595
  ],
611
596
  },
612
597
  "vercel-blob-storage": {
613
598
  name: "vercel-blob-storage",
614
599
  displayName: "Vercel Blob storage",
615
- importName: "vercelBlobStorageProvider",
600
+ importName: "createVercelBlobStorageProvider",
616
601
  importFrom: "@beignet/provider-storage-vercel-blob",
617
- providerEntry: "vercelBlobStorageProvider",
602
+ providerEntry: "createVercelBlobStorageProvider()",
618
603
  dependencies: (packageJson) => ({
619
604
  "@beignet/provider-storage-vercel-blob":
620
605
  beignetDependencyVersion(packageJson),
@@ -640,7 +625,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
640
625
  "",
641
626
  "- Package: `@beignet/provider-storage-vercel-blob`",
642
627
  "- Peer dependency: `@vercel/blob`",
643
- "- The preset wires `vercelBlobStorageProvider` in `server/providers.ts` and defers `storage` in `infra/app-ports.ts`.",
628
+ "- The preset wires `createVercelBlobStorageProvider()` in `server/providers.ts` and defers `storage` in `infra/app-ports.ts`.",
644
629
  "- On Vercel, connect a Blob store to the project and `BLOB_READ_WRITE_TOKEN` is set automatically; set it manually elsewhere.",
645
630
  "- The store is uniform-visibility (`BLOB_ACCESS`, default `private`): Vercel Blob does not report per-object access back, so mixed visibility needs a second store.",
646
631
  "- Use `ctx.ports.storage` in app code; `ctx.ports.vercelBlob` is available as an escape hatch.",
@@ -652,18 +637,16 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
652
637
  "Run beignet providers audit and beignet doctor --strict.",
653
638
  ],
654
639
  conflictingProviderEntries: [
655
- "localStorageProvider",
656
640
  "createLocalStorageProvider",
657
- "s3StorageProvider",
658
641
  "createS3StorageProvider",
659
642
  ],
660
643
  },
661
644
  "upstash-rate-limit": {
662
645
  name: "upstash-rate-limit",
663
646
  displayName: "Upstash rate limit",
664
- importName: "upstashRateLimitProvider",
647
+ importName: "createUpstashRateLimitProvider",
665
648
  importFrom: "@beignet/provider-rate-limit-upstash",
666
- providerEntry: "upstashRateLimitProvider",
649
+ providerEntry: "createUpstashRateLimitProvider()",
667
650
  dependencies: (packageJson) => ({
668
651
  "@beignet/provider-rate-limit-upstash":
669
652
  beignetDependencyVersion(packageJson),
@@ -690,7 +673,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
690
673
  "",
691
674
  "- Package: `@beignet/provider-rate-limit-upstash`",
692
675
  "- Peer dependencies: `@upstash/ratelimit` and `@upstash/redis`",
693
- "- The preset wires `upstashRateLimitProvider` in `server/providers.ts` and defers `rateLimit` in `infra/app-ports.ts`.",
676
+ "- The preset wires `createUpstashRateLimitProvider()` in `server/providers.ts` and defers `rateLimit` in `infra/app-ports.ts`.",
694
677
  "- `.env.example` ships dev placeholders so the app boots; set real `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` values before rate limiting requests.",
695
678
  "- Use `ctx.ports.rateLimit` in app code; `ctx.ports.upstash` is available as an escape hatch.",
696
679
  "",
@@ -710,9 +693,9 @@ const providerPresetSet = new Set<string>(providerPresetChoices);
710
693
  * integration, used to pre-validate preset selections at create time.
711
694
  */
712
695
  const integrationProviderEntries: Record<IntegrationName, string> = {
713
- inngest: "inngestJobsProvider",
714
- resend: "resendMailProvider",
715
- "upstash-rate-limit": "upstashRateLimitProvider",
696
+ inngest: "createInngestJobsProvider",
697
+ resend: "createResendMailProvider",
698
+ "upstash-rate-limit": "createUpstashRateLimitProvider",
716
699
  };
717
700
 
718
701
  /**
@@ -735,7 +718,7 @@ export function assertCompatibleCreatePresets(
735
718
  selectedEntries.set(integrationProviderEntries[integration], integration);
736
719
  }
737
720
  for (const name of presetNames) {
738
- selectedEntries.set(presets[name].providerEntry, name);
721
+ selectedEntries.set(presets[name].importName, name);
739
722
  }
740
723
 
741
724
  for (const name of presetNames) {
@@ -300,7 +300,8 @@ export function appendToOutboxRegistryArray(
300
300
  }
301
301
 
302
302
  function entryIdentifier(entry: string): string {
303
- return entry.replace(/^\.{3}/, "").trim();
303
+ const normalized = entry.replace(/^\.{3}/, "").trim();
304
+ return /^[A-Za-z_$][\w$]*/.exec(normalized)?.[0] ?? normalized;
304
305
  }
305
306
 
306
307
  function appendEntry(
package/src/schedule.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import type { ProviderInstrumentationPort } from "@beignet/core/providers";
3
4
  import type {
4
5
  ScheduleDef,
5
- ScheduleInstrumentation,
6
6
  ScheduleRunOptions,
7
7
  StandardSchema,
8
8
  } from "@beignet/core/schedules";
@@ -54,7 +54,8 @@ type ScheduleContext = {
54
54
  traceId?: string;
55
55
  ports?: {
56
56
  logger?: ScheduleLogger;
57
- devtools?: ScheduleInstrumentation;
57
+ devtools?: ProviderInstrumentationPort;
58
+ instrumentation?: ProviderInstrumentationPort;
58
59
  };
59
60
  };
60
61
 
@@ -107,7 +108,7 @@ export async function runAppSchedule(
107
108
  );
108
109
  const runner = createInlineScheduleRunner<unknown>({
109
110
  ctx,
110
- instrumentation: scheduleContext.ports?.devtools,
111
+ instrumentation: scheduleContext.ports,
111
112
  instrumentationContext: {
112
113
  requestId: scheduleContext.requestId,
113
114
  traceId: scheduleContext.traceId,
@@ -48,6 +48,8 @@ is a silent failure — the file exists but never runs:
48
48
 
49
49
  - Feature route groups in \`features/<feature>/routes.ts\` must be composed in
50
50
  \`server/routes.ts\` via \`defineRoutes([...])\`.
51
+ - Feature route files import the app-bound \`defineRouteGroup\` from
52
+ \`lib/routes.ts\`; that builder owns the shared \`AppContext\` binding.
51
53
  - Schedules must be added to the \`schedules\` array in \`server/schedules.ts\`.
52
54
  - Tasks must be added to \`defineTasks([...])\` in \`server/tasks.ts\`.
53
55
  - Outbox events and jobs must be registered in \`defineOutboxRegistry({...})\`
@@ -390,7 +390,7 @@ export function integrationsDoc(ctx: TemplateContext): string {
390
390
  "- Package: `@beignet/core` (`@beignet/core/events`, `@beignet/core/jobs`)",
391
391
  "- Package: `@beignet/provider-jobs-inngest`",
392
392
  "- Peer dependency: `inngest`",
393
- "- The starter wires `inngestJobsProvider` in `server/providers.ts`.",
393
+ "- The starter wires `createInngestJobsProvider()` in `server/providers.ts`.",
394
394
  "- Define jobs with `defineJob(...)` and dispatch them through `ctx.ports.jobs.dispatch(...)`.",
395
395
  "- Use `ctx.ports.inngest` only as an escape hatch for Inngest-specific workflow APIs; prefer `ctx.ports.jobs.dispatch(...)` for Beignet jobs.",
396
396
  "",
@@ -403,7 +403,7 @@ export function integrationsDoc(ctx: TemplateContext): string {
403
403
  "",
404
404
  "- Package: `@beignet/provider-mail-resend`",
405
405
  "- Peer dependency: `resend`",
406
- "- The starter wires `resendMailProvider` in `server/providers.ts`.",
406
+ "- The starter wires `createResendMailProvider()` in `server/providers.ts`.",
407
407
  "- `.env.example` ships dev placeholders so the app boots; set real `RESEND_API_KEY` and `RESEND_FROM` values before sending mail.",
408
408
  "",
409
409
  );
@@ -415,7 +415,7 @@ export function integrationsDoc(ctx: TemplateContext): string {
415
415
  "",
416
416
  "- Package: `@beignet/provider-rate-limit-upstash`",
417
417
  "- Peer dependencies: `@upstash/ratelimit` and `@upstash/redis`",
418
- "- The starter wires `upstashRateLimitProvider` in `server/providers.ts` and adds `rateLimit: RateLimitPort` to `AppPorts`.",
418
+ "- The starter wires `createUpstashRateLimitProvider()` in `server/providers.ts` and adds `rateLimit: RateLimitPort` to `AppPorts`.",
419
419
  "- `.env.example` ships dev placeholders so the app boots; set real `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` values before rate limiting requests.",
420
420
  "",
421
421
  );
@@ -136,6 +136,7 @@ export function getTemplateFiles(ctx: TemplateContext): TemplateFile[] {
136
136
  { path: "lib/tenant.ts", content: serverFiles.tenantHelper },
137
137
  ...testSupportTemplateFiles,
138
138
  { path: "lib/use-case.ts", content: serverFiles.useCaseBuilder },
139
+ { path: "lib/routes.ts", content: serverFiles.routesBuilder },
139
140
  { path: "server/index.ts", content: server(ctx) },
140
141
  { path: "server/context.ts", content: serverFiles.serverContext },
141
142
  { path: "server/routes.ts", content: serverFiles.serverRoutes },
@@ -10,17 +10,17 @@ export function serverProviders(ctx: TemplateContext): string {
10
10
  const imports = [
11
11
  'import "@beignet/core/server-only";',
12
12
  'import { createDevtoolsProvider } from "@beignet/devtools";',
13
- 'import { createAuthBetterAuthProvider } from "@beignet/provider-auth-better-auth";',
13
+ 'import { createBetterAuthProvider } from "@beignet/provider-auth-better-auth";',
14
14
  `import { ${db.providerFactory} } from "@beignet/provider-db-drizzle/${db.subpath}";`,
15
15
  hasIntegration(ctx, "inngest")
16
- ? 'import { inngestJobsProvider } from "@beignet/provider-jobs-inngest";'
16
+ ? 'import { createInngestJobsProvider } from "@beignet/provider-jobs-inngest";'
17
17
  : undefined,
18
- 'import { pinoLoggerProvider } from "@beignet/provider-logger-pino";',
18
+ 'import { createPinoLoggerProvider } from "@beignet/provider-logger-pino";',
19
19
  hasIntegration(ctx, "resend")
20
- ? 'import { resendMailProvider } from "@beignet/provider-mail-resend";'
20
+ ? 'import { createResendMailProvider } from "@beignet/provider-mail-resend";'
21
21
  : undefined,
22
22
  hasIntegration(ctx, "upstash-rate-limit")
23
- ? 'import { upstashRateLimitProvider } from "@beignet/provider-rate-limit-upstash";'
23
+ ? 'import { createUpstashRateLimitProvider } from "@beignet/provider-rate-limit-upstash";'
24
24
  : undefined,
25
25
  'import * as schema from "@/infra/db/schema";',
26
26
  'import { starterDatabaseProvider } from "@/infra/db/provider";',
@@ -30,14 +30,16 @@ export function serverProviders(ctx: TemplateContext): string {
30
30
 
31
31
  const entries = [
32
32
  "\tcreateDevtoolsProvider(),",
33
- "\tcreateAuthBetterAuthProvider<AuthUser, AuthSessionMetadata>(auth),",
34
- "\tpinoLoggerProvider,",
33
+ "\tcreateBetterAuthProvider<AuthUser, AuthSessionMetadata>(auth),",
34
+ "\tcreatePinoLoggerProvider(),",
35
35
  `\t${db.providerInstance},`,
36
36
  "\tstarterDatabaseProvider,",
37
- hasIntegration(ctx, "inngest") ? "\tinngestJobsProvider," : undefined,
38
- hasIntegration(ctx, "resend") ? "\tresendMailProvider," : undefined,
37
+ hasIntegration(ctx, "inngest")
38
+ ? "\tcreateInngestJobsProvider(),"
39
+ : undefined,
40
+ hasIntegration(ctx, "resend") ? "\tcreateResendMailProvider()," : undefined,
39
41
  hasIntegration(ctx, "upstash-rate-limit")
40
- ? "\tupstashRateLimitProvider,"
42
+ ? "\tcreateUpstashRateLimitProvider(),"
41
43
  : undefined,
42
44
  ].filter((entry): entry is string => Boolean(entry));
43
45
 
@@ -394,6 +396,12 @@ import { createUseCase } from "@beignet/core/application";
394
396
  import type { AppContext } from "@/app-context";
395
397
 
396
398
  export const useCase = createUseCase<AppContext>();
399
+ `,
400
+ routesBuilder: `import "@beignet/core/server-only";
401
+ import { createRoutes } from "@beignet/core/server";
402
+ import type { AppContext } from "@/app-context";
403
+
404
+ export const { defineRoute, defineRouteGroup } = createRoutes<AppContext>();
397
405
  `,
398
406
  authHelpers: `import type { AppContext } from "@/app-context";
399
407
  import type { AuthSession, AuthUser } from "@/ports/auth";
@@ -445,7 +453,7 @@ export type AuthPort = BeignetAuthPort<
445
453
  >;
446
454
  `,
447
455
  serverRoutes: `import "@beignet/core/server-only";
448
- import { contractsFromRoutes, defineRoutes } from "@beignet/next";
456
+ import { contractsFromRoutes, defineRoutes } from "@beignet/core/server";
449
457
  import type { AppContext } from "@/app-context";
450
458
  import { todoRoutes } from "@/features/todos/routes";
451
459
 
@@ -406,6 +406,7 @@ export const testRunnerTemplateFile: TemplateFile = {
406
406
  content: `import { spawnSync } from "node:child_process";
407
407
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
408
408
  import path from "node:path";
409
+ import { pathToFileURL } from "node:url";
409
410
 
410
411
  const defaultTestRoots = [
411
412
  "app",
@@ -428,11 +429,18 @@ const ignoredDirectories = new Set([
428
429
  ]);
429
430
  const testFilePattern = /\\.(test|spec)\\.[cm]?[tj]sx?$/;
430
431
 
432
+ const configFileNames = [
433
+ "beignet.config.ts",
434
+ "beignet.config.json",
435
+ "beignet.config.mjs",
436
+ "beignet.config.js",
437
+ ];
438
+
431
439
  const explicitArgs = process.argv.slice(2);
432
440
  const testArgs =
433
441
  explicitArgs.length > 0
434
442
  ? resolveTestArgs(process.cwd(), explicitArgs)
435
- : discoverTestFiles(process.cwd());
443
+ : await discoverTestFiles(process.cwd());
436
444
 
437
445
  if (testArgs.length === 0) {
438
446
  console.log("No test files found.");
@@ -456,9 +464,9 @@ if (result.error) {
456
464
 
457
465
  process.exit(result.status ?? 1);
458
466
 
459
- function discoverTestFiles(cwd: string): string[] {
467
+ async function discoverTestFiles(cwd: string): Promise<string[]> {
460
468
  const files: string[] = [];
461
- for (const root of testRoots(cwd)) {
469
+ for (const root of await testRoots(cwd)) {
462
470
  const rootPath = path.join(cwd, root);
463
471
  if (existsSync(rootPath)) {
464
472
  walk(rootPath, cwd, files);
@@ -494,32 +502,53 @@ function resolveTestArgs(cwd: string, args: string[]): string[] {
494
502
  return Array.from(new Set(resolved));
495
503
  }
496
504
 
497
- function testRoots(cwd: string): string[] {
498
- return Array.from(new Set([...defaultTestRoots, ...configuredRoots(cwd)]));
505
+ async function testRoots(cwd: string): Promise<string[]> {
506
+ return Array.from(
507
+ new Set([...defaultTestRoots, ...(await configuredRoots(cwd))]),
508
+ );
499
509
  }
500
510
 
501
- function configuredRoots(cwd: string): string[] {
502
- const configPath = path.join(cwd, "beignet.config.json");
503
- if (!existsSync(configPath)) return [];
504
- try {
505
- const config = JSON.parse(readFileSync(configPath, "utf8")) as {
506
- paths?: Record<string, string>;
507
- };
508
- const paths = config.paths ?? {};
509
- return [
510
- paths.tests,
511
- paths.features,
512
- paths.routes ? path.dirname(paths.routes) : undefined,
513
- paths.server ? path.dirname(paths.server) : undefined,
514
- paths.infrastructurePorts
515
- ? path.dirname(paths.infrastructurePorts)
516
- : undefined,
517
- paths.ports ? path.dirname(paths.ports) : undefined,
518
- paths.useCaseBuilder ? path.dirname(paths.useCaseBuilder) : undefined,
519
- ].filter((root): root is string => Boolean(root) && root !== ".");
520
- } catch {
521
- return [];
511
+ async function configuredRoots(cwd: string): Promise<string[]> {
512
+ const configFiles = configFileNames.filter((file) =>
513
+ existsSync(path.join(cwd, file)),
514
+ );
515
+ if (configFiles.length === 0) return [];
516
+ if (configFiles.length > 1) {
517
+ throw new Error(
518
+ "Multiple Beignet config files found: " +
519
+ configFiles.join(", ") +
520
+ ". Keep only one config file.",
521
+ );
522
522
  }
523
+
524
+ const configFile = configFiles[0];
525
+ const configPath = path.join(cwd, configFile);
526
+ const config = configFile.endsWith(".json")
527
+ ? JSON.parse(readFileSync(configPath, "utf8"))
528
+ : ((await import(pathToFileURL(configPath).href)) as { default?: unknown })
529
+ .default;
530
+ if (!isObjectRecord(config)) {
531
+ throw new Error(configFile + " must export a Beignet config object.");
532
+ }
533
+ const paths = isObjectRecord(config.paths) ? config.paths : {};
534
+ return [
535
+ paths.tests,
536
+ paths.features,
537
+ parentPath(paths.routes),
538
+ parentPath(paths.server),
539
+ parentPath(paths.infrastructurePorts),
540
+ parentPath(paths.ports),
541
+ parentPath(paths.useCaseBuilder),
542
+ parentPath(paths.routesBuilder),
543
+ ].filter((root): root is string => Boolean(root) && root !== ".");
544
+ }
545
+
546
+ function parentPath(value: unknown): string | undefined {
547
+ return typeof value === "string" ? path.dirname(value) : undefined;
548
+ }
549
+
550
+ function isObjectRecord(value: unknown): value is Record<string, unknown> {
551
+ return value !== null && typeof value === "object" && !Array.isArray(value);
523
552
  }
524
553
 
525
554
  function walk(directory: string, cwd: string, files: string[]): void {