@beignet/cli 0.0.37 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +138 -101
- package/dist/choices.d.ts +17 -21
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +21 -67
- package/dist/choices.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/create-prompts.d.ts +5 -6
- package/dist/create-prompts.d.ts.map +1 -1
- package/dist/create-prompts.js +15 -15
- package/dist/create-prompts.js.map +1 -1
- package/dist/create.d.ts +11 -2
- package/dist/create.d.ts.map +1 -1
- package/dist/create.js +38 -28
- package/dist/create.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -47
- package/dist/index.js.map +1 -1
- package/dist/inspect.js +69 -16
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +27 -6
- package/dist/lint.js.map +1 -1
- package/dist/make/inbox.js +10 -10
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts +2 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +18 -14
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +11 -4
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +56 -45
- package/dist/make/shared.js.map +1 -1
- package/dist/make/tenancy.js +8 -8
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.d.ts +3 -2
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +481 -200
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +15 -11
- package/dist/mcp.js.map +1 -1
- package/dist/operational-error-reporting.d.ts +15 -0
- package/dist/operational-error-reporting.d.ts.map +1 -0
- package/dist/operational-error-reporting.js +42 -0
- package/dist/operational-error-reporting.js.map +1 -0
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +73 -0
- package/dist/outbox.js.map +1 -1
- package/dist/provider-add.d.ts +21 -3
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +117 -81
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.js +3 -3
- package/dist/provider-audit.js.map +1 -1
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +28 -1
- package/dist/schedule.js.map +1 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +38 -5
- package/dist/task.js.map +1 -1
- package/dist/templates/base.d.ts +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +15 -15
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts +2 -2
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +7 -7
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +22 -18
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +4 -4
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +6 -6
- package/dist/templates/shared.js.map +1 -1
- package/dist/templates/testing.js +1 -1
- package/dist/templates/todos.js +6 -6
- package/package.json +4 -4
- package/skills/app-structure/SKILL.md +13 -8
- package/src/choices.ts +38 -88
- package/src/config.ts +2 -2
- package/src/create-prompts.ts +20 -21
- package/src/create.ts +54 -36
- package/src/index.ts +193 -65
- package/src/inspect.ts +116 -18
- package/src/lib.ts +1 -1
- package/src/lint.ts +35 -4
- package/src/make/inbox.ts +10 -10
- package/src/make/payments.ts +26 -18
- package/src/make/shared.ts +72 -50
- package/src/make/tenancy.ts +8 -8
- package/src/make.ts +607 -191
- package/src/mcp.ts +20 -13
- package/src/operational-error-reporting.ts +60 -0
- package/src/outbox.ts +77 -0
- package/src/provider-add.ts +179 -95
- package/src/provider-audit.ts +3 -3
- package/src/schedule.ts +32 -1
- package/src/task.ts +42 -5
- package/src/templates/base.ts +16 -16
- package/src/templates/index.ts +8 -8
- package/src/templates/server.ts +22 -18
- package/src/templates/shared.ts +10 -10
- package/src/templates/testing.ts +1 -1
- package/src/templates/todos.ts +6 -6
- package/src/test-helpers/generated-app.ts +1 -0
package/src/provider-add.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {
|
|
4
|
-
type IntegrationName,
|
|
5
4
|
type ProviderPresetName,
|
|
6
5
|
providerPresetChoices,
|
|
6
|
+
type StarterProviderName,
|
|
7
7
|
} from "./choices.js";
|
|
8
8
|
import {
|
|
9
9
|
directoryPath,
|
|
@@ -72,6 +72,18 @@ type PlannedFile = {
|
|
|
72
72
|
result: "created" | "updated" | "skipped";
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
+
type ProviderPlanFile = {
|
|
76
|
+
path: string;
|
|
77
|
+
content: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type ProviderSourceReader = (filePath: string) => Promise<string | undefined>;
|
|
81
|
+
|
|
82
|
+
export type CreateProviderPresetPlan = {
|
|
83
|
+
files: ProviderPlanFile[];
|
|
84
|
+
presets: AddProviderPresetResult[];
|
|
85
|
+
};
|
|
86
|
+
|
|
75
87
|
const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
76
88
|
"flags-openfeature": {
|
|
77
89
|
name: "flags-openfeature",
|
|
@@ -97,7 +109,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
97
109
|
"",
|
|
98
110
|
"- Package: `@beignet/provider-flags-openfeature`",
|
|
99
111
|
"- Peer dependency: `@openfeature/server-sdk`",
|
|
100
|
-
"- The preset wires `createOpenFeatureFlagsProvider()` in `server/providers.ts` and defers `flags` in `infra/
|
|
112
|
+
"- The preset wires `createOpenFeatureFlagsProvider()` in `server/providers.ts` and defers `flags` in `infra/port-wiring.ts`.",
|
|
101
113
|
"- Configure the OpenFeature provider globally or replace this preset with `createOpenFeatureFlagsProvider(...)` when the app needs provider-specific setup.",
|
|
102
114
|
"- Use `ctx.ports.flags` in app code; `ctx.ports.openFeature` is available as an escape hatch.",
|
|
103
115
|
"",
|
|
@@ -105,7 +117,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
105
117
|
nextSteps: [
|
|
106
118
|
"Run your package manager install command.",
|
|
107
119
|
"Configure an OpenFeature provider before relying on non-default flag values.",
|
|
108
|
-
"Run beignet
|
|
120
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
109
121
|
],
|
|
110
122
|
},
|
|
111
123
|
"jobs-bullmq": {
|
|
@@ -138,7 +150,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
138
150
|
"",
|
|
139
151
|
"- Package: `@beignet/provider-jobs-bullmq`",
|
|
140
152
|
"- Peer dependency: `bullmq`",
|
|
141
|
-
"- The preset wires `createBullMQJobsProvider()` in `server/providers.ts` and defers `jobs` in `infra/
|
|
153
|
+
"- The preset wires `createBullMQJobsProvider()` in `server/providers.ts` and defers `jobs` in `infra/port-wiring.ts`.",
|
|
142
154
|
"- Set `BULLMQ_REDIS_URL` before booting the app; the default points at local Redis.",
|
|
143
155
|
"- Dispatching enqueues jobs, but nothing executes them until a worker entrypoint runs `createBullMQJobWorker(...)` in a worker process. See the jobs docs page.",
|
|
144
156
|
"- Use `ctx.ports.jobs.dispatch(...)` in app code; `ctx.ports.bullMQJobs` is available as an escape hatch.",
|
|
@@ -148,7 +160,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
148
160
|
"Run your package manager install command.",
|
|
149
161
|
"Set BULLMQ_REDIS_URL in .env.local or your deployment environment.",
|
|
150
162
|
"Add a worker entrypoint that runs createBullMQJobWorker(...) in a worker process; dispatched jobs do not execute without one. See the jobs docs page.",
|
|
151
|
-
"Run beignet
|
|
163
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
152
164
|
],
|
|
153
165
|
conflictingProviderEntries: ["createInngestJobsProvider"],
|
|
154
166
|
},
|
|
@@ -180,7 +192,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
180
192
|
"",
|
|
181
193
|
"- Package: `@beignet/provider-jobs-inngest`",
|
|
182
194
|
"- Peer dependency: `inngest`",
|
|
183
|
-
"- The preset wires `createInngestJobsProvider()` in `server/providers.ts` and defers `jobs` in `infra/
|
|
195
|
+
"- The preset wires `createInngestJobsProvider()` in `server/providers.ts` and defers `jobs` in `infra/port-wiring.ts`.",
|
|
184
196
|
"- Define jobs with `defineJob(...)` and dispatch them through `ctx.ports.jobs.dispatch(...)`.",
|
|
185
197
|
"- Execution needs an Inngest serve route built from `createInngestJobFunction(...)` and `serve(...)`. See the jobs docs page.",
|
|
186
198
|
"- Use `ctx.ports.inngest` only as an escape hatch for Inngest-specific workflow APIs; prefer `ctx.ports.jobs.dispatch(...)` for Beignet jobs.",
|
|
@@ -190,7 +202,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
190
202
|
"Run your package manager install command.",
|
|
191
203
|
"Set INNGEST_APP_NAME and, for Inngest cloud, INNGEST_EVENT_KEY in .env.local or your deployment environment.",
|
|
192
204
|
"Expose an Inngest serve route with createInngestJobFunction(...) so dispatched jobs execute. See the jobs docs page.",
|
|
193
|
-
"Run beignet
|
|
205
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
194
206
|
],
|
|
195
207
|
conflictingProviderEntries: ["createBullMQJobsProvider"],
|
|
196
208
|
},
|
|
@@ -222,7 +234,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
222
234
|
"",
|
|
223
235
|
"- Package: `@beignet/provider-mail-resend`",
|
|
224
236
|
"- Peer dependency: `resend`",
|
|
225
|
-
"- The preset wires `createResendMailProvider()` in `server/providers.ts` and defers `mailer` in `infra/
|
|
237
|
+
"- The preset wires `createResendMailProvider()` in `server/providers.ts` and defers `mailer` in `infra/port-wiring.ts`.",
|
|
226
238
|
"- `.env.example` ships dev placeholders so the app boots; set real `RESEND_API_KEY` and `RESEND_FROM` values before sending mail.",
|
|
227
239
|
"- Use `ctx.ports.mailer` in app code; `ctx.ports.resend` is available as an escape hatch.",
|
|
228
240
|
"",
|
|
@@ -230,7 +242,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
230
242
|
nextSteps: [
|
|
231
243
|
"Run your package manager install command.",
|
|
232
244
|
"Set RESEND_API_KEY and RESEND_FROM in .env.local or your deployment environment.",
|
|
233
|
-
"Run beignet
|
|
245
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
234
246
|
],
|
|
235
247
|
conflictingProviderEntries: [
|
|
236
248
|
"createSmtpMailProvider",
|
|
@@ -268,7 +280,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
268
280
|
"",
|
|
269
281
|
"- Package: `@beignet/provider-mail-smtp`",
|
|
270
282
|
"- Peer dependency: `nodemailer`",
|
|
271
|
-
"- The preset wires `createSmtpMailProvider()` in `server/providers.ts` and defers `mailer` in `infra/
|
|
283
|
+
"- The preset wires `createSmtpMailProvider()` in `server/providers.ts` and defers `mailer` in `infra/port-wiring.ts`.",
|
|
272
284
|
"- `.env.example` ships dev placeholders so the app boots; set real `MAIL_*` values before sending mail.",
|
|
273
285
|
"- Use `ctx.ports.mailer` in app code; `ctx.ports.smtp` is available as an escape hatch.",
|
|
274
286
|
"",
|
|
@@ -276,7 +288,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
276
288
|
nextSteps: [
|
|
277
289
|
"Run your package manager install command.",
|
|
278
290
|
"Set MAIL_HOST, MAIL_PORT, MAIL_USER, MAIL_PASS, and MAIL_FROM in .env.local or your deployment environment.",
|
|
279
|
-
"Run beignet
|
|
291
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
280
292
|
],
|
|
281
293
|
conflictingProviderEntries: [
|
|
282
294
|
"createResendMailProvider",
|
|
@@ -317,7 +329,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
317
329
|
"",
|
|
318
330
|
"- Package: `@beignet/provider-payments-stripe`",
|
|
319
331
|
"- Peer dependency: `stripe`",
|
|
320
|
-
"- The preset wires `createStripePaymentsProvider()` in `server/providers.ts` and defers `payments` in `infra/
|
|
332
|
+
"- The preset wires `createStripePaymentsProvider()` in `server/providers.ts` and defers `payments` in `infra/port-wiring.ts`.",
|
|
321
333
|
"- `.env.example` ships dev placeholders so the app boots; set real `STRIPE_SECRET_KEY` and `STRIPE_WEBHOOK_SECRET` values before taking payments.",
|
|
322
334
|
"- Stripe requires a webhook route: run `beignet make payments` for the full billing slice, including a `createPaymentWebhookRoute(...)` route.",
|
|
323
335
|
"- Use `ctx.ports.payments` in app code; `ctx.ports.stripe` is available as an escape hatch.",
|
|
@@ -327,7 +339,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
327
339
|
"Run your package manager install command.",
|
|
328
340
|
"Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in .env.local or your deployment environment.",
|
|
329
341
|
"Run beignet make payments for the full billing slice; Stripe needs its webhook route before subscription state can sync.",
|
|
330
|
-
"Run beignet
|
|
342
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
331
343
|
],
|
|
332
344
|
conflictingProviderEntries: ["createMemoryPaymentsProvider"],
|
|
333
345
|
},
|
|
@@ -360,7 +372,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
360
372
|
"## Meilisearch search",
|
|
361
373
|
"",
|
|
362
374
|
"- Package: `@beignet/provider-search-meilisearch`",
|
|
363
|
-
"- The preset wires `createMeilisearchSearchProvider()` in `server/providers.ts` and defers `search` in `infra/
|
|
375
|
+
"- The preset wires `createMeilisearchSearchProvider()` in `server/providers.ts` and defers `search` in `infra/port-wiring.ts`.",
|
|
364
376
|
"- Set `MEILISEARCH_HOST` before booting the app; set `MEILISEARCH_API_KEY` when your Meilisearch server requires one.",
|
|
365
377
|
"- Use `ctx.ports.search` in app code; `ctx.ports.meilisearch` is available as an escape hatch.",
|
|
366
378
|
"",
|
|
@@ -368,11 +380,11 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
368
380
|
nextSteps: [
|
|
369
381
|
"Run your package manager install command.",
|
|
370
382
|
"Set MEILISEARCH_HOST and any needed MEILISEARCH_* values in .env.local or your deployment environment.",
|
|
371
|
-
"Run beignet
|
|
383
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
372
384
|
],
|
|
373
385
|
},
|
|
374
|
-
sentry: {
|
|
375
|
-
name: "sentry",
|
|
386
|
+
"error-reporting-sentry": {
|
|
387
|
+
name: "error-reporting-sentry",
|
|
376
388
|
displayName: "Sentry error reporting",
|
|
377
389
|
importName: "createSentryErrorReportingProvider",
|
|
378
390
|
importFrom: "@beignet/provider-error-reporting-sentry",
|
|
@@ -403,11 +415,11 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
403
415
|
nextSteps: [
|
|
404
416
|
"Run your package manager install command.",
|
|
405
417
|
"Set SENTRY_DSN in .env.local or your deployment environment.",
|
|
406
|
-
"Run beignet
|
|
418
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
407
419
|
],
|
|
408
420
|
},
|
|
409
|
-
"redis
|
|
410
|
-
name: "redis
|
|
421
|
+
"cache-redis": {
|
|
422
|
+
name: "cache-redis",
|
|
411
423
|
displayName: "Redis cache",
|
|
412
424
|
importName: "createRedisCacheProvider",
|
|
413
425
|
importFrom: "@beignet/provider-cache-redis",
|
|
@@ -434,7 +446,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
434
446
|
"",
|
|
435
447
|
"- Package: `@beignet/provider-cache-redis`",
|
|
436
448
|
"- Peer dependency: `ioredis`",
|
|
437
|
-
"- The preset wires `createRedisCacheProvider()` in `server/providers.ts` and defers `cache` in `infra/
|
|
449
|
+
"- The preset wires `createRedisCacheProvider()` in `server/providers.ts` and defers `cache` in `infra/port-wiring.ts`.",
|
|
438
450
|
"- Set `REDIS_URL` before booting the app.",
|
|
439
451
|
"- Use `ctx.ports.cache` in app code; `ctx.ports.redis` is available as an escape hatch.",
|
|
440
452
|
"",
|
|
@@ -442,7 +454,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
442
454
|
nextSteps: [
|
|
443
455
|
"Run your package manager install command.",
|
|
444
456
|
"Set REDIS_URL in .env.local or your deployment environment.",
|
|
445
|
-
"Run beignet
|
|
457
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
446
458
|
],
|
|
447
459
|
},
|
|
448
460
|
"event-bus-redis": {
|
|
@@ -475,7 +487,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
475
487
|
"",
|
|
476
488
|
"- Package: `@beignet/provider-event-bus-redis`",
|
|
477
489
|
"- Peer dependency: `ioredis`",
|
|
478
|
-
"- The preset wires `createRedisEventBusProvider()` in `server/providers.ts` and defers `eventBus` in `infra/
|
|
490
|
+
"- The preset wires `createRedisEventBusProvider()` in `server/providers.ts` and defers `eventBus` in `infra/port-wiring.ts`.",
|
|
479
491
|
"- Set `REDIS_EVENT_BUS_URL` before booting the app.",
|
|
480
492
|
"- 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.",
|
|
481
493
|
"- Use `ctx.ports.eventBus` in app code; `ctx.ports.redisEventBus` is available as an escape hatch.",
|
|
@@ -485,12 +497,12 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
485
497
|
"Run your package manager install command.",
|
|
486
498
|
"Set REDIS_EVENT_BUS_URL in .env.local or your deployment environment.",
|
|
487
499
|
"Keep durable side effects on the outbox/jobs path; Redis Pub/Sub does not replay missed events.",
|
|
488
|
-
"Run beignet
|
|
500
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
489
501
|
],
|
|
490
502
|
conflictingProviderEntries: ["createMemoryEventBusProvider"],
|
|
491
503
|
},
|
|
492
|
-
"redis
|
|
493
|
-
name: "redis
|
|
504
|
+
"locks-redis": {
|
|
505
|
+
name: "locks-redis",
|
|
494
506
|
displayName: "Redis locks",
|
|
495
507
|
importName: "createRedisLocksProvider",
|
|
496
508
|
importFrom: "@beignet/provider-locks-redis",
|
|
@@ -518,7 +530,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
518
530
|
"",
|
|
519
531
|
"- Package: `@beignet/provider-locks-redis`",
|
|
520
532
|
"- Peer dependency: `ioredis`",
|
|
521
|
-
"- The preset wires `createRedisLocksProvider()` in `server/providers.ts` and defers `locks` in `infra/
|
|
533
|
+
"- The preset wires `createRedisLocksProvider()` in `server/providers.ts` and defers `locks` in `infra/port-wiring.ts`.",
|
|
522
534
|
"- Set `REDIS_LOCKS_URL` before booting the app.",
|
|
523
535
|
"- Use `ctx.ports.locks` in app code; `ctx.ports.redisLocks` is available as an escape hatch.",
|
|
524
536
|
"",
|
|
@@ -526,11 +538,11 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
526
538
|
nextSteps: [
|
|
527
539
|
"Run your package manager install command.",
|
|
528
540
|
"Set REDIS_LOCKS_URL in .env.local or your deployment environment.",
|
|
529
|
-
"Run beignet
|
|
541
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
530
542
|
],
|
|
531
543
|
},
|
|
532
|
-
"s3
|
|
533
|
-
name: "s3
|
|
544
|
+
"storage-s3": {
|
|
545
|
+
name: "storage-s3",
|
|
534
546
|
displayName: "S3-compatible storage",
|
|
535
547
|
importName: "createS3StorageProvider",
|
|
536
548
|
importFrom: "@beignet/provider-storage-s3",
|
|
@@ -579,7 +591,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
579
591
|
"",
|
|
580
592
|
"- Package: `@beignet/provider-storage-s3`",
|
|
581
593
|
"- Peer dependencies: `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner`",
|
|
582
|
-
"- The preset wires `createS3StorageProvider()` in `server/providers.ts` and defers `storage` in `infra/
|
|
594
|
+
"- The preset wires `createS3StorageProvider()` in `server/providers.ts` and defers `storage` in `infra/port-wiring.ts`.",
|
|
583
595
|
"- Set `STORAGE_S3_BUCKET` before booting the app; set endpoint and credentials when using an S3-compatible host outside AWS defaults.",
|
|
584
596
|
"- Use `ctx.ports.storage` in app code; `ctx.ports.s3Storage` is available as an escape hatch.",
|
|
585
597
|
"",
|
|
@@ -587,15 +599,15 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
587
599
|
nextSteps: [
|
|
588
600
|
"Run your package manager install command.",
|
|
589
601
|
"Set STORAGE_S3_BUCKET and any needed STORAGE_S3_* credentials in .env.local or your deployment environment.",
|
|
590
|
-
"Run beignet
|
|
602
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
591
603
|
],
|
|
592
604
|
conflictingProviderEntries: [
|
|
593
605
|
"createLocalStorageProvider",
|
|
594
606
|
"createVercelBlobStorageProvider",
|
|
595
607
|
],
|
|
596
608
|
},
|
|
597
|
-
"vercel-blob
|
|
598
|
-
name: "vercel-blob
|
|
609
|
+
"storage-vercel-blob": {
|
|
610
|
+
name: "storage-vercel-blob",
|
|
599
611
|
displayName: "Vercel Blob storage",
|
|
600
612
|
importName: "createVercelBlobStorageProvider",
|
|
601
613
|
importFrom: "@beignet/provider-storage-vercel-blob",
|
|
@@ -625,7 +637,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
625
637
|
"",
|
|
626
638
|
"- Package: `@beignet/provider-storage-vercel-blob`",
|
|
627
639
|
"- Peer dependency: `@vercel/blob`",
|
|
628
|
-
"- The preset wires `createVercelBlobStorageProvider()` in `server/providers.ts` and defers `storage` in `infra/
|
|
640
|
+
"- The preset wires `createVercelBlobStorageProvider()` in `server/providers.ts` and defers `storage` in `infra/port-wiring.ts`.",
|
|
629
641
|
"- On Vercel, connect a Blob store to the project and `BLOB_READ_WRITE_TOKEN` is set automatically; set it manually elsewhere.",
|
|
630
642
|
"- 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.",
|
|
631
643
|
"- Use `ctx.ports.storage` in app code; `ctx.ports.vercelBlob` is available as an escape hatch.",
|
|
@@ -634,15 +646,15 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
634
646
|
nextSteps: [
|
|
635
647
|
"Run your package manager install command.",
|
|
636
648
|
"Connect a Vercel Blob store to the project (or set BLOB_READ_WRITE_TOKEN manually).",
|
|
637
|
-
"Run beignet
|
|
649
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
638
650
|
],
|
|
639
651
|
conflictingProviderEntries: [
|
|
640
652
|
"createLocalStorageProvider",
|
|
641
653
|
"createS3StorageProvider",
|
|
642
654
|
],
|
|
643
655
|
},
|
|
644
|
-
"
|
|
645
|
-
name: "
|
|
656
|
+
"rate-limit-upstash": {
|
|
657
|
+
name: "rate-limit-upstash",
|
|
646
658
|
displayName: "Upstash rate limit",
|
|
647
659
|
importName: "createUpstashRateLimitProvider",
|
|
648
660
|
importFrom: "@beignet/provider-rate-limit-upstash",
|
|
@@ -673,7 +685,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
673
685
|
"",
|
|
674
686
|
"- Package: `@beignet/provider-rate-limit-upstash`",
|
|
675
687
|
"- Peer dependencies: `@upstash/ratelimit` and `@upstash/redis`",
|
|
676
|
-
"- The preset wires `createUpstashRateLimitProvider()` in `server/providers.ts` and defers `rateLimit` in `infra/
|
|
688
|
+
"- The preset wires `createUpstashRateLimitProvider()` in `server/providers.ts` and defers `rateLimit` in `infra/port-wiring.ts`.",
|
|
677
689
|
"- `.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.",
|
|
678
690
|
"- Use `ctx.ports.rateLimit` in app code; `ctx.ports.upstash` is available as an escape hatch.",
|
|
679
691
|
"",
|
|
@@ -681,7 +693,7 @@ const presets: Record<ProviderPresetName, ProviderPreset> = {
|
|
|
681
693
|
nextSteps: [
|
|
682
694
|
"Run your package manager install command.",
|
|
683
695
|
"Set UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN in .env.local or your deployment environment.",
|
|
684
|
-
"Run beignet
|
|
696
|
+
"Run beignet provider audit and beignet doctor --strict.",
|
|
685
697
|
],
|
|
686
698
|
},
|
|
687
699
|
};
|
|
@@ -690,12 +702,12 @@ const providerPresetSet = new Set<string>(providerPresetChoices);
|
|
|
690
702
|
|
|
691
703
|
/**
|
|
692
704
|
* Provider entries the starter template wires for each create-time
|
|
693
|
-
*
|
|
705
|
+
* provider, used to pre-validate preset selections at create time.
|
|
694
706
|
*/
|
|
695
|
-
const
|
|
696
|
-
inngest: "createInngestJobsProvider",
|
|
697
|
-
resend: "createResendMailProvider",
|
|
698
|
-
"
|
|
707
|
+
const starterProviderEntries: Record<StarterProviderName, string> = {
|
|
708
|
+
"jobs-inngest": "createInngestJobsProvider",
|
|
709
|
+
"mail-resend": "createResendMailProvider",
|
|
710
|
+
"rate-limit-upstash": "createUpstashRateLimitProvider",
|
|
699
711
|
};
|
|
700
712
|
|
|
701
713
|
/**
|
|
@@ -707,15 +719,18 @@ export function providerPresetNeedsEnv(name: ProviderPresetName): boolean {
|
|
|
707
719
|
|
|
708
720
|
/**
|
|
709
721
|
* Fail fast when a create-time selection combines presets or template
|
|
710
|
-
*
|
|
722
|
+
* starter providers that fill the same app port, before any files are written.
|
|
711
723
|
*/
|
|
712
724
|
export function assertCompatibleCreatePresets(
|
|
713
725
|
presetNames: readonly ProviderPresetName[],
|
|
714
|
-
|
|
726
|
+
starterProviders: readonly StarterProviderName[],
|
|
715
727
|
): void {
|
|
716
728
|
const selectedEntries = new Map<string, string>();
|
|
717
|
-
for (const
|
|
718
|
-
selectedEntries.set(
|
|
729
|
+
for (const starterProvider of starterProviders) {
|
|
730
|
+
selectedEntries.set(
|
|
731
|
+
starterProviderEntries[starterProvider],
|
|
732
|
+
starterProvider,
|
|
733
|
+
);
|
|
719
734
|
}
|
|
720
735
|
for (const name of presetNames) {
|
|
721
736
|
selectedEntries.set(presets[name].importName, name);
|
|
@@ -726,7 +741,7 @@ export function assertCompatibleCreatePresets(
|
|
|
726
741
|
const other = selectedEntries.get(conflict);
|
|
727
742
|
if (other !== undefined && other !== name) {
|
|
728
743
|
throw new Error(
|
|
729
|
-
`--
|
|
744
|
+
`--providers selections "${name}" and "${other}" fill the same app port. Pick one of them.`,
|
|
730
745
|
);
|
|
731
746
|
}
|
|
732
747
|
}
|
|
@@ -747,18 +762,85 @@ export async function addProviderPreset(
|
|
|
747
762
|
const preset = presets[options.name as ProviderPresetName];
|
|
748
763
|
const targetDir = path.resolve(options.cwd ?? process.cwd());
|
|
749
764
|
const config = await loadBeignetConfig(targetDir);
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
const
|
|
765
|
+
const readSource: ProviderSourceReader = (filePath) =>
|
|
766
|
+
readOptionalAppFile(targetDir, filePath);
|
|
767
|
+
const filesToPlan = await providerPresetFiles(preset, config, readSource);
|
|
768
|
+
const plannedFiles = await planFiles(filesToPlan, readSource);
|
|
753
769
|
|
|
754
|
-
|
|
770
|
+
if (!options.dryRun) {
|
|
771
|
+
for (const file of plannedFiles) {
|
|
772
|
+
await writePlannedFile(targetDir, file);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
return providerPresetResult(
|
|
777
|
+
preset,
|
|
755
778
|
targetDir,
|
|
779
|
+
Boolean(options.dryRun),
|
|
780
|
+
plannedFiles,
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Apply provider presets to an in-memory fresh scaffold. Each preset reads the
|
|
786
|
+
* previous preset's output so dry runs model the real sequential create path.
|
|
787
|
+
*/
|
|
788
|
+
export async function planProviderPresetsForCreate(options: {
|
|
789
|
+
targetDir: string;
|
|
790
|
+
files: readonly ProviderPlanFile[];
|
|
791
|
+
names: readonly ProviderPresetName[];
|
|
792
|
+
}): Promise<CreateProviderPresetPlan> {
|
|
793
|
+
const sources = new Map(
|
|
794
|
+
options.files.map((file) => [file.path, file.content] as const),
|
|
795
|
+
);
|
|
796
|
+
const config = await loadBeignetConfig(options.targetDir);
|
|
797
|
+
const presetResults: AddProviderPresetResult[] = [];
|
|
798
|
+
const readSource: ProviderSourceReader = async (filePath) => {
|
|
799
|
+
const generatedSource = sources.get(filePath);
|
|
800
|
+
return generatedSource ?? readOptionalAppFile(options.targetDir, filePath);
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
for (const name of options.names) {
|
|
804
|
+
const preset = presets[name];
|
|
805
|
+
const filesToPlan = await providerPresetFiles(preset, config, readSource);
|
|
806
|
+
const plannedFiles = await planFiles(filesToPlan, readSource);
|
|
807
|
+
|
|
808
|
+
for (const file of plannedFiles) {
|
|
809
|
+
sources.set(file.path, file.content);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
presetResults.push(
|
|
813
|
+
providerPresetResult(preset, options.targetDir, true, plannedFiles),
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return {
|
|
818
|
+
files: [...sources].map(([filePath, content]) => ({
|
|
819
|
+
path: filePath,
|
|
820
|
+
content,
|
|
821
|
+
})),
|
|
822
|
+
presets: presetResults,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
async function providerPresetFiles(
|
|
827
|
+
preset: ProviderPreset,
|
|
828
|
+
config: ResolvedBeignetConfig,
|
|
829
|
+
readSource: ProviderSourceReader,
|
|
830
|
+
): Promise<ProviderPlanFile[]> {
|
|
831
|
+
const packageJsonPath = "package.json";
|
|
832
|
+
const providersPath = serverProvidersPath(config);
|
|
833
|
+
const providersDocsPath = "docs/providers.md";
|
|
834
|
+
const packageJsonSource = await readRequiredSource(
|
|
835
|
+
readSource,
|
|
756
836
|
packageJsonPath,
|
|
757
837
|
);
|
|
758
838
|
const packageJson = parsePackageJson(packageJsonSource);
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
839
|
+
const envExampleContent = updateEnvExample(
|
|
840
|
+
await readSource(".env.example"),
|
|
841
|
+
preset,
|
|
842
|
+
);
|
|
843
|
+
const files = [
|
|
762
844
|
{
|
|
763
845
|
path: packageJsonPath,
|
|
764
846
|
content: updatePackageJson(packageJsonSource, packageJson, preset),
|
|
@@ -766,53 +848,51 @@ export async function addProviderPreset(
|
|
|
766
848
|
{
|
|
767
849
|
path: providersPath,
|
|
768
850
|
content: updateServerProviders(
|
|
769
|
-
await
|
|
851
|
+
await readRequiredSource(readSource, providersPath),
|
|
770
852
|
preset,
|
|
771
853
|
),
|
|
772
854
|
},
|
|
773
855
|
{
|
|
774
856
|
path: config.paths.ports,
|
|
775
857
|
content: updatePortsFile(
|
|
776
|
-
await
|
|
858
|
+
await readRequiredSource(readSource, config.paths.ports),
|
|
777
859
|
preset.appPort,
|
|
778
860
|
),
|
|
779
861
|
},
|
|
780
862
|
{
|
|
781
|
-
path: config.paths.
|
|
863
|
+
path: config.paths.portWiring,
|
|
782
864
|
content: updateInfrastructurePortsFile(
|
|
783
|
-
await
|
|
865
|
+
await readRequiredSource(readSource, config.paths.portWiring),
|
|
784
866
|
preset.appPort.key,
|
|
785
867
|
),
|
|
786
868
|
},
|
|
787
869
|
{
|
|
788
|
-
path:
|
|
789
|
-
content:
|
|
790
|
-
await readOptionalAppFile(targetDir, integrationsPath),
|
|
791
|
-
preset,
|
|
792
|
-
),
|
|
870
|
+
path: providersDocsPath,
|
|
871
|
+
content: updateProvidersDoc(await readSource(providersDocsPath), preset),
|
|
793
872
|
},
|
|
794
873
|
];
|
|
795
874
|
|
|
796
875
|
if (envExampleContent !== undefined) {
|
|
797
|
-
|
|
876
|
+
files.splice(files.length - 1, 0, {
|
|
798
877
|
path: ".env.example",
|
|
799
878
|
content: envExampleContent,
|
|
800
879
|
});
|
|
801
880
|
}
|
|
802
881
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (!options.dryRun) {
|
|
806
|
-
for (const file of plannedFiles) {
|
|
807
|
-
await writePlannedFile(targetDir, file);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
882
|
+
return files;
|
|
883
|
+
}
|
|
810
884
|
|
|
885
|
+
function providerPresetResult(
|
|
886
|
+
preset: ProviderPreset,
|
|
887
|
+
targetDir: string,
|
|
888
|
+
dryRun: boolean,
|
|
889
|
+
plannedFiles: readonly PlannedFile[],
|
|
890
|
+
): AddProviderPresetResult {
|
|
811
891
|
return {
|
|
812
892
|
schemaVersion: 1,
|
|
813
893
|
name: preset.name,
|
|
814
894
|
targetDir,
|
|
815
|
-
dryRun
|
|
895
|
+
dryRun,
|
|
816
896
|
files: plannedFiles.map((file) => file.path),
|
|
817
897
|
createdFiles: plannedFiles
|
|
818
898
|
.filter((file) => file.result === "created")
|
|
@@ -827,6 +907,19 @@ export async function addProviderPreset(
|
|
|
827
907
|
};
|
|
828
908
|
}
|
|
829
909
|
|
|
910
|
+
async function readRequiredSource(
|
|
911
|
+
readSource: ProviderSourceReader,
|
|
912
|
+
filePath: string,
|
|
913
|
+
): Promise<string> {
|
|
914
|
+
const content = await readSource(filePath);
|
|
915
|
+
if (content === undefined) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
`Could not find ${filePath}. Run this command in a Beignet app.`,
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
return content;
|
|
921
|
+
}
|
|
922
|
+
|
|
830
923
|
function beignetDependencyVersion(packageJson: PackageJson): string {
|
|
831
924
|
return (
|
|
832
925
|
packageJson.dependencies?.["@beignet/core"] ??
|
|
@@ -959,14 +1052,14 @@ function updateEnvExample(
|
|
|
959
1052
|
return appendSection(current, preset.envExample);
|
|
960
1053
|
}
|
|
961
1054
|
|
|
962
|
-
function
|
|
1055
|
+
function updateProvidersDoc(
|
|
963
1056
|
source: string | undefined,
|
|
964
1057
|
preset: ProviderPreset,
|
|
965
1058
|
): string {
|
|
966
1059
|
const current =
|
|
967
1060
|
source ??
|
|
968
1061
|
[
|
|
969
|
-
"#
|
|
1062
|
+
"# Providers",
|
|
970
1063
|
"",
|
|
971
1064
|
"This file tracks app-level provider setup added after the initial scaffold.",
|
|
972
1065
|
"",
|
|
@@ -1056,13 +1149,17 @@ function ensureTypeProperty(
|
|
|
1056
1149
|
function ensureDeferredPort(source: string, portKey: string): string {
|
|
1057
1150
|
const match = /\bdeferred\s*:\s*\[/.exec(source);
|
|
1058
1151
|
if (!match) {
|
|
1059
|
-
throw new Error(
|
|
1152
|
+
throw new Error(
|
|
1153
|
+
"Could not find `deferred: [...]` in infra/port-wiring.ts.",
|
|
1154
|
+
);
|
|
1060
1155
|
}
|
|
1061
1156
|
|
|
1062
1157
|
const openBracket = source.indexOf("[", match.index);
|
|
1063
1158
|
const closeBracket = matchingDelimiterIndex(source, openBracket, "[", "]");
|
|
1064
1159
|
if (closeBracket === -1) {
|
|
1065
|
-
throw new Error(
|
|
1160
|
+
throw new Error(
|
|
1161
|
+
"Could not parse `deferred` array in infra/port-wiring.ts.",
|
|
1162
|
+
);
|
|
1066
1163
|
}
|
|
1067
1164
|
|
|
1068
1165
|
const arrayText = source.slice(openBracket, closeBracket + 1);
|
|
@@ -1140,13 +1237,13 @@ function sortRecord(record: Record<string, string>): Record<string, string> {
|
|
|
1140
1237
|
}
|
|
1141
1238
|
|
|
1142
1239
|
async function planFiles(
|
|
1143
|
-
|
|
1144
|
-
|
|
1240
|
+
files: ProviderPlanFile[],
|
|
1241
|
+
readSource: ProviderSourceReader,
|
|
1145
1242
|
): Promise<PlannedFile[]> {
|
|
1146
1243
|
const planned: PlannedFile[] = [];
|
|
1147
1244
|
|
|
1148
1245
|
for (const file of files) {
|
|
1149
|
-
const existing = await
|
|
1246
|
+
const existing = await readSource(file.path);
|
|
1150
1247
|
planned.push({
|
|
1151
1248
|
...file,
|
|
1152
1249
|
result:
|
|
@@ -1172,19 +1269,6 @@ async function writePlannedFile(
|
|
|
1172
1269
|
await writeFile(destination, file.content);
|
|
1173
1270
|
}
|
|
1174
1271
|
|
|
1175
|
-
async function readRequiredAppFile(
|
|
1176
|
-
targetDir: string,
|
|
1177
|
-
filePath: string,
|
|
1178
|
-
): Promise<string> {
|
|
1179
|
-
const content = await readOptionalAppFile(targetDir, filePath);
|
|
1180
|
-
if (content === undefined) {
|
|
1181
|
-
throw new Error(
|
|
1182
|
-
`Could not find ${filePath}. Run this command in a Beignet app.`,
|
|
1183
|
-
);
|
|
1184
|
-
}
|
|
1185
|
-
return content;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
1272
|
async function readOptionalAppFile(
|
|
1189
1273
|
targetDir: string,
|
|
1190
1274
|
filePath: string,
|
package/src/provider-audit.ts
CHANGED
|
@@ -149,7 +149,7 @@ export async function auditProviders(
|
|
|
149
149
|
? await readCachedSource(targetDir, config.paths.ports, sourceCache)
|
|
150
150
|
: "";
|
|
151
151
|
const infrastructurePath = directoryPath(
|
|
152
|
-
path.dirname(config.paths.
|
|
152
|
+
path.dirname(config.paths.portWiring),
|
|
153
153
|
);
|
|
154
154
|
const schemaDir = `${infrastructurePath}/db/schema`;
|
|
155
155
|
|
|
@@ -1679,13 +1679,13 @@ function isInfraOrServerFile(
|
|
|
1679
1679
|
): boolean {
|
|
1680
1680
|
const serverDir = directoryPath(path.dirname(config.paths.server));
|
|
1681
1681
|
const infrastructureDir = directoryPath(
|
|
1682
|
-
path.dirname(config.paths.
|
|
1682
|
+
path.dirname(config.paths.portWiring),
|
|
1683
1683
|
);
|
|
1684
1684
|
|
|
1685
1685
|
return (
|
|
1686
1686
|
file === config.paths.server ||
|
|
1687
1687
|
file.startsWith(`${serverDir}/`) ||
|
|
1688
|
-
file === config.paths.
|
|
1688
|
+
file === config.paths.portWiring ||
|
|
1689
1689
|
file.startsWith(`${infrastructureDir}/`)
|
|
1690
1690
|
);
|
|
1691
1691
|
}
|