betterstart-cli 0.0.19 → 0.0.21
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/dist/assets/adapters/next/plugins/portfolio/schemas/portfolio.json +1 -1
- package/dist/assets/adapters/next/templates/init/data/navigation.ts +2 -1
- package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/custom/media-field-empty-state.tsx +14 -42
- package/dist/assets/shared-assets/react-admin/custom/media-field.tsx +0 -1
- package/dist/assets/shared-assets/react-admin/schema.json +2 -64
- package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +18 -2
- package/dist/cli.js +71 -40
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AdminNavigationItem } from '@admin/types/navigation'
|
|
2
2
|
import { House, ImagePlay } from 'lucide-react'
|
|
3
|
+
import { contactNav } from './navigation/contact'
|
|
3
4
|
import { menusNav } from './navigation/menus'
|
|
4
5
|
import { portfolioNav } from './navigation/portfolio'
|
|
5
6
|
import { postsNav } from './navigation/posts'
|
|
@@ -30,7 +31,7 @@ function comparePositions(a?: number, b?: number): number {
|
|
|
30
31
|
return 0
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
const combinedNavigation = [...coreNav, ...menusNav, ...portfolioNav, ...postsNav]
|
|
34
|
+
const combinedNavigation = [...coreNav, ...contactNav, ...menusNav, ...portfolioNav, ...postsNav]
|
|
34
35
|
|
|
35
36
|
const dashboard = combinedNavigation.find((item) => item.href === '/admin')
|
|
36
37
|
const others = combinedNavigation.filter((item) => item.href !== '/admin')
|
|
@@ -195,7 +195,7 @@ export function GalleryField({
|
|
|
195
195
|
<CarouselContent className="-ml-3">
|
|
196
196
|
{canAdd && !disabled ? (
|
|
197
197
|
<CarouselItem className="basis-[168px] pl-3 sm:basis-[184px]">
|
|
198
|
-
<Card className="h-full">
|
|
198
|
+
<Card className="h-full" onClick={() => setOpen(true)}>
|
|
199
199
|
<CardContent className="p-2 flex flex-col cursor-pointer">
|
|
200
200
|
<div className="flex size-full bg-background border border-border rounded-sm aspect-square overflow-hidden text-muted-foreground hover:text-foreground transition-colors items-center flex-col justify-center gap-1">
|
|
201
201
|
<Plus className="size-6" strokeWidth={1.5} />
|
|
@@ -6,39 +6,27 @@ import { Card, CardContent } from '@admin/components/ui/card'
|
|
|
6
6
|
import type { AdminMedia } from '@admin/types'
|
|
7
7
|
import { cn } from '@admin/utils/shared/cn'
|
|
8
8
|
import { ImageIcon } from 'lucide-react'
|
|
9
|
-
import type * as React from 'react'
|
|
10
9
|
|
|
11
10
|
interface MediaFieldEmptyStateProps {
|
|
12
11
|
accept?: string
|
|
13
|
-
addButtonLabel?: string
|
|
14
12
|
className?: string
|
|
15
13
|
disabled?: boolean
|
|
16
|
-
isRemovable?: boolean
|
|
17
14
|
mode?: 'single' | 'multi'
|
|
18
15
|
open: boolean
|
|
19
16
|
onOpenChange: (open: boolean) => void
|
|
20
|
-
onRemove?: React.MouseEventHandler<HTMLButtonElement>
|
|
21
17
|
onSelect: (selected: AdminMedia | AdminMedia[]) => void
|
|
22
|
-
showAddButton?: boolean
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
export function MediaFieldEmptyState({
|
|
26
21
|
accept,
|
|
27
|
-
addButtonLabel = 'Add Image',
|
|
28
22
|
className,
|
|
29
23
|
disabled,
|
|
30
|
-
isRemovable = false,
|
|
31
24
|
mode = 'single',
|
|
32
25
|
open,
|
|
33
26
|
onOpenChange,
|
|
34
|
-
|
|
35
|
-
onSelect,
|
|
36
|
-
showAddButton = false
|
|
27
|
+
onSelect
|
|
37
28
|
}: MediaFieldEmptyStateProps) {
|
|
38
|
-
|
|
39
|
-
const shouldShowActions = !disabled && (shouldShowAddButton || onRemove)
|
|
40
|
-
|
|
41
|
-
function handleAddImage(e: React.MouseEvent) {
|
|
29
|
+
function handleAddImage(e: React.MouseEvent<HTMLButtonElement>) {
|
|
42
30
|
e.preventDefault()
|
|
43
31
|
e.stopPropagation()
|
|
44
32
|
onOpenChange(true)
|
|
@@ -54,36 +42,20 @@ export function MediaFieldEmptyState({
|
|
|
54
42
|
>
|
|
55
43
|
<CardContent className="relative p-0 justify-center items-center flex flex-col h-40 hover:bg-muted/50 transition-colors duration-200">
|
|
56
44
|
<ImageIcon
|
|
57
|
-
className=
|
|
58
|
-
'group-hover:opacity-0 group-focus-within:opacity-0': shouldShowActions
|
|
59
|
-
})}
|
|
45
|
+
className="text-muted-foreground transition-opacity group-hover:opacity-0 group-focus-within:opacity-0"
|
|
60
46
|
strokeWidth={1}
|
|
61
47
|
/>
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</Button>
|
|
74
|
-
) : null}
|
|
75
|
-
{onRemove ? (
|
|
76
|
-
<Button
|
|
77
|
-
type="button"
|
|
78
|
-
data-media-field-action="true"
|
|
79
|
-
variant="default"
|
|
80
|
-
onClick={onRemove}
|
|
81
|
-
>
|
|
82
|
-
Remove
|
|
83
|
-
</Button>
|
|
84
|
-
) : null}
|
|
85
|
-
</div>
|
|
86
|
-
)}
|
|
48
|
+
<div className="absolute inset-0 flex items-center justify-center gap-1 opacity-0 transition-opacity duration-200 ease-out group-hover:opacity-100 group-focus-within:opacity-100">
|
|
49
|
+
<Button
|
|
50
|
+
type="button"
|
|
51
|
+
data-media-field-action="true"
|
|
52
|
+
variant="outline"
|
|
53
|
+
onClick={handleAddImage}
|
|
54
|
+
className="border-primary/10"
|
|
55
|
+
>
|
|
56
|
+
Add Image
|
|
57
|
+
</Button>
|
|
58
|
+
</div>
|
|
87
59
|
</CardContent>
|
|
88
60
|
</Card>
|
|
89
61
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"title": "BetterStart Schema Definition",
|
|
5
5
|
"description": "JSON Schema for validating custom schema files (courses.json, testimonials.json, instructors.json, events.json, etc.)",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"required": ["
|
|
7
|
+
"required": ["name", "label"],
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"properties": {
|
|
10
10
|
"$schema": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"type": {
|
|
32
32
|
"type": "string",
|
|
33
|
-
"description": "Schema type: 'entity' for CRUD list, 'single' for singleton record (site settings, about page), or 'form' for public submission forms",
|
|
33
|
+
"description": "Schema type: 'entity' for CRUD list (default), 'single' for singleton record (site settings, about page), or 'form' for public submission forms",
|
|
34
34
|
"enum": ["entity", "single", "form"]
|
|
35
35
|
},
|
|
36
36
|
"fields": {
|
|
@@ -157,9 +157,6 @@
|
|
|
157
157
|
{
|
|
158
158
|
"$ref": "#/$defs/stringField"
|
|
159
159
|
},
|
|
160
|
-
{
|
|
161
|
-
"$ref": "#/$defs/emailField"
|
|
162
|
-
},
|
|
163
160
|
{
|
|
164
161
|
"$ref": "#/$defs/textField"
|
|
165
162
|
},
|
|
@@ -426,61 +423,6 @@
|
|
|
426
423
|
}
|
|
427
424
|
}
|
|
428
425
|
},
|
|
429
|
-
"emailField": {
|
|
430
|
-
"type": "object",
|
|
431
|
-
"description": "Email input field",
|
|
432
|
-
"required": ["name", "type"],
|
|
433
|
-
"additionalProperties": false,
|
|
434
|
-
"properties": {
|
|
435
|
-
"name": {
|
|
436
|
-
"type": "string"
|
|
437
|
-
},
|
|
438
|
-
"type": {
|
|
439
|
-
"const": "email"
|
|
440
|
-
},
|
|
441
|
-
"label": {
|
|
442
|
-
"type": "string"
|
|
443
|
-
},
|
|
444
|
-
"required": {
|
|
445
|
-
"type": "boolean"
|
|
446
|
-
},
|
|
447
|
-
"length": {
|
|
448
|
-
"type": "integer",
|
|
449
|
-
"description": "Maximum character length",
|
|
450
|
-
"minimum": 1,
|
|
451
|
-
"maximum": 65535
|
|
452
|
-
},
|
|
453
|
-
"hint": {
|
|
454
|
-
"type": "string"
|
|
455
|
-
},
|
|
456
|
-
"placeholder": {
|
|
457
|
-
"type": "string",
|
|
458
|
-
"description": "Placeholder text displayed in the email input"
|
|
459
|
-
},
|
|
460
|
-
"default": {
|
|
461
|
-
"type": "string"
|
|
462
|
-
},
|
|
463
|
-
"defaultValueFrom": {
|
|
464
|
-
"$ref": "#/$defs/defaultValueFrom"
|
|
465
|
-
},
|
|
466
|
-
"hasIcon": {
|
|
467
|
-
"type": "boolean",
|
|
468
|
-
"description": "Whether this field has an associated icon picker"
|
|
469
|
-
},
|
|
470
|
-
"hidden": {
|
|
471
|
-
"type": "boolean",
|
|
472
|
-
"description": "Whether this field is hidden from the form UI but still exists in the database"
|
|
473
|
-
},
|
|
474
|
-
"showWhen": {
|
|
475
|
-
"$ref": "#/$defs/showWhenCondition"
|
|
476
|
-
},
|
|
477
|
-
"height": {
|
|
478
|
-
"enum": ["auto", "fill"],
|
|
479
|
-
"default": "auto",
|
|
480
|
-
"description": "Generated field height behavior: auto size or fill remaining flexible space."
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
},
|
|
484
426
|
"markdownField": {
|
|
485
427
|
"type": "object",
|
|
486
428
|
"description": "Rich text editor with markdown support",
|
|
@@ -1279,10 +1221,6 @@
|
|
|
1279
1221
|
"type": "string",
|
|
1280
1222
|
"description": "Tab display label"
|
|
1281
1223
|
},
|
|
1282
|
-
"icon": {
|
|
1283
|
-
"type": "string",
|
|
1284
|
-
"description": "Lucide icon name rendered before the tab label"
|
|
1285
|
-
},
|
|
1286
1224
|
"fields": {
|
|
1287
1225
|
"type": "array",
|
|
1288
1226
|
"description": "Fields within this tab",
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { cn } from '@admin/utils/shared/cn'
|
|
4
|
-
import
|
|
4
|
+
import * as React from 'react'
|
|
5
5
|
import { Drawer as DrawerPrimitive } from 'vaul'
|
|
6
6
|
|
|
7
7
|
function Drawer({
|
|
8
|
+
children,
|
|
9
|
+
container,
|
|
8
10
|
handleOnly = true,
|
|
9
11
|
...props
|
|
10
12
|
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
|
11
|
-
|
|
13
|
+
const portalContainerRef = React.useRef<HTMLDivElement>(null)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<div ref={portalContainerRef} data-slot="drawer-container" className="contents" />
|
|
18
|
+
<DrawerPrimitive.Root
|
|
19
|
+
data-slot="drawer"
|
|
20
|
+
container={container ?? portalContainerRef.current}
|
|
21
|
+
handleOnly={handleOnly}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</DrawerPrimitive.Root>
|
|
26
|
+
</>
|
|
27
|
+
)
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
package/dist/cli.js
CHANGED
|
@@ -3056,7 +3056,7 @@ async function resolvePasswordEnvValue(options) {
|
|
|
3056
3056
|
options.overwriteEnvKeys.add(options.key);
|
|
3057
3057
|
return result.trim();
|
|
3058
3058
|
}
|
|
3059
|
-
async function
|
|
3059
|
+
async function collectResendConfig(cwd) {
|
|
3060
3060
|
const overwriteEnvKeys = /* @__PURE__ */ new Set();
|
|
3061
3061
|
const apiKey = await resolvePasswordEnvValue({
|
|
3062
3062
|
cwd,
|
|
@@ -3083,9 +3083,8 @@ async function promptResendConfig(cwd) {
|
|
|
3083
3083
|
}
|
|
3084
3084
|
}
|
|
3085
3085
|
});
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
[
|
|
3086
|
+
return {
|
|
3087
|
+
sections: [
|
|
3089
3088
|
{
|
|
3090
3089
|
header: "Email (Resend)",
|
|
3091
3090
|
vars: [
|
|
@@ -3094,15 +3093,19 @@ async function promptResendConfig(cwd) {
|
|
|
3094
3093
|
]
|
|
3095
3094
|
}
|
|
3096
3095
|
],
|
|
3097
|
-
overwriteEnvKeys
|
|
3098
|
-
|
|
3096
|
+
overwriteKeys: overwriteEnvKeys
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
async function promptResendConfig(cwd) {
|
|
3100
|
+
const collected = await collectResendConfig(cwd);
|
|
3101
|
+
const envResult = appendEnvVars(cwd, collected.sections, collected.overwriteKeys);
|
|
3099
3102
|
return {
|
|
3100
3103
|
configured: true,
|
|
3101
3104
|
addedEnvKeys: envResult.added,
|
|
3102
3105
|
updatedEnvKeys: envResult.updated
|
|
3103
3106
|
};
|
|
3104
3107
|
}
|
|
3105
|
-
async function
|
|
3108
|
+
async function collectR2Config(cwd) {
|
|
3106
3109
|
const overwriteEnvKeys = /* @__PURE__ */ new Set();
|
|
3107
3110
|
const accountId = await resolveTextEnvValue({
|
|
3108
3111
|
cwd,
|
|
@@ -3157,9 +3160,8 @@ async function promptR2Config(cwd) {
|
|
|
3157
3160
|
}
|
|
3158
3161
|
}
|
|
3159
3162
|
});
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
[
|
|
3163
|
+
return {
|
|
3164
|
+
sections: [
|
|
3163
3165
|
{
|
|
3164
3166
|
header: "Storage (Cloudflare R2)",
|
|
3165
3167
|
vars: [
|
|
@@ -3171,14 +3173,34 @@ async function promptR2Config(cwd) {
|
|
|
3171
3173
|
]
|
|
3172
3174
|
}
|
|
3173
3175
|
],
|
|
3174
|
-
overwriteEnvKeys
|
|
3175
|
-
|
|
3176
|
+
overwriteKeys: overwriteEnvKeys
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
async function promptR2Config(cwd) {
|
|
3180
|
+
const collected = await collectR2Config(cwd);
|
|
3181
|
+
const envResult = appendEnvVars(cwd, collected.sections, collected.overwriteKeys);
|
|
3176
3182
|
return {
|
|
3177
3183
|
configured: true,
|
|
3178
3184
|
addedEnvKeys: envResult.added,
|
|
3179
3185
|
updatedEnvKeys: envResult.updated
|
|
3180
3186
|
};
|
|
3181
3187
|
}
|
|
3188
|
+
async function collectIntegrationConfig(cwd, integrationIds) {
|
|
3189
|
+
const sections = [];
|
|
3190
|
+
const overwriteKeys = /* @__PURE__ */ new Set();
|
|
3191
|
+
for (const integrationId of resolveIntegrationInstallOrder(integrationIds)) {
|
|
3192
|
+
const definition = getIntegrationDefinition(integrationId);
|
|
3193
|
+
const collected = definition.configure === "resend" ? await collectResendConfig(cwd) : definition.configure === "r2" ? await collectR2Config(cwd) : void 0;
|
|
3194
|
+
if (!collected) {
|
|
3195
|
+
continue;
|
|
3196
|
+
}
|
|
3197
|
+
sections.push(...collected.sections);
|
|
3198
|
+
for (const key of collected.overwriteKeys) {
|
|
3199
|
+
overwriteKeys.add(key);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return { sections, overwriteKeys };
|
|
3203
|
+
}
|
|
3182
3204
|
async function configureIntegration(definition, cwd, interactive) {
|
|
3183
3205
|
if (definition.configure === "resend") {
|
|
3184
3206
|
if (interactive) {
|
|
@@ -15366,7 +15388,7 @@ export type TabValue = (typeof TAB_VALUES)[number]
|
|
|
15366
15388
|
const metadataStrip = showMetadataWidget ? ` {initialData?.id != null && (
|
|
15367
15389
|
<div
|
|
15368
15390
|
className={cn(
|
|
15369
|
-
'sticky z-
|
|
15391
|
+
'sticky z-100 flex flex-col gap-1 px-4 bg-background h-14 xl:flex-row xl:items-center xl:justify-between top-14 transition-shadow overflow-x-clip',
|
|
15370
15392
|
{
|
|
15371
15393
|
'shadow-modal/2': hasScrolled,
|
|
15372
15394
|
'border-b border-border/80': !hasScrolled
|
|
@@ -15449,11 +15471,31 @@ ${relState}` : ""}${creatableSelectState ? `
|
|
|
15449
15471
|
${creatableSelectState}` : ""}${staticMultiSelectState ? `
|
|
15450
15472
|
${staticMultiSelectState}` : ""}${hasDraft ? "\n const [submitIntent, setSubmitIntent] = React.useState<'draft' | 'publish' | null>(null)" : ""}
|
|
15451
15473
|
|
|
15474
|
+
const defaultValues: FormValues = {
|
|
15475
|
+
${defaultValues}
|
|
15476
|
+
}
|
|
15477
|
+
|
|
15478
|
+
const form = useForm<FormValues>({
|
|
15479
|
+
resolver: standardSchemaResolver(formSchema),
|
|
15480
|
+
mode: 'onBlur',
|
|
15481
|
+
reValidateMode: 'onChange',
|
|
15482
|
+
defaultValues
|
|
15483
|
+
})
|
|
15484
|
+
const defaultValuesRef = React.useRef(defaultValues)
|
|
15485
|
+
defaultValuesRef.current = defaultValues
|
|
15486
|
+
React.useEffect(
|
|
15487
|
+
() => () => {
|
|
15488
|
+
form.reset(defaultValuesRef.current)
|
|
15489
|
+
},
|
|
15490
|
+
[form]
|
|
15491
|
+
)
|
|
15492
|
+
|
|
15452
15493
|
const createMutation = useMutation({
|
|
15453
15494
|
mutationFn: (data: ${Singular}CreateInput) => create${Singular}(data),
|
|
15454
15495
|
onSuccess: async () => {
|
|
15455
15496
|
toast.success('${Singular} created successfully')
|
|
15456
15497
|
await queryClient.invalidateQueries({ queryKey: ['${plural}'] })
|
|
15498
|
+
form.reset(defaultValues)
|
|
15457
15499
|
router.push('${adminRoutePath}/${schema.name}')
|
|
15458
15500
|
},
|
|
15459
15501
|
onError: (error: Error) => {
|
|
@@ -15474,25 +15516,6 @@ ${staticMultiSelectState}` : ""}${hasDraft ? "\n const [submitIntent, setSubmit
|
|
|
15474
15516
|
})
|
|
15475
15517
|
|
|
15476
15518
|
const isPending = createMutation.isPending || updateMutation.isPending
|
|
15477
|
-
|
|
15478
|
-
const defaultValues: FormValues = {
|
|
15479
|
-
${defaultValues}
|
|
15480
|
-
}
|
|
15481
|
-
|
|
15482
|
-
const form = useForm<FormValues>({
|
|
15483
|
-
resolver: standardSchemaResolver(formSchema),
|
|
15484
|
-
mode: 'onBlur',
|
|
15485
|
-
reValidateMode: 'onChange',
|
|
15486
|
-
defaultValues
|
|
15487
|
-
})
|
|
15488
|
-
const defaultValuesRef = React.useRef(defaultValues)
|
|
15489
|
-
defaultValuesRef.current = defaultValues
|
|
15490
|
-
React.useEffect(
|
|
15491
|
-
() => () => {
|
|
15492
|
-
form.reset(defaultValuesRef.current)
|
|
15493
|
-
},
|
|
15494
|
-
[form]
|
|
15495
|
-
)
|
|
15496
15519
|
const { isDirty } = form.formState
|
|
15497
15520
|
${hasDraft ? " const canPublishPristineDraft = Boolean(initialData && !initialData.published)\n const canSubmitDraftAction = Boolean(initialData?.published || isDirty)\n const canSubmitPublishAction = isDirty || canPublishPristineDraft\n" : ""}
|
|
15498
15521
|
${defaultValueFromSyncFunctions ? `
|
|
@@ -15607,9 +15630,7 @@ ${topLevelTabsField ? `import type { TabValue } from '${hookImportPath}'
|
|
|
15607
15630
|
import { ${hookName} } from '${hookImportPath}'
|
|
15608
15631
|
import { ${contentComponentName} } from './${schema.name}-form-content'
|
|
15609
15632
|
|
|
15610
|
-
${metadataOffsetConst}
|
|
15611
|
-
|
|
15612
|
-
interface ${Singular}FormProps {
|
|
15633
|
+
${metadataOffsetConst}interface ${Singular}FormProps {
|
|
15613
15634
|
initialData?: ${Singular}
|
|
15614
15635
|
pageTitle: string
|
|
15615
15636
|
}
|
|
@@ -21131,14 +21152,14 @@ ${styleText(
|
|
|
21131
21152
|
value: "resend",
|
|
21132
21153
|
label: "Resend"
|
|
21133
21154
|
},
|
|
21134
|
-
{
|
|
21135
|
-
value: "skip",
|
|
21136
|
-
label: "Skip"
|
|
21137
|
-
},
|
|
21138
21155
|
{
|
|
21139
21156
|
value: "cloudflare",
|
|
21140
21157
|
label: `Cloudflare ${styleText("dim", "(coming soon)")}`,
|
|
21141
21158
|
disabled: true
|
|
21159
|
+
},
|
|
21160
|
+
{
|
|
21161
|
+
value: "skip",
|
|
21162
|
+
label: "Skip"
|
|
21142
21163
|
}
|
|
21143
21164
|
],
|
|
21144
21165
|
initialValue: "resend"
|
|
@@ -23620,6 +23641,7 @@ async function runInitCommand(name, options) {
|
|
|
23620
23641
|
integrations: [],
|
|
23621
23642
|
storage: "local"
|
|
23622
23643
|
} : await promptPlugins();
|
|
23644
|
+
const collectedIntegrationConfig = !options.yes && pluginSelection.integrations.length > 0 ? await collectIntegrationConfig(cwd, pluginSelection.integrations) : { sections: [], overwriteKeys: /* @__PURE__ */ new Set() };
|
|
23623
23645
|
let databaseUrl;
|
|
23624
23646
|
const existingDbUrl = readExistingDbUrl(cwd);
|
|
23625
23647
|
if (options.yes) {
|
|
@@ -23679,6 +23701,13 @@ async function runInitCommand(name, options) {
|
|
|
23679
23701
|
});
|
|
23680
23702
|
s.message("Environment variables");
|
|
23681
23703
|
const envResult = scaffoldEnv(cwd, { databaseUrl, namespace });
|
|
23704
|
+
if (collectedIntegrationConfig.sections.length > 0) {
|
|
23705
|
+
appendEnvVars(
|
|
23706
|
+
cwd,
|
|
23707
|
+
collectedIntegrationConfig.sections,
|
|
23708
|
+
collectedIntegrationConfig.overwriteKeys
|
|
23709
|
+
);
|
|
23710
|
+
}
|
|
23682
23711
|
const envCount = envResult.added.length + envResult.updated.length;
|
|
23683
23712
|
results.push({
|
|
23684
23713
|
label: "Environment variables",
|
|
@@ -23807,7 +23836,9 @@ async function runInitCommand(name, options) {
|
|
|
23807
23836
|
config: resolvedPluginInstallResult.config,
|
|
23808
23837
|
pm,
|
|
23809
23838
|
integrationIds: pluginSelection.integrations,
|
|
23810
|
-
|
|
23839
|
+
// Secrets were already collected up front and written to .env.local,
|
|
23840
|
+
// so the install runs without prompting (rule: no mid-scaffold input).
|
|
23841
|
+
interactive: false,
|
|
23811
23842
|
includeBiome: project2.linter.type === "none"
|
|
23812
23843
|
});
|
|
23813
23844
|
})() : Promise.resolve({
|