@beignet/cli 0.0.38 → 0.0.40
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 +18 -0
- package/README.md +154 -102
- 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 +4 -2
- 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/framework.d.ts +3 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +6 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -47
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +4 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +174 -34
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/make/inbox.js +3 -3
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +5 -3
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +10 -3
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +7 -8
- package/dist/make/shared.js.map +1 -1
- package/dist/make.d.ts +3 -2
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +549 -186
- 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/provider-add.d.ts +21 -3
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +101 -65
- package/dist/provider-add.js.map +1 -1
- package/dist/task.js +1 -1
- 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 +5 -5
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +17 -13
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shadcn.js +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/package.json +2 -2
- package/skills/app-structure/SKILL.md +29 -9
- package/src/choices.ts +38 -88
- package/src/config.ts +7 -3
- package/src/create-prompts.ts +20 -21
- package/src/create.ts +54 -36
- package/src/framework.ts +13 -0
- package/src/index.ts +193 -65
- package/src/inspect.ts +290 -39
- package/src/lib.ts +1 -1
- package/src/make/inbox.ts +3 -3
- package/src/make/payments.ts +10 -3
- package/src/make/shared.ts +16 -10
- package/src/make.ts +720 -176
- package/src/mcp.ts +20 -13
- package/src/provider-add.ts +159 -79
- package/src/task.ts +1 -1
- package/src/templates/base.ts +16 -16
- package/src/templates/index.ts +6 -6
- package/src/templates/server.ts +17 -13
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +10 -10
- package/src/test-helpers/generated-app.ts +2 -1
package/src/mcp.ts
CHANGED
|
@@ -78,13 +78,13 @@ const makeInputSchema = {
|
|
|
78
78
|
.string()
|
|
79
79
|
.optional()
|
|
80
80
|
.describe(
|
|
81
|
-
"Artifact name. Required except for inbox, outbox, payments, and tenancy. Feature-owned artifacts use
|
|
81
|
+
"Artifact name. Required except for inbox, outbox, payments, and tenancy. Feature-owned artifacts use identities such as posts.backfill.",
|
|
82
82
|
),
|
|
83
83
|
with: z
|
|
84
84
|
.array(z.enum(makeFeatureAddonChoices))
|
|
85
85
|
.optional()
|
|
86
86
|
.describe(
|
|
87
|
-
"feature only: add feature-owned artifacts such as policy,
|
|
87
|
+
"feature only: add feature-owned artifacts such as policy, factory, seed, event, listener, job, notification, schedule, task, ui, and upload.",
|
|
88
88
|
),
|
|
89
89
|
recipe: z
|
|
90
90
|
.enum(makeFeatureRecipeChoices)
|
|
@@ -94,7 +94,7 @@ const makeInputSchema = {
|
|
|
94
94
|
.string()
|
|
95
95
|
.optional()
|
|
96
96
|
.describe(
|
|
97
|
-
"listener only (required): event to listen to, for example posts
|
|
97
|
+
"listener only (required): event to listen to, for example posts.published.",
|
|
98
98
|
),
|
|
99
99
|
cron: z
|
|
100
100
|
.string()
|
|
@@ -108,13 +108,19 @@ const makeInputSchema = {
|
|
|
108
108
|
.boolean()
|
|
109
109
|
.optional()
|
|
110
110
|
.describe("schedule only: generate a Next.js cron route for the schedule."),
|
|
111
|
-
|
|
111
|
+
ui: z
|
|
112
|
+
.boolean()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe(
|
|
115
|
+
"upload only: generate a typed React upload client and component.",
|
|
116
|
+
),
|
|
117
|
+
authorization: z
|
|
112
118
|
.boolean()
|
|
113
119
|
.optional()
|
|
114
120
|
.describe(
|
|
115
121
|
"resource only: generate policy metadata and use-case authorization checks for mutating routes.",
|
|
116
122
|
),
|
|
117
|
-
|
|
123
|
+
tenantScoped: z
|
|
118
124
|
.boolean()
|
|
119
125
|
.optional()
|
|
120
126
|
.describe(
|
|
@@ -163,8 +169,9 @@ type MakeToolInput = {
|
|
|
163
169
|
cron?: string;
|
|
164
170
|
timezone?: string;
|
|
165
171
|
route?: boolean;
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
ui?: boolean;
|
|
173
|
+
authorization?: boolean;
|
|
174
|
+
tenantScoped?: boolean;
|
|
168
175
|
events?: boolean;
|
|
169
176
|
softDelete?: boolean;
|
|
170
177
|
force?: boolean;
|
|
@@ -268,7 +275,7 @@ async function runMakeTool(
|
|
|
268
275
|
case "listener":
|
|
269
276
|
if (!input.event) {
|
|
270
277
|
throw new Error(
|
|
271
|
-
"make listener requires an event, for example event: posts
|
|
278
|
+
"make listener requires an event, for example event: posts.published.",
|
|
272
279
|
);
|
|
273
280
|
}
|
|
274
281
|
return makeListener({ ...base, event: input.event });
|
|
@@ -281,8 +288,8 @@ async function runMakeTool(
|
|
|
281
288
|
case "resource":
|
|
282
289
|
return makeResource({
|
|
283
290
|
...base,
|
|
284
|
-
|
|
285
|
-
|
|
291
|
+
authorization: input.authorization,
|
|
292
|
+
tenantScoped: input.tenantScoped,
|
|
286
293
|
events: input.events,
|
|
287
294
|
softDelete: input.softDelete,
|
|
288
295
|
});
|
|
@@ -300,7 +307,7 @@ async function runMakeTool(
|
|
|
300
307
|
case "test":
|
|
301
308
|
return makeTest(base);
|
|
302
309
|
case "upload":
|
|
303
|
-
return makeUpload(base);
|
|
310
|
+
return makeUpload({ ...base, ui: input.ui });
|
|
304
311
|
case "use-case":
|
|
305
312
|
return makeUseCase(base);
|
|
306
313
|
}
|
|
@@ -418,7 +425,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
418
425
|
"make",
|
|
419
426
|
{
|
|
420
427
|
description:
|
|
421
|
-
"Generate Beignet app files for one artifact: adapter, contract, event, factory, feature, inbox, job, listener, notification, outbox, payments, policy, port, resource, schedule, seed, task, tenancy, test, upload, or use-case. Generators auto-register artifacts, so doctor stays clean. Options by artifact: feature accepts with (addons); resource accepts
|
|
428
|
+
"Generate Beignet app files for one artifact: adapter, contract, event, factory, feature, inbox, job, listener, notification, outbox, payments, policy, port, resource, schedule, seed, task, tenancy, test, upload, or use-case. Generators auto-register artifacts, so doctor stays clean. Options by artifact: feature accepts with (addons); resource accepts authorization, tenantScoped, events, softDelete; listener requires event; schedule accepts cron, timezone, route; upload accepts ui. All artifacts accept force and dryRun. Returns created, updated, and skipped files as JSON.",
|
|
422
429
|
inputSchema: makeInputSchema,
|
|
423
430
|
},
|
|
424
431
|
async (input) =>
|
|
@@ -430,7 +437,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
430
437
|
server.registerTool(
|
|
431
438
|
"provider_add",
|
|
432
439
|
{
|
|
433
|
-
description: `Add a Beignet provider setup preset to this app. Updates dependencies, provider registration, app port wiring, .env.example, and docs/
|
|
440
|
+
description: `Add a Beignet provider setup preset to this app. Updates dependencies, provider registration, app port wiring, .env.example, and docs/providers.md. Presets: ${providerPresetDescription}. Supports dryRun.`,
|
|
434
441
|
inputSchema: providerAddInputSchema,
|
|
435
442
|
},
|
|
436
443
|
async (input) =>
|
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",
|
|
@@ -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": {
|
|
@@ -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
|
},
|
|
@@ -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
|
},
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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
|
},
|
|
@@ -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",
|
|
@@ -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": {
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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",
|
|
@@ -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
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
|
"",
|
|
@@ -1144,13 +1237,13 @@ function sortRecord(record: Record<string, string>): Record<string, string> {
|
|
|
1144
1237
|
}
|
|
1145
1238
|
|
|
1146
1239
|
async function planFiles(
|
|
1147
|
-
|
|
1148
|
-
|
|
1240
|
+
files: ProviderPlanFile[],
|
|
1241
|
+
readSource: ProviderSourceReader,
|
|
1149
1242
|
): Promise<PlannedFile[]> {
|
|
1150
1243
|
const planned: PlannedFile[] = [];
|
|
1151
1244
|
|
|
1152
1245
|
for (const file of files) {
|
|
1153
|
-
const existing = await
|
|
1246
|
+
const existing = await readSource(file.path);
|
|
1154
1247
|
planned.push({
|
|
1155
1248
|
...file,
|
|
1156
1249
|
result:
|
|
@@ -1176,19 +1269,6 @@ async function writePlannedFile(
|
|
|
1176
1269
|
await writeFile(destination, file.content);
|
|
1177
1270
|
}
|
|
1178
1271
|
|
|
1179
|
-
async function readRequiredAppFile(
|
|
1180
|
-
targetDir: string,
|
|
1181
|
-
filePath: string,
|
|
1182
|
-
): Promise<string> {
|
|
1183
|
-
const content = await readOptionalAppFile(targetDir, filePath);
|
|
1184
|
-
if (content === undefined) {
|
|
1185
|
-
throw new Error(
|
|
1186
|
-
`Could not find ${filePath}. Run this command in a Beignet app.`,
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
return content;
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
1272
|
async function readOptionalAppFile(
|
|
1193
1273
|
targetDir: string,
|
|
1194
1274
|
filePath: string,
|
package/src/task.ts
CHANGED
|
@@ -142,7 +142,7 @@ async function loadTaskModule(
|
|
|
142
142
|
await stat(absolutePath);
|
|
143
143
|
} catch {
|
|
144
144
|
throw new Error(
|
|
145
|
-
`Could not find app task registry at ${modulePath}. Create it with beignet make task <feature
|
|
145
|
+
`Could not find app task registry at ${modulePath}. Create it with beignet make task <feature.name> or configure paths.tasks in beignet.config.ts.`,
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
148
|
|