betterstart-cli 0.0.85 → 0.0.87
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/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
|
|
23
23
|
// cli.ts
|
|
24
24
|
import { readFileSync } from "fs";
|
|
25
|
+
import * as p29 from "@clack/prompts";
|
|
25
26
|
|
|
26
27
|
// core-engine/commands/require-init.ts
|
|
27
28
|
import path2 from "path";
|
|
@@ -74,7 +75,13 @@ function createAddCommand(runtime) {
|
|
|
74
75
|
"--skip-migration",
|
|
75
76
|
"Skip running db:push after installing schema-backed presets",
|
|
76
77
|
false
|
|
77
|
-
).option("--cwd <path>", "Project root path").
|
|
78
|
+
).option("--cwd <path>", "Project root path").addHelpText(
|
|
79
|
+
"after",
|
|
80
|
+
`
|
|
81
|
+
Examples:
|
|
82
|
+
$ betterstart add blog
|
|
83
|
+
$ betterstart add r2 --integration`
|
|
84
|
+
).action((items, options) => runtime.runAdd(items, options));
|
|
78
85
|
}
|
|
79
86
|
function collectRepeatedOption(value, previous = []) {
|
|
80
87
|
return [...previous, value];
|
|
@@ -95,6 +102,12 @@ function createCreateCommand(runtime) {
|
|
|
95
102
|
"--accept-generated",
|
|
96
103
|
"Accept generated output for files that would otherwise get merge conflicts",
|
|
97
104
|
false
|
|
105
|
+
).addHelpText(
|
|
106
|
+
"after",
|
|
107
|
+
`
|
|
108
|
+
Examples:
|
|
109
|
+
$ betterstart create entity
|
|
110
|
+
$ betterstart create form contact`
|
|
98
111
|
).action(
|
|
99
112
|
(kind, schemaName, options) => runtime.runCreate(kind, schemaName, options)
|
|
100
113
|
);
|
|
@@ -104,12 +117,18 @@ function createGenerateCommand(runtime) {
|
|
|
104
117
|
"--accept-generated",
|
|
105
118
|
"Accept generated output for files that would otherwise get merge conflicts",
|
|
106
119
|
false
|
|
107
|
-
).option("-a, --all", "Regenerate all schemas", false).option("--interactive", "Prompt during generate --all", false).option("--skip-migration", "Skip running db:push after generation", false).option("--cwd <path>", "Project root path").
|
|
120
|
+
).option("-a, --all", "Regenerate all schemas", false).option("--interactive", "Prompt during generate --all", false).option("--skip-migration", "Skip running db:push after generation", false).option("--cwd <path>", "Project root path").addHelpText(
|
|
121
|
+
"after",
|
|
122
|
+
`
|
|
123
|
+
Examples:
|
|
124
|
+
$ betterstart generate posts
|
|
125
|
+
$ betterstart generate --all --skip-migration`
|
|
126
|
+
).action(
|
|
108
127
|
(schemaName, options) => runtime.runGenerate(schemaName, options)
|
|
109
128
|
);
|
|
110
129
|
}
|
|
111
130
|
function createInitCommand(runtime) {
|
|
112
|
-
return new Command("init").description("Scaffold Admin into a new or existing Next.js app").argument("[name]", "Project name (creates new directory if fresh project)").option("--presets <presets>", "Comma-separated content preset IDs (e.g. blog)").option("--integrations <integrations>", "Comma-separated integration IDs (e.g. r2,resend)").option("--namespace <name>", "Generated admin namespace (e.g. admin, dashboard)").option("-y, --yes", "Skip prompts (defaults to manual database, local storage, and no deploy)").option("--skip-migration", "Skip running drizzle-kit push after scaffolding").option("--skip-admin-creation", "Skip prompting to create the initial admin user").option("--skip-dev-server-start", "Skip prompting to start the development server").option(
|
|
131
|
+
return new Command("init").description("Scaffold Admin into a new or existing Next.js app").argument("[name]", "Project name (creates new directory if fresh project)").option("--presets <presets>", "Comma-separated content preset IDs (e.g. blog)").option("--integrations <integrations>", "Comma-separated integration IDs (e.g. r2,resend)").option("--namespace <name>", "Generated admin namespace (e.g. admin, dashboard)").option("-y, --yes", "Skip prompts (defaults to manual database, local storage, and no deploy)").option("--json", "With --yes, print a machine-readable setup result on stdout", false).option("--skip-migration", "Skip running drizzle-kit push after scaffolding").option("--skip-admin-creation", "Skip prompting to create the initial admin user").option("--skip-dev-server-start", "Skip prompting to start the development server").option(
|
|
113
132
|
"--database-url <url>",
|
|
114
133
|
"PostgreSQL database connection string (postgres:// or postgresql://)"
|
|
115
134
|
).option("--database-provider <provider>", "Database provider: vercel, railway, or manual").option(
|
|
@@ -118,15 +137,22 @@ function createInitCommand(runtime) {
|
|
|
118
137
|
).option("--deploy-provider <provider>", "Deploy provider: vercel, railway, or none").option("--database-plan <id>", "Neon plan id for a Vercel-provisioned database").option("--railway-workspace <id-or-name>", "Railway workspace for a new project").option("--railway-project <id-or-name>", "Existing Railway project to intentionally reuse").option("--railway-environment <id-or-name>", "Railway environment (default: production)").option("--railway-service <id-or-name>", "Railway app service (default: web)").option(
|
|
119
138
|
"--railway-bucket-region <region>",
|
|
120
139
|
"Railway bucket region: sjc, iad, ams, or sin (default: iad)"
|
|
121
|
-
).option("--force", "Overwrite all existing Admin files (nuclear option)").
|
|
140
|
+
).option("--force", "Overwrite all existing Admin files (nuclear option)").addHelpText(
|
|
141
|
+
"after",
|
|
142
|
+
`
|
|
143
|
+
Examples:
|
|
144
|
+
$ betterstart init
|
|
145
|
+
$ betterstart init my-app --yes --database-url <url>
|
|
146
|
+
$ betterstart init --yes --database-provider vercel --deploy-provider vercel --json`
|
|
147
|
+
).action(
|
|
122
148
|
(name, options) => runtime.runInit(name, options)
|
|
123
149
|
);
|
|
124
150
|
}
|
|
125
151
|
function createListPresetsCommand(runtime) {
|
|
126
|
-
return new Command("list-presets").description("List available BetterStart presets").option("--cwd <path>", "Project root path").action((options) => runtime.runListPresets(options));
|
|
152
|
+
return new Command("list-presets").description("List available BetterStart presets").option("--json", "Print presets as JSON on stdout", false).option("--cwd <path>", "Project root path").action((options) => runtime.runListPresets(options));
|
|
127
153
|
}
|
|
128
154
|
function createListIntegrationsCommand(runtime) {
|
|
129
|
-
return new Command("list-integrations").description("List available BetterStart integrations").option("--cwd <path>", "Project root path").action((options) => runtime.runListIntegrations(options));
|
|
155
|
+
return new Command("list-integrations").description("List available BetterStart integrations").option("--json", "Print integrations as JSON on stdout", false).option("--cwd <path>", "Project root path").action((options) => runtime.runListIntegrations(options));
|
|
130
156
|
}
|
|
131
157
|
function createRemoveCommand(runtime) {
|
|
132
158
|
return new Command("remove").description("Remove installed BetterStart presets or integrations").argument("<items...>", "Preset or integration IDs to remove (e.g. blog r2)").option("-f, --force", "Skip confirmation prompt", false).option("--integration", "Remove integration IDs instead of content preset IDs", false).option("--cwd <path>", "Project root path").action((items, options) => runtime.runRemove(items, options));
|
|
@@ -137,13 +163,29 @@ function createRemoveSchemaCommand(runtime) {
|
|
|
137
163
|
);
|
|
138
164
|
}
|
|
139
165
|
function createSeedCommand(runtime) {
|
|
140
|
-
return new Command("seed").description("Create the initial admin user").option("--
|
|
166
|
+
return new Command("seed").description("Create the initial admin user").option("--email <email>", "Admin email (required off-TTY)").option(
|
|
167
|
+
"--name <name>",
|
|
168
|
+
'Admin display name (defaults to "Admin"); set the password via BETTERSTART_ADMIN_PASSWORD off-TTY'
|
|
169
|
+
).option("--cwd <path>", "Project root path").addHelpText(
|
|
170
|
+
"after",
|
|
171
|
+
`
|
|
172
|
+
Examples:
|
|
173
|
+
$ betterstart seed
|
|
174
|
+
$ BETTERSTART_ADMIN_PASSWORD=<password> betterstart seed --email admin@example.com`
|
|
175
|
+
).action((options) => runtime.runSeed(options));
|
|
141
176
|
}
|
|
142
177
|
function createUninstallCommand(runtime) {
|
|
143
178
|
return new Command("uninstall").description("Remove all Admin files and undo modifications made by betterstart init").option("-f, --force", "Skip all confirmation prompts", false).option("--cwd <path>", "Project root path").action((options) => runtime.runUninstall(options));
|
|
144
179
|
}
|
|
145
180
|
function createUpdateCommand(runtime) {
|
|
146
|
-
return new Command("update").alias("update-component").description("Update Admin components from the latest CLI templates").argument("[components...]", "Component names to update (e.g., media-field button)").option("--list", "List all available components").option("--all", "Update all components").option("--shadcn-preset <preset>", "Apply a shadcn preset to Admin UI components").option("--only <parts>", "Apply only supported shadcn preset parts: theme").option("--cwd <path>", "Project root path").
|
|
181
|
+
return new Command("update").alias("update-component").description("Update Admin components from the latest CLI templates").argument("[components...]", "Component names to update (e.g., media-field button)").option("--list", "List all available components").option("--json", "With --list, print components as JSON on stdout", false).option("--all", "Update all components").option("--shadcn-preset <preset>", "Apply a shadcn preset to Admin UI components").option("--only <parts>", "Apply only supported shadcn preset parts: theme").option("-y, --yes", "Skip the overwrite confirmation", false).option("--cwd <path>", "Project root path").addHelpText(
|
|
182
|
+
"after",
|
|
183
|
+
`
|
|
184
|
+
Examples:
|
|
185
|
+
$ betterstart update --list
|
|
186
|
+
$ betterstart update media-field
|
|
187
|
+
$ betterstart update --all --yes`
|
|
188
|
+
).action(
|
|
147
189
|
(components, options) => runtime.runUpdate(components, options)
|
|
148
190
|
);
|
|
149
191
|
}
|
|
@@ -156,7 +198,7 @@ function createUpdateStylesCommand(runtime) {
|
|
|
156
198
|
|
|
157
199
|
// adapters/next/commands/add.ts
|
|
158
200
|
import path27 from "path";
|
|
159
|
-
import * as
|
|
201
|
+
import * as p6 from "@clack/prompts";
|
|
160
202
|
|
|
161
203
|
// core-engine/config/serialize.ts
|
|
162
204
|
import fs3 from "fs";
|
|
@@ -186,6 +228,7 @@ function compactShortStringArrays(json) {
|
|
|
186
228
|
// core-engine/snapshots/store.ts
|
|
187
229
|
var BARREL_SCOPE = "_barrels";
|
|
188
230
|
var REMOVED_SCOPE = "_removed";
|
|
231
|
+
var UNSUPPORTED_PROJECT_STATE_MESSAGE = "Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout.";
|
|
189
232
|
function normalizeLineEndings(content) {
|
|
190
233
|
return content.replace(/\r\n/g, "\n");
|
|
191
234
|
}
|
|
@@ -459,6 +502,7 @@ function createNextAppCommand() {
|
|
|
459
502
|
// adapters/next/config.ts
|
|
460
503
|
import fs6 from "fs";
|
|
461
504
|
import path7 from "path";
|
|
505
|
+
import * as clack from "@clack/prompts";
|
|
462
506
|
|
|
463
507
|
// adapters/next/config/detect.ts
|
|
464
508
|
import fs5 from "fs";
|
|
@@ -691,11 +735,22 @@ async function resolveConfig(cwd) {
|
|
|
691
735
|
};
|
|
692
736
|
return config;
|
|
693
737
|
}
|
|
738
|
+
async function resolveConfigOrExit(cwd) {
|
|
739
|
+
try {
|
|
740
|
+
return await resolveConfig(cwd);
|
|
741
|
+
} catch (error) {
|
|
742
|
+
clack.log.error(
|
|
743
|
+
`Error loading config: ${error instanceof Error ? error.message : String(error)}`
|
|
744
|
+
);
|
|
745
|
+
process.exit(1);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
694
748
|
|
|
695
749
|
// adapters/next/generators/post-generate.ts
|
|
696
750
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
697
751
|
import fs8 from "fs";
|
|
698
752
|
import path10 from "path";
|
|
753
|
+
import * as p3 from "@clack/prompts";
|
|
699
754
|
|
|
700
755
|
// adapters/next/init/scaffolders/dependencies.ts
|
|
701
756
|
import { spawn } from "child_process";
|
|
@@ -1739,6 +1794,19 @@ ${stderr}`.trim();
|
|
|
1739
1794
|
});
|
|
1740
1795
|
}
|
|
1741
1796
|
|
|
1797
|
+
// adapters/next/utils/next-steps.ts
|
|
1798
|
+
import * as p2 from "@clack/prompts";
|
|
1799
|
+
function printNextSteps(options) {
|
|
1800
|
+
const steps = [`1. Review ${options.reviewLabel}`];
|
|
1801
|
+
if (options.needsMigration) {
|
|
1802
|
+
steps.push("2. Run database migration: db:push");
|
|
1803
|
+
steps.push(`3. Start the dev server and visit ${options.route}`);
|
|
1804
|
+
} else {
|
|
1805
|
+
steps.push(`2. Start the dev server and visit ${options.route}`);
|
|
1806
|
+
}
|
|
1807
|
+
p2.note(steps.join("\n"), "Next steps");
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1742
1810
|
// adapters/next/generators/post-generate.ts
|
|
1743
1811
|
var BIOME_FORMAT_TIMEOUT_MS = 3e4;
|
|
1744
1812
|
function loadEnvFile(cwd) {
|
|
@@ -1838,26 +1906,25 @@ function ensureDatabasePushDependencies(cwd, pm) {
|
|
|
1838
1906
|
if (missing.length === 0) {
|
|
1839
1907
|
return true;
|
|
1840
1908
|
}
|
|
1841
|
-
|
|
1842
|
-
`
|
|
1843
|
-
Installing database push dependencies (${missing.map((dependency) => dependency.name).join(", ")})...`
|
|
1909
|
+
p3.log.info(
|
|
1910
|
+
`Installing database push dependencies (${missing.map((dependency) => dependency.name).join(", ")})...`
|
|
1844
1911
|
);
|
|
1845
1912
|
for (const dependency of missing) {
|
|
1846
1913
|
const installed2 = installDependency(cwd, pm, dependency.name, dependency.dev);
|
|
1847
1914
|
if (!installed2) {
|
|
1848
|
-
|
|
1915
|
+
p3.log.warn(`Failed to install ${dependency.name}`);
|
|
1849
1916
|
return false;
|
|
1850
1917
|
}
|
|
1851
1918
|
}
|
|
1852
1919
|
const ready = hasPostgresRuntimeDependency(cwd) && hasDrizzleKitPostgresDriverDependency(cwd);
|
|
1853
1920
|
if (ready) {
|
|
1854
|
-
|
|
1921
|
+
p3.log.success("Database push dependencies installed");
|
|
1855
1922
|
} else {
|
|
1856
1923
|
const unresolved = [
|
|
1857
1924
|
!hasPostgresRuntimeDependency(cwd) ? POSTGRES_RUNTIME_DEP : null,
|
|
1858
1925
|
!hasDrizzleKitPostgresDriverDependency(cwd) ? DRIZZLE_KIT_POSTGRES_DRIVER_DEP : null
|
|
1859
1926
|
].filter((dependency) => Boolean(dependency));
|
|
1860
|
-
|
|
1927
|
+
p3.log.warn(`Installed dependencies but could not resolve ${unresolved.join(", ")}`);
|
|
1861
1928
|
}
|
|
1862
1929
|
return ready;
|
|
1863
1930
|
}
|
|
@@ -1895,16 +1962,14 @@ async function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
1895
1962
|
if (options.hasConflicts) {
|
|
1896
1963
|
result.dbPush = "skipped";
|
|
1897
1964
|
result.lintFix = "skipped";
|
|
1898
|
-
|
|
1899
|
-
`
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
console.log(
|
|
1907
|
-
"\n Resolve markers in the files above and re-run the BetterStart command that wrote them. Post-write tasks run automatically once every file is clean."
|
|
1965
|
+
const lines = [
|
|
1966
|
+
`Database: skipped (resolve conflict markers in ${options.conflictPaths?.length ?? 0} file(s), then re-run)`,
|
|
1967
|
+
"Formatting: skipped (biome refuses files with conflict markers)",
|
|
1968
|
+
...(options.conflictPaths ?? []).map((conflictPath) => ` - ${conflictPath}`)
|
|
1969
|
+
];
|
|
1970
|
+
p3.log.warn(lines.join("\n"));
|
|
1971
|
+
p3.log.message(
|
|
1972
|
+
"Resolve markers in the files above and re-run the BetterStart command that wrote them. Post-write tasks run automatically once every file is clean."
|
|
1908
1973
|
);
|
|
1909
1974
|
return result;
|
|
1910
1975
|
}
|
|
@@ -1913,63 +1978,71 @@ async function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
1913
1978
|
const dbUrl = process.env.DATABASE_URL;
|
|
1914
1979
|
if (!dbUrl) {
|
|
1915
1980
|
result.dbPush = "no-db-url";
|
|
1916
|
-
|
|
1917
|
-
|
|
1981
|
+
p3.log.warn(
|
|
1982
|
+
[
|
|
1983
|
+
"Database: skipped (no DATABASE_URL configured)",
|
|
1984
|
+
" To sync later: run db:push after setting DATABASE_URL"
|
|
1985
|
+
].join("\n")
|
|
1986
|
+
);
|
|
1918
1987
|
} else if (!ensureDatabasePushDependencies(cwd, pm)) {
|
|
1919
1988
|
result.dbPush = "failed";
|
|
1920
|
-
|
|
1921
|
-
"
|
|
1989
|
+
p3.log.warn(
|
|
1990
|
+
"Database push failed (install database dependencies and run drizzle-kit push manually)"
|
|
1922
1991
|
);
|
|
1923
1992
|
} else if (hasPkgScript(cwd, "db:push")) {
|
|
1924
|
-
|
|
1993
|
+
p3.log.info("Running db:push...");
|
|
1925
1994
|
const ok = runPmScript(pm, "db:push", cwd);
|
|
1926
1995
|
result.dbPush = ok ? "success" : "failed";
|
|
1927
|
-
|
|
1996
|
+
if (ok) {
|
|
1997
|
+
p3.log.success("Database schema synced");
|
|
1998
|
+
} else {
|
|
1999
|
+
p3.log.warn("Database push failed (run db:push manually)");
|
|
2000
|
+
}
|
|
1928
2001
|
} else {
|
|
1929
|
-
|
|
2002
|
+
p3.log.info("Running drizzle-kit push...");
|
|
1930
2003
|
try {
|
|
1931
2004
|
const pushResult = await runDrizzlePush(cwd, { interactive: true });
|
|
1932
2005
|
if (!pushResult.success) {
|
|
1933
2006
|
throw new Error(pushResult.error ?? "drizzle-kit push failed");
|
|
1934
2007
|
}
|
|
1935
2008
|
result.dbPush = "success";
|
|
1936
|
-
|
|
2009
|
+
p3.log.success("Database schema synced");
|
|
1937
2010
|
} catch {
|
|
1938
2011
|
result.dbPush = "failed";
|
|
1939
|
-
|
|
2012
|
+
p3.log.warn("Database push failed (run drizzle-kit push manually)");
|
|
1940
2013
|
}
|
|
1941
2014
|
}
|
|
1942
2015
|
} else {
|
|
1943
|
-
|
|
1944
|
-
Database: skipped (${options.skipMigrationMessage ?? "--skip-migration"})`);
|
|
2016
|
+
p3.log.info(`Database: skipped (${options.skipMigrationMessage ?? "--skip-migration"})`);
|
|
1945
2017
|
}
|
|
1946
2018
|
if (hasPkgScript(cwd, "lint:fix")) {
|
|
1947
|
-
|
|
2019
|
+
p3.log.info("Running lint:fix...");
|
|
1948
2020
|
const ok = runPmScript(pm, "lint:fix", cwd, BIOME_FORMAT_TIMEOUT_MS);
|
|
1949
2021
|
result.lintFix = ok ? "success" : "failed";
|
|
1950
|
-
|
|
2022
|
+
if (ok) {
|
|
2023
|
+
p3.log.success("Code formatted");
|
|
2024
|
+
} else {
|
|
2025
|
+
p3.log.warn("Lint fix had issues (run lint:fix manually)");
|
|
2026
|
+
}
|
|
1951
2027
|
} else {
|
|
1952
2028
|
try {
|
|
1953
2029
|
if (!runBiomeCheckWrite(cwd)) {
|
|
1954
2030
|
throw new Error("Biome binary not found");
|
|
1955
2031
|
}
|
|
1956
2032
|
result.lintFix = "success";
|
|
1957
|
-
|
|
2033
|
+
p3.log.success("Code formatted with Biome");
|
|
1958
2034
|
} catch {
|
|
1959
2035
|
result.lintFix = "failed";
|
|
1960
|
-
|
|
2036
|
+
p3.log.warn("Biome formatting had issues (run biome check --write manually)");
|
|
1961
2037
|
}
|
|
1962
2038
|
}
|
|
1963
2039
|
if (options.showNextSteps !== false) {
|
|
1964
2040
|
const schemaRoute = options.schemaRoutePath ?? `${options.adminRoutePath ?? "/admin"}/${schemaName}`;
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
} else {
|
|
1971
|
-
console.log(` 2. Start the dev server and visit ${schemaRoute}`);
|
|
1972
|
-
}
|
|
2041
|
+
printNextSteps({
|
|
2042
|
+
reviewLabel: "the generated files",
|
|
2043
|
+
needsMigration: Boolean(options.skipMigration) || result.dbPush !== "success",
|
|
2044
|
+
route: schemaRoute
|
|
2045
|
+
});
|
|
1973
2046
|
}
|
|
1974
2047
|
return result;
|
|
1975
2048
|
}
|
|
@@ -1977,7 +2050,7 @@ async function runPostGenerate(cwd, schemaName, options = {}) {
|
|
|
1977
2050
|
// adapters/next/integration-runtime.ts
|
|
1978
2051
|
import fs14 from "fs";
|
|
1979
2052
|
import path17 from "path";
|
|
1980
|
-
import * as
|
|
2053
|
+
import * as p4 from "@clack/prompts";
|
|
1981
2054
|
|
|
1982
2055
|
// core-engine/schema/schema-reader.ts
|
|
1983
2056
|
import fs9 from "fs";
|
|
@@ -3303,12 +3376,12 @@ async function resolveTextEnvValue(options) {
|
|
|
3303
3376
|
if (existingValue) {
|
|
3304
3377
|
return existingValue;
|
|
3305
3378
|
}
|
|
3306
|
-
const result = await
|
|
3379
|
+
const result = await p4.text({
|
|
3307
3380
|
message: options.message,
|
|
3308
3381
|
defaultValue: options.defaultValue,
|
|
3309
3382
|
validate: options.validate
|
|
3310
3383
|
});
|
|
3311
|
-
if (
|
|
3384
|
+
if (p4.isCancel(result)) {
|
|
3312
3385
|
throw new Error(options.cancelMessage);
|
|
3313
3386
|
}
|
|
3314
3387
|
options.overwriteEnvKeys.add(options.key);
|
|
@@ -3319,11 +3392,11 @@ async function resolvePasswordEnvValue(options) {
|
|
|
3319
3392
|
if (existingValue) {
|
|
3320
3393
|
return existingValue;
|
|
3321
3394
|
}
|
|
3322
|
-
const result = await
|
|
3395
|
+
const result = await p4.password({
|
|
3323
3396
|
message: options.message,
|
|
3324
3397
|
validate: options.validate
|
|
3325
3398
|
});
|
|
3326
|
-
if (
|
|
3399
|
+
if (p4.isCancel(result)) {
|
|
3327
3400
|
throw new Error(options.cancelMessage);
|
|
3328
3401
|
}
|
|
3329
3402
|
options.overwriteEnvKeys.add(options.key);
|
|
@@ -5704,14 +5777,14 @@ function buildStepsConstant(steps) {
|
|
|
5704
5777
|
${entries.join(",\n")}
|
|
5705
5778
|
]`;
|
|
5706
5779
|
}
|
|
5707
|
-
function buildComponentSource(
|
|
5780
|
+
function buildComponentSource(p30) {
|
|
5708
5781
|
const providerOpen = ` <NuqsAdapter>
|
|
5709
5782
|
<React.Suspense fallback={null}>
|
|
5710
|
-
<${
|
|
5783
|
+
<${p30.pascal}FormInner />
|
|
5711
5784
|
</React.Suspense>
|
|
5712
5785
|
</NuqsAdapter>`;
|
|
5713
5786
|
const exportWrapper = `
|
|
5714
|
-
export function ${
|
|
5787
|
+
export function ${p30.pascal}Form() {
|
|
5715
5788
|
return (
|
|
5716
5789
|
${providerOpen}
|
|
5717
5790
|
)
|
|
@@ -5720,22 +5793,22 @@ ${providerOpen}
|
|
|
5720
5793
|
return `'use client'
|
|
5721
5794
|
|
|
5722
5795
|
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
5723
|
-
import { ChevronLeft, ChevronRight${
|
|
5796
|
+
import { ChevronLeft, ChevronRight${p30.hasListFields ? ", Trash2" : ""} } from 'lucide-react'
|
|
5724
5797
|
import { createParser, useQueryState } from 'nuqs'
|
|
5725
5798
|
import { NuqsAdapter } from 'nuqs/adapters/next/app'
|
|
5726
5799
|
import * as React from 'react'
|
|
5727
|
-
${
|
|
5800
|
+
${p30.rhfImport}
|
|
5728
5801
|
import { z } from 'zod/v3'
|
|
5729
|
-
import { create${
|
|
5802
|
+
import { create${p30.pascal}Submission } from '@admin/actions/${p30.actionImportPath}'
|
|
5730
5803
|
|
|
5731
5804
|
const formSchema = z.object({
|
|
5732
|
-
${
|
|
5805
|
+
${p30.zodFields}
|
|
5733
5806
|
})
|
|
5734
5807
|
|
|
5735
5808
|
type FormValues = z.infer<typeof formSchema>
|
|
5736
5809
|
${buildFieldErrorHelper()}
|
|
5737
5810
|
|
|
5738
|
-
${
|
|
5811
|
+
${p30.stepsConst}
|
|
5739
5812
|
|
|
5740
5813
|
const stepParser = createParser({
|
|
5741
5814
|
parse(value) {
|
|
@@ -5753,7 +5826,7 @@ const stepParser = createParser({
|
|
|
5753
5826
|
}
|
|
5754
5827
|
}).withDefault(0)
|
|
5755
5828
|
|
|
5756
|
-
function ${
|
|
5829
|
+
function ${p30.pascal}FormInner() {
|
|
5757
5830
|
const [currentStep, setCurrentStep] = useQueryState('step', stepParser)
|
|
5758
5831
|
const [submitted, setSubmitted] = React.useState(false)
|
|
5759
5832
|
const [submitting, startSubmitTransition] = React.useTransition()
|
|
@@ -5761,11 +5834,11 @@ function ${p27.pascal}FormInner() {
|
|
|
5761
5834
|
const form = useForm<FormValues>({
|
|
5762
5835
|
resolver: standardSchemaResolver(formSchema),
|
|
5763
5836
|
defaultValues: {
|
|
5764
|
-
${
|
|
5837
|
+
${p30.defaults}
|
|
5765
5838
|
},
|
|
5766
5839
|
})
|
|
5767
5840
|
|
|
5768
|
-
${
|
|
5841
|
+
${p30.fieldArraySetup}${p30.watchSetup}
|
|
5769
5842
|
async function handleNext() {
|
|
5770
5843
|
const stepFields = STEPS[currentStep].fields as (keyof FormValues)[]
|
|
5771
5844
|
const isValid = await form.trigger(stepFields, { shouldFocus: true })
|
|
@@ -5781,9 +5854,9 @@ ${p27.fieldArraySetup}${p27.watchSetup}
|
|
|
5781
5854
|
function onSubmit(values: FormValues) {
|
|
5782
5855
|
startSubmitTransition(async () => {
|
|
5783
5856
|
try {
|
|
5784
|
-
const result = await create${
|
|
5857
|
+
const result = await create${p30.pascal}Submission(values)
|
|
5785
5858
|
if (result.success) {
|
|
5786
|
-
${
|
|
5859
|
+
${p30.successHandler}
|
|
5787
5860
|
} else {
|
|
5788
5861
|
form.setError('root', { message: result.error || 'Something went wrong' })
|
|
5789
5862
|
}
|
|
@@ -5797,7 +5870,7 @@ ${p27.fieldArraySetup}${p27.watchSetup}
|
|
|
5797
5870
|
return (
|
|
5798
5871
|
<div className="rounded-sm border p-6 text-center">
|
|
5799
5872
|
<h3 className="text-lg font-semibold">Thank you!</h3>
|
|
5800
|
-
<p className="mt-2 text-muted-foreground">${
|
|
5873
|
+
<p className="mt-2 text-muted-foreground">${p30.successMessage}</p>
|
|
5801
5874
|
</div>
|
|
5802
5875
|
)
|
|
5803
5876
|
}
|
|
@@ -5814,7 +5887,7 @@ ${p27.fieldArraySetup}${p27.watchSetup}
|
|
|
5814
5887
|
|
|
5815
5888
|
{/* Step content */}
|
|
5816
5889
|
<div key={currentStep} className="animate-in fade-in duration-300 space-y-6">
|
|
5817
|
-
${
|
|
5890
|
+
${p30.stepContentBlocks}
|
|
5818
5891
|
</div>
|
|
5819
5892
|
|
|
5820
5893
|
{form.formState.errors.root && (
|
|
@@ -5848,7 +5921,7 @@ ${p27.stepContentBlocks}
|
|
|
5848
5921
|
onClick={() => form.handleSubmit(onSubmit)()}
|
|
5849
5922
|
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-xs transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50"
|
|
5850
5923
|
>
|
|
5851
|
-
{submitting ? 'Submitting...' : ${
|
|
5924
|
+
{submitting ? 'Submitting...' : ${p30.submitText}}
|
|
5852
5925
|
</button>
|
|
5853
5926
|
)}
|
|
5854
5927
|
</div>
|
|
@@ -17672,7 +17745,7 @@ function readPresetTemplate(presetId, relativePath) {
|
|
|
17672
17745
|
// adapters/next/snapshots/apply.ts
|
|
17673
17746
|
import fs20 from "fs";
|
|
17674
17747
|
import path24 from "path";
|
|
17675
|
-
import * as
|
|
17748
|
+
import * as p5 from "@clack/prompts";
|
|
17676
17749
|
|
|
17677
17750
|
// core-engine/snapshots/ast-substitution.ts
|
|
17678
17751
|
import { Node, Project } from "ts-morph";
|
|
@@ -18339,7 +18412,7 @@ function isInteractiveSession(interactive) {
|
|
|
18339
18412
|
return interactive && Boolean(process.stdin.isTTY) && process.env.CI !== "true";
|
|
18340
18413
|
}
|
|
18341
18414
|
async function promptNoBaseDecision(filePath) {
|
|
18342
|
-
const answer = await
|
|
18415
|
+
const answer = await p5.select({
|
|
18343
18416
|
message: `File already exists without snapshot base: ${filePath}`,
|
|
18344
18417
|
options: [
|
|
18345
18418
|
{ value: "backup", label: "Backup and overwrite", hint: "recommended" },
|
|
@@ -18348,7 +18421,7 @@ async function promptNoBaseDecision(filePath) {
|
|
|
18348
18421
|
],
|
|
18349
18422
|
initialValue: "backup"
|
|
18350
18423
|
});
|
|
18351
|
-
if (
|
|
18424
|
+
if (p5.isCancel(answer)) {
|
|
18352
18425
|
throw new Error("Generation cancelled.");
|
|
18353
18426
|
}
|
|
18354
18427
|
return answer;
|
|
@@ -19218,43 +19291,40 @@ function listAvailablePresets() {
|
|
|
19218
19291
|
|
|
19219
19292
|
// adapters/next/commands/add.ts
|
|
19220
19293
|
function printAddNextSteps(installKind, hasSchemaChanges, result, adminRoutePath) {
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
return;
|
|
19227
|
-
}
|
|
19228
|
-
console.log(` 2. Start the dev server and review ${adminRoutePath}`);
|
|
19294
|
+
printNextSteps({
|
|
19295
|
+
reviewLabel: `the installed ${installKind} files`,
|
|
19296
|
+
needsMigration: hasSchemaChanges && result.dbPush !== "success",
|
|
19297
|
+
route: adminRoutePath
|
|
19298
|
+
});
|
|
19229
19299
|
}
|
|
19230
19300
|
async function runAddCommand(items, options) {
|
|
19231
19301
|
const installIntegrationsMode = Boolean(options.integration);
|
|
19232
19302
|
const presetIds = items.filter(isPresetId);
|
|
19233
19303
|
const integrationIds = items.filter(isIntegrationId);
|
|
19234
19304
|
if (!installIntegrationsMode && integrationIds.length > 0) {
|
|
19235
|
-
|
|
19305
|
+
p6.log.error(
|
|
19236
19306
|
`Integration IDs require --integration. Run \`betterstart add --integration ${integrationIds.join(" ")}\`.`
|
|
19237
19307
|
);
|
|
19238
19308
|
process.exit(1);
|
|
19239
19309
|
}
|
|
19240
19310
|
if (installIntegrationsMode && presetIds.length > 0) {
|
|
19241
|
-
|
|
19311
|
+
p6.log.error(`Preset IDs cannot be installed with --integration: ${presetIds.join(", ")}`);
|
|
19242
19312
|
process.exit(1);
|
|
19243
19313
|
}
|
|
19244
19314
|
const invalidItems = installIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((presetId) => !isPresetId(presetId));
|
|
19245
19315
|
if (invalidItems.length > 0) {
|
|
19246
|
-
|
|
19316
|
+
p6.log.error(
|
|
19247
19317
|
installIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPresetMessage(invalidItems)
|
|
19248
19318
|
);
|
|
19249
19319
|
process.exit(1);
|
|
19250
19320
|
}
|
|
19251
19321
|
const cwd = options.cwd ? path27.resolve(options.cwd) : process.cwd();
|
|
19252
|
-
const config = await
|
|
19322
|
+
const config = await resolveConfigOrExit(cwd);
|
|
19253
19323
|
const adminRoutePath = resolveAdminNamespace(config.frameworkConfig.next.namespace).routePath;
|
|
19254
19324
|
const pm = detectPackageManager(cwd);
|
|
19255
19325
|
const cliSyncResult = await syncProjectCliDependency(cwd, pm);
|
|
19256
19326
|
if (cliSyncResult && !cliSyncResult.success) {
|
|
19257
|
-
|
|
19327
|
+
p6.log.error(cliSyncResult.error ?? "Failed to sync betterstart-cli");
|
|
19258
19328
|
process.exit(1);
|
|
19259
19329
|
}
|
|
19260
19330
|
if (installIntegrationsMode) {
|
|
@@ -19268,10 +19338,10 @@ async function runAddCommand(items, options) {
|
|
|
19268
19338
|
});
|
|
19269
19339
|
writeConfigFile(cwd, result2.config);
|
|
19270
19340
|
if (result2.warnings.length > 0) {
|
|
19271
|
-
|
|
19341
|
+
p6.note(result2.warnings.join("\n"), "Warnings");
|
|
19272
19342
|
}
|
|
19273
19343
|
if (result2.installed.length === 0 && result2.activated.length === 0 && result2.skipped.length > 0) {
|
|
19274
|
-
|
|
19344
|
+
p6.outro(`No changes made. Already installed: ${result2.skipped.join(", ")}`);
|
|
19275
19345
|
return;
|
|
19276
19346
|
}
|
|
19277
19347
|
const conflictPaths2 = scanConflictPaths(cwd);
|
|
@@ -19293,12 +19363,12 @@ async function runAddCommand(items, options) {
|
|
|
19293
19363
|
result2.installed.length > 0 ? `Installed integration${result2.installed.length === 1 ? "" : "s"}: ${result2.installed.join(", ")}` : null,
|
|
19294
19364
|
result2.activated.length > 0 ? `Activated integration${result2.activated.length === 1 ? "" : "s"}: ${result2.activated.join(", ")}` : null
|
|
19295
19365
|
].filter(Boolean);
|
|
19296
|
-
|
|
19366
|
+
p6.outro(messages.join("\n"));
|
|
19297
19367
|
return;
|
|
19298
19368
|
}
|
|
19299
19369
|
const invalidPresets = items.filter((presetId) => !isPresetId(presetId));
|
|
19300
19370
|
if (invalidPresets.length > 0) {
|
|
19301
|
-
|
|
19371
|
+
p6.log.error(formatUnknownPresetMessage(invalidPresets));
|
|
19302
19372
|
process.exit(1);
|
|
19303
19373
|
}
|
|
19304
19374
|
const result = await installPresets({
|
|
@@ -19311,11 +19381,11 @@ async function runAddCommand(items, options) {
|
|
|
19311
19381
|
});
|
|
19312
19382
|
writeConfigFile(cwd, result.config);
|
|
19313
19383
|
if (result.installed.length === 0 && result.skipped.length > 0) {
|
|
19314
|
-
|
|
19384
|
+
p6.outro(`No changes made. Already installed: ${result.skipped.join(", ")}`);
|
|
19315
19385
|
return;
|
|
19316
19386
|
}
|
|
19317
19387
|
if (result.warnings.length > 0) {
|
|
19318
|
-
|
|
19388
|
+
p6.note(result.warnings.join("\n"), "Warnings");
|
|
19319
19389
|
}
|
|
19320
19390
|
const installedSchemaNames = result.installed.flatMap(
|
|
19321
19391
|
(presetId) => getPresetDefinition(presetId).schemaFiles.map((schemaFile) => schemaFile.replace(/\.json$/, ""))
|
|
@@ -19332,14 +19402,14 @@ async function runAddCommand(items, options) {
|
|
|
19332
19402
|
if (conflictPaths.length === 0) {
|
|
19333
19403
|
printAddNextSteps("preset", hasSchemaChanges, postGenerateResult, adminRoutePath);
|
|
19334
19404
|
}
|
|
19335
|
-
|
|
19405
|
+
p6.outro(
|
|
19336
19406
|
`Installed preset${result.installed.length === 1 ? "" : "s"}: ${result.installed.join(", ")}`
|
|
19337
19407
|
);
|
|
19338
19408
|
}
|
|
19339
19409
|
|
|
19340
19410
|
// adapters/next/commands/add-field.ts
|
|
19341
19411
|
import path30 from "path";
|
|
19342
|
-
import * as
|
|
19412
|
+
import * as p10 from "@clack/prompts";
|
|
19343
19413
|
|
|
19344
19414
|
// core-engine/schema/add-field.ts
|
|
19345
19415
|
import fs23 from "fs";
|
|
@@ -19991,7 +20061,7 @@ function getTabFields(tab) {
|
|
|
19991
20061
|
// adapters/next/commands/generate.ts
|
|
19992
20062
|
import fs24 from "fs";
|
|
19993
20063
|
import path29 from "path";
|
|
19994
|
-
import * as
|
|
20064
|
+
import * as p8 from "@clack/prompts";
|
|
19995
20065
|
|
|
19996
20066
|
// core-engine/snapshots/rename-detector.ts
|
|
19997
20067
|
function levenshtein(a, b) {
|
|
@@ -20085,7 +20155,7 @@ function diffSchemas(before, after) {
|
|
|
20085
20155
|
|
|
20086
20156
|
// core-engine/utils/spinner.ts
|
|
20087
20157
|
import { stripVTControlCharacters } from "util";
|
|
20088
|
-
import * as
|
|
20158
|
+
import * as p7 from "@clack/prompts";
|
|
20089
20159
|
var RENDER_OVERHEAD = 7;
|
|
20090
20160
|
var MIN_MESSAGE_WIDTH = 8;
|
|
20091
20161
|
function fitSpinnerMessage(message) {
|
|
@@ -20132,7 +20202,7 @@ function hasActiveSpinner() {
|
|
|
20132
20202
|
return activeSpinners > 0;
|
|
20133
20203
|
}
|
|
20134
20204
|
function spinner2(options) {
|
|
20135
|
-
const inner =
|
|
20205
|
+
const inner = p7.spinner({ ...options, cancelMessage: "Setup cancelled." });
|
|
20136
20206
|
const guided = options?.withGuide !== false;
|
|
20137
20207
|
let started = false;
|
|
20138
20208
|
const setStarted = (next) => {
|
|
@@ -20251,9 +20321,7 @@ function assertSnapshotStateOrExit(cwd) {
|
|
|
20251
20321
|
if (snapshotRootExists(cwd)) {
|
|
20252
20322
|
return;
|
|
20253
20323
|
}
|
|
20254
|
-
|
|
20255
|
-
" Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
|
|
20256
|
-
);
|
|
20324
|
+
p8.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
20257
20325
|
process.exit(1);
|
|
20258
20326
|
}
|
|
20259
20327
|
function printSchemaDiff(scope, loaded, cwd) {
|
|
@@ -20266,35 +20334,36 @@ function printSchemaDiff(scope, loaded, cwd) {
|
|
|
20266
20334
|
if (diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0 && diff.customComponents.added.length === 0 && diff.customComponents.removed.length === 0) {
|
|
20267
20335
|
return;
|
|
20268
20336
|
}
|
|
20269
|
-
|
|
20337
|
+
const lines = [`Schema diff for ${scope}:`];
|
|
20270
20338
|
if (diff.added.length > 0) {
|
|
20271
|
-
|
|
20339
|
+
lines.push(` + fields: ${diff.added.map((field) => field.name).join(", ")}`);
|
|
20272
20340
|
}
|
|
20273
20341
|
if (diff.removed.length > 0) {
|
|
20274
|
-
|
|
20342
|
+
lines.push(` - fields: ${diff.removed.map((field) => field.name).join(", ")}`);
|
|
20275
20343
|
}
|
|
20276
20344
|
if (diff.changed.length > 0) {
|
|
20277
|
-
|
|
20278
|
-
`
|
|
20345
|
+
lines.push(
|
|
20346
|
+
` ~ fields: ${diff.changed.map((entry) => `${entry.before.name}:${entry.before.type}->${entry.after.type}`).join(", ")}`
|
|
20279
20347
|
);
|
|
20280
20348
|
}
|
|
20281
20349
|
if (renameCandidates.length > 0) {
|
|
20282
|
-
|
|
20283
|
-
`
|
|
20350
|
+
lines.push(
|
|
20351
|
+
` ? rename candidates: ${renameCandidates.map((entry) => `${entry.before}->${entry.after}`).join(", ")}`
|
|
20284
20352
|
);
|
|
20285
20353
|
}
|
|
20286
20354
|
if (diff.customComponents.added.length > 0 || diff.customComponents.removed.length > 0) {
|
|
20287
|
-
|
|
20288
|
-
`
|
|
20355
|
+
lines.push(
|
|
20356
|
+
` custom cells: +[${diff.customComponents.added.join(", ")}] -[${diff.customComponents.removed.join(", ")}]`
|
|
20289
20357
|
);
|
|
20290
20358
|
}
|
|
20359
|
+
p8.log.message(lines.join("\n"));
|
|
20291
20360
|
}
|
|
20292
20361
|
async function promptConfirm(message) {
|
|
20293
|
-
const result = await
|
|
20362
|
+
const result = await p8.confirm({
|
|
20294
20363
|
message,
|
|
20295
20364
|
initialValue: true
|
|
20296
20365
|
});
|
|
20297
|
-
if (
|
|
20366
|
+
if (p8.isCancel(result)) {
|
|
20298
20367
|
throw new Error("Generation cancelled.");
|
|
20299
20368
|
}
|
|
20300
20369
|
return Boolean(result);
|
|
@@ -20358,9 +20427,9 @@ function collectRenamePreview(cwd, remotePaths, plan) {
|
|
|
20358
20427
|
}
|
|
20359
20428
|
for (const change of result.previewChanges) {
|
|
20360
20429
|
previewLines.push(
|
|
20361
|
-
`
|
|
20362
|
-
|
|
20363
|
-
|
|
20430
|
+
` ${change.path}:${change.line}
|
|
20431
|
+
- ${change.before || "(empty)"}
|
|
20432
|
+
+ ${change.after || "(empty)"}`
|
|
20364
20433
|
);
|
|
20365
20434
|
}
|
|
20366
20435
|
}
|
|
@@ -20423,8 +20492,8 @@ async function maybeBuildRenamePlan(loaded, cwd, config, generatedFiles, options
|
|
|
20423
20492
|
customCells: validatedCustomCells.renames
|
|
20424
20493
|
};
|
|
20425
20494
|
if (finalPlan.fields.length === 0 && finalPlan.customCells.length === 0) {
|
|
20426
|
-
|
|
20427
|
-
|
|
20495
|
+
if (validatedCustomCells.warnings.length > 0) {
|
|
20496
|
+
p8.log.warn(validatedCustomCells.warnings.join("\n"));
|
|
20428
20497
|
}
|
|
20429
20498
|
return void 0;
|
|
20430
20499
|
}
|
|
@@ -20433,19 +20502,19 @@ async function maybeBuildRenamePlan(loaded, cwd, config, generatedFiles, options
|
|
|
20433
20502
|
generatedFiles.map((file) => file.path),
|
|
20434
20503
|
finalPlan
|
|
20435
20504
|
);
|
|
20436
|
-
|
|
20437
|
-
|
|
20438
|
-
|
|
20439
|
-
|
|
20505
|
+
const renameLines = [
|
|
20506
|
+
"Proposed rename substitutions:",
|
|
20507
|
+
...formatRenameSelectionSummary(finalPlan).map((line) => ` ${line}`)
|
|
20508
|
+
];
|
|
20440
20509
|
const previewLines = preview.previewLines.slice(0, 20);
|
|
20441
|
-
|
|
20442
|
-
console.log(line);
|
|
20443
|
-
}
|
|
20510
|
+
renameLines.push(...previewLines);
|
|
20444
20511
|
if (preview.previewLines.length > previewLines.length) {
|
|
20445
|
-
|
|
20512
|
+
renameLines.push(` ... ${preview.previewLines.length - previewLines.length} more change(s)`);
|
|
20446
20513
|
}
|
|
20447
|
-
|
|
20448
|
-
|
|
20514
|
+
p8.log.message(renameLines.join("\n"));
|
|
20515
|
+
const warnings = [...validatedCustomCells.warnings, ...preview.warnings];
|
|
20516
|
+
if (warnings.length > 0) {
|
|
20517
|
+
p8.log.warn(warnings.join("\n"));
|
|
20449
20518
|
}
|
|
20450
20519
|
const confirmed = await promptConfirm("Apply these rename substitutions before merge?");
|
|
20451
20520
|
return confirmed ? finalPlan : void 0;
|
|
@@ -20525,37 +20594,38 @@ async function applyBarrels(cwd, config, options) {
|
|
|
20525
20594
|
});
|
|
20526
20595
|
}
|
|
20527
20596
|
function printApplySummary(schemaName, summary) {
|
|
20528
|
-
|
|
20597
|
+
const lines = [`${schemaName}:`];
|
|
20529
20598
|
if (summary.tombstoneCleared) {
|
|
20530
|
-
|
|
20599
|
+
lines.push(` Tombstone cleared: ${summary.tombstoneCleared}`);
|
|
20531
20600
|
}
|
|
20532
20601
|
if (summary.writtenFresh.length > 0) {
|
|
20533
|
-
|
|
20602
|
+
lines.push(` written fresh: ${summary.writtenFresh.length}`);
|
|
20534
20603
|
}
|
|
20535
20604
|
if (summary.overwritten.length > 0) {
|
|
20536
|
-
|
|
20605
|
+
lines.push(` overwritten: ${summary.overwritten.length}`);
|
|
20537
20606
|
}
|
|
20538
20607
|
if (summary.mergedCleanly.length > 0) {
|
|
20539
|
-
|
|
20608
|
+
lines.push(` merged cleanly: ${summary.mergedCleanly.length}`);
|
|
20540
20609
|
}
|
|
20541
20610
|
if (summary.acceptedGenerated.length > 0) {
|
|
20542
|
-
|
|
20611
|
+
lines.push(` accepted generated: ${summary.acceptedGenerated.length}`);
|
|
20543
20612
|
}
|
|
20544
20613
|
if (summary.conflictPaths.length > 0) {
|
|
20545
|
-
|
|
20614
|
+
lines.push(` with conflicts: ${summary.conflictPaths.length}`);
|
|
20546
20615
|
}
|
|
20547
20616
|
if (summary.orphanedDeleted.length > 0) {
|
|
20548
|
-
|
|
20617
|
+
lines.push(` orphaned deleted: ${summary.orphanedDeleted.length}`);
|
|
20549
20618
|
}
|
|
20550
20619
|
if (summary.orphanedKept.length > 0) {
|
|
20551
|
-
|
|
20620
|
+
lines.push(` orphaned kept: ${summary.orphanedKept.length}`);
|
|
20552
20621
|
}
|
|
20553
20622
|
if (summary.skipped.length > 0) {
|
|
20554
|
-
|
|
20623
|
+
lines.push(` skipped: ${summary.skipped.join(", ")}`);
|
|
20555
20624
|
}
|
|
20556
20625
|
if (summary.skippedCleared.length > 0) {
|
|
20557
|
-
|
|
20626
|
+
lines.push(` skip entries cleared: ${summary.skippedCleared.join(", ")}`);
|
|
20558
20627
|
}
|
|
20628
|
+
p8.log.message(lines.join("\n"));
|
|
20559
20629
|
}
|
|
20560
20630
|
async function runGenerateCommand(schemaName, options) {
|
|
20561
20631
|
const cwd = options.cwd ? path29.resolve(options.cwd) : process.cwd();
|
|
@@ -20564,9 +20634,7 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20564
20634
|
try {
|
|
20565
20635
|
config = await resolveConfig(cwd);
|
|
20566
20636
|
} catch (error) {
|
|
20567
|
-
|
|
20568
|
-
` Error loading config: ${error instanceof Error ? error.message : String(error)}`
|
|
20569
|
-
);
|
|
20637
|
+
p8.log.error(`Error loading config: ${error instanceof Error ? error.message : String(error)}`);
|
|
20570
20638
|
process.exit(1);
|
|
20571
20639
|
}
|
|
20572
20640
|
assertSnapshotStateOrExit(cwd);
|
|
@@ -20576,9 +20644,7 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20576
20644
|
if (options.all) {
|
|
20577
20645
|
const schemaNames = listSchemaNames(schemasDir);
|
|
20578
20646
|
if (schemaNames.length === 0) {
|
|
20579
|
-
|
|
20580
|
-
No schemas found in ${schemasDir}
|
|
20581
|
-
`);
|
|
20647
|
+
p8.log.error(`No schemas found in ${schemasDir}`);
|
|
20582
20648
|
process.exit(1);
|
|
20583
20649
|
}
|
|
20584
20650
|
const failed = [];
|
|
@@ -20586,16 +20652,14 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20586
20652
|
const processedRoutes = [];
|
|
20587
20653
|
let markdownDepsChecked = false;
|
|
20588
20654
|
const adminRoutePath2 = resolveAdminNamespace(config.frameworkConfig.next.namespace).routePath;
|
|
20589
|
-
|
|
20590
|
-
BetterStart Generator \u2014 regenerating ${schemaNames.length} schema(s)
|
|
20591
|
-
`);
|
|
20655
|
+
p8.intro(`BetterStart Generator \u2014 regenerating ${schemaNames.length} schema(s)`);
|
|
20592
20656
|
for (const name of schemaNames) {
|
|
20593
20657
|
if (hasTombstone(cwd, name)) {
|
|
20594
20658
|
if (loadManifest(cwd, name)) {
|
|
20595
20659
|
clearTombstone(cwd, name);
|
|
20596
|
-
|
|
20660
|
+
p8.log.warn(`${name}: tombstone cleared (manifest present)`);
|
|
20597
20661
|
} else {
|
|
20598
|
-
|
|
20662
|
+
p8.log.message(`${name}: skipped (tombstoned)`);
|
|
20599
20663
|
continue;
|
|
20600
20664
|
}
|
|
20601
20665
|
}
|
|
@@ -20621,7 +20685,7 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20621
20685
|
processed.push(name);
|
|
20622
20686
|
processedRoutes.push(getGeneratedSchemaRoutePath(loaded2, adminRoutePath2));
|
|
20623
20687
|
} catch (error) {
|
|
20624
|
-
|
|
20688
|
+
p8.log.error(`${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
20625
20689
|
failed.push(name);
|
|
20626
20690
|
}
|
|
20627
20691
|
}
|
|
@@ -20644,15 +20708,14 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20644
20708
|
});
|
|
20645
20709
|
}
|
|
20646
20710
|
if (failed.length > 0) {
|
|
20647
|
-
|
|
20648
|
-
Failed: ${failed.join(", ")}`);
|
|
20711
|
+
p8.log.error(`Failed: ${failed.join(", ")}`);
|
|
20649
20712
|
process.exit(1);
|
|
20650
20713
|
}
|
|
20651
|
-
|
|
20714
|
+
p8.outro(`Regenerated ${processed.length}/${schemaNames.length} schema(s)`);
|
|
20652
20715
|
return;
|
|
20653
20716
|
}
|
|
20654
20717
|
if (!schemaName) {
|
|
20655
|
-
|
|
20718
|
+
p8.log.error("Error: schema name is required (or use --all)");
|
|
20656
20719
|
process.exit(1);
|
|
20657
20720
|
}
|
|
20658
20721
|
let loaded;
|
|
@@ -20660,25 +20723,20 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20660
20723
|
loaded = loadSchema(schemasDir, schemaName);
|
|
20661
20724
|
} catch (error) {
|
|
20662
20725
|
if (error instanceof SchemaNotFoundError) {
|
|
20663
|
-
|
|
20726
|
+
p8.log.error(error.message);
|
|
20664
20727
|
} else {
|
|
20665
|
-
|
|
20666
|
-
` Error loading schema: ${error instanceof Error ? error.message : String(error)}`
|
|
20667
|
-
);
|
|
20728
|
+
p8.log.error(`Error loading schema: ${error instanceof Error ? error.message : String(error)}`);
|
|
20668
20729
|
}
|
|
20669
20730
|
process.exit(1);
|
|
20670
20731
|
}
|
|
20671
20732
|
const validationErrors = validateLoadedSchema(loaded);
|
|
20672
20733
|
if (validationErrors.length > 0) {
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
}
|
|
20734
|
+
p8.log.error(
|
|
20735
|
+
["Schema validation failed:", ...validationErrors.map((error) => ` - ${error}`)].join("\n")
|
|
20736
|
+
);
|
|
20677
20737
|
process.exit(1);
|
|
20678
20738
|
}
|
|
20679
|
-
|
|
20680
|
-
BetterStart Generator \u2014 ${loaded.schema.name}
|
|
20681
|
-
`);
|
|
20739
|
+
p8.intro(`BetterStart Generator \u2014 ${loaded.schema.name}`);
|
|
20682
20740
|
const needsMarkdownRenderer = schemaNeedsMarkdownRenderer(loaded);
|
|
20683
20741
|
await ensureMarkdownRendererDependencies(cwd, needsMarkdownRenderer);
|
|
20684
20742
|
const result = await applySchemaGeneration(loaded, cwd, config, {
|
|
@@ -20703,38 +20761,38 @@ async function runGenerateCommand(schemaName, options) {
|
|
|
20703
20761
|
adminRoutePath,
|
|
20704
20762
|
schemaRoutePath: getGeneratedSchemaRoutePath(loaded, adminRoutePath)
|
|
20705
20763
|
});
|
|
20706
|
-
|
|
20764
|
+
p8.outro(`Generated ${loaded.schema.name}`);
|
|
20707
20765
|
}
|
|
20708
20766
|
|
|
20709
20767
|
// adapters/next/commands/schema-prompts.ts
|
|
20710
|
-
import * as
|
|
20768
|
+
import * as p9 from "@clack/prompts";
|
|
20711
20769
|
function isInteractiveSession3() {
|
|
20712
20770
|
return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
|
|
20713
20771
|
}
|
|
20714
20772
|
function fail(message) {
|
|
20715
|
-
|
|
20773
|
+
p9.log.error(message);
|
|
20716
20774
|
process.exit(1);
|
|
20717
20775
|
}
|
|
20718
20776
|
function cancel2(context) {
|
|
20719
|
-
|
|
20777
|
+
p9.cancel(context.cancelMessage);
|
|
20720
20778
|
process.exit(0);
|
|
20721
20779
|
}
|
|
20722
20780
|
function cancelIfNeeded(context, value) {
|
|
20723
|
-
if (
|
|
20781
|
+
if (p9.isCancel(value)) {
|
|
20724
20782
|
cancel2(context);
|
|
20725
20783
|
}
|
|
20726
20784
|
return value;
|
|
20727
20785
|
}
|
|
20728
20786
|
async function promptConfirm2(context, message, initialValue = true) {
|
|
20729
|
-
const confirmed = await
|
|
20730
|
-
if (
|
|
20787
|
+
const confirmed = await p9.confirm({ message, initialValue });
|
|
20788
|
+
if (p9.isCancel(confirmed)) {
|
|
20731
20789
|
cancel2(context);
|
|
20732
20790
|
}
|
|
20733
20791
|
return Boolean(confirmed);
|
|
20734
20792
|
}
|
|
20735
20793
|
async function promptText(context, message, optionsOrDefaultValue) {
|
|
20736
20794
|
const options = typeof optionsOrDefaultValue === "string" ? { defaultValue: optionsOrDefaultValue } : optionsOrDefaultValue ?? {};
|
|
20737
|
-
const value = await
|
|
20795
|
+
const value = await p9.text({
|
|
20738
20796
|
message,
|
|
20739
20797
|
defaultValue: options.defaultValue,
|
|
20740
20798
|
placeholder: options.placeholder,
|
|
@@ -20751,7 +20809,7 @@ async function promptText(context, message, optionsOrDefaultValue) {
|
|
|
20751
20809
|
return String(cancelIfNeeded(context, value)).trim();
|
|
20752
20810
|
}
|
|
20753
20811
|
async function promptOptionalText(context, message) {
|
|
20754
|
-
const value = await
|
|
20812
|
+
const value = await p9.text({
|
|
20755
20813
|
message,
|
|
20756
20814
|
placeholder: "Leave blank to skip"
|
|
20757
20815
|
});
|
|
@@ -20759,7 +20817,7 @@ async function promptOptionalText(context, message) {
|
|
|
20759
20817
|
return result || void 0;
|
|
20760
20818
|
}
|
|
20761
20819
|
async function promptSelectValue(context, message, options, initialValue) {
|
|
20762
|
-
const value = await
|
|
20820
|
+
const value = await p9.select({
|
|
20763
20821
|
message,
|
|
20764
20822
|
options,
|
|
20765
20823
|
initialValue
|
|
@@ -20978,7 +21036,7 @@ async function shouldPromptRequired(type) {
|
|
|
20978
21036
|
return !["group", "section", "tabs", "separator"].includes(type);
|
|
20979
21037
|
}
|
|
20980
21038
|
async function promptFormFileOptions(context) {
|
|
20981
|
-
const selected = await
|
|
21039
|
+
const selected = await p9.multiselect({
|
|
20982
21040
|
message: "Field options",
|
|
20983
21041
|
options: [
|
|
20984
21042
|
{ value: "required", label: "Required field?" },
|
|
@@ -21017,7 +21075,7 @@ async function promptFieldOptions(context, kind, type, creatable) {
|
|
|
21017
21075
|
try {
|
|
21018
21076
|
return parseInteractiveOptionsList(value);
|
|
21019
21077
|
} catch (error) {
|
|
21020
|
-
|
|
21078
|
+
p9.log.error(error instanceof Error ? error.message : String(error));
|
|
21021
21079
|
}
|
|
21022
21080
|
}
|
|
21023
21081
|
}
|
|
@@ -21135,7 +21193,7 @@ async function promptChildFields(context, kind, schemasDir, scope, fields, depth
|
|
|
21135
21193
|
const addChild = fields.length === 0 ? await promptConfirm2(context, "Add a child field?", true) : await promptConfirm2(context, "Add another child field?", false);
|
|
21136
21194
|
if (!addChild) {
|
|
21137
21195
|
if (fields.length === 0) {
|
|
21138
|
-
|
|
21196
|
+
p9.log.warn("Containers need at least one child field.");
|
|
21139
21197
|
continue;
|
|
21140
21198
|
}
|
|
21141
21199
|
return;
|
|
@@ -21160,7 +21218,7 @@ async function promptTabsField(context, kind, schemasDir, scope, name, label, de
|
|
|
21160
21218
|
const addTab = field.tabs.length === 0 ? await promptConfirm2(context, "Add a tab?", true) : await promptConfirm2(context, "Add another tab?", false);
|
|
21161
21219
|
if (!addTab) {
|
|
21162
21220
|
if (field.tabs.length === 0) {
|
|
21163
|
-
|
|
21221
|
+
p9.log.warn("Tabs need at least one tab.");
|
|
21164
21222
|
continue;
|
|
21165
21223
|
}
|
|
21166
21224
|
return field;
|
|
@@ -21185,7 +21243,7 @@ async function promptTabsField(context, kind, schemasDir, scope, name, label, de
|
|
|
21185
21243
|
const addChild = mainFields.length + sidebarFields.length === 0 ? await promptConfirm2(context, "Add a tab child field?", true) : await promptConfirm2(context, "Add another tab child field?", false);
|
|
21186
21244
|
if (!addChild) {
|
|
21187
21245
|
if (mainFields.length + sidebarFields.length === 0) {
|
|
21188
|
-
|
|
21246
|
+
p9.log.warn("Tabs need at least one child field.");
|
|
21189
21247
|
continue;
|
|
21190
21248
|
}
|
|
21191
21249
|
break;
|
|
@@ -21256,7 +21314,7 @@ async function applyAdvancedOptions(context, field, kind, type, options) {
|
|
|
21256
21314
|
if (Number.isInteger(parsed) && parsed > 0) {
|
|
21257
21315
|
record.length = parsed;
|
|
21258
21316
|
} else {
|
|
21259
|
-
|
|
21317
|
+
p9.log.warn("Skipped invalid length.");
|
|
21260
21318
|
}
|
|
21261
21319
|
}
|
|
21262
21320
|
}
|
|
@@ -21414,11 +21472,11 @@ function hasNonInteractiveFieldOptions(options) {
|
|
|
21414
21472
|
);
|
|
21415
21473
|
}
|
|
21416
21474
|
function fail2(message) {
|
|
21417
|
-
|
|
21475
|
+
p10.log.error(message);
|
|
21418
21476
|
process.exit(1);
|
|
21419
21477
|
}
|
|
21420
21478
|
function cancel4(message = "Add field cancelled.") {
|
|
21421
|
-
|
|
21479
|
+
p10.cancel(message);
|
|
21422
21480
|
process.exit(0);
|
|
21423
21481
|
}
|
|
21424
21482
|
function schemaNameFromLoaded(loaded) {
|
|
@@ -21523,7 +21581,7 @@ function printPreview(loaded, owner, firstTimeGeneration, insertion, options) {
|
|
|
21523
21581
|
} else {
|
|
21524
21582
|
lines.push("schema integration: none");
|
|
21525
21583
|
}
|
|
21526
|
-
|
|
21584
|
+
p10.note(
|
|
21527
21585
|
`${lines.join("\n")}
|
|
21528
21586
|
|
|
21529
21587
|
${stringifyProjectJson(insertion.field).trim()}`,
|
|
@@ -21556,7 +21614,7 @@ Re-run with --yes to confirm these warning cases.`);
|
|
|
21556
21614
|
}
|
|
21557
21615
|
return;
|
|
21558
21616
|
}
|
|
21559
|
-
|
|
21617
|
+
p10.note(warnings.join("\n"), "Warnings");
|
|
21560
21618
|
const confirmed = await promptConfirm3("Continue with these warnings?", true);
|
|
21561
21619
|
if (!confirmed) {
|
|
21562
21620
|
cancel4();
|
|
@@ -21617,11 +21675,9 @@ async function runAddFieldCommand(schemaName, options) {
|
|
|
21617
21675
|
const cwd = options.cwd ? path30.resolve(options.cwd) : process.cwd();
|
|
21618
21676
|
const nonInteractive = hasNonInteractiveFieldOptions(options);
|
|
21619
21677
|
if (!snapshotRootExists(cwd)) {
|
|
21620
|
-
fail2(
|
|
21621
|
-
"Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
|
|
21622
|
-
);
|
|
21678
|
+
fail2(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
21623
21679
|
}
|
|
21624
|
-
const config = await
|
|
21680
|
+
const config = await resolveConfigOrExit(cwd);
|
|
21625
21681
|
const paths = resolveProjectPaths(config);
|
|
21626
21682
|
const schemasDir = path30.join(cwd, ...paths.schemasDir.split("/"));
|
|
21627
21683
|
const selectedSchemaName = await resolveSchemaNameInteractively(schemasDir, schemaName);
|
|
@@ -21675,7 +21731,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21675
21731
|
}
|
|
21676
21732
|
}
|
|
21677
21733
|
writeAuthoredGeneratedSchema(loaded);
|
|
21678
|
-
|
|
21734
|
+
p10.log.success(`Updated ${path30.relative(cwd, loaded.filePath)}`);
|
|
21679
21735
|
try {
|
|
21680
21736
|
await runGenerateCommand(selectedSchemaName, {
|
|
21681
21737
|
force: false,
|
|
@@ -21686,8 +21742,8 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21686
21742
|
cwd
|
|
21687
21743
|
});
|
|
21688
21744
|
} catch (error) {
|
|
21689
|
-
|
|
21690
|
-
|
|
21745
|
+
p10.log.error(error instanceof Error ? error.message : String(error));
|
|
21746
|
+
p10.log.info(
|
|
21691
21747
|
`Schema JSON was kept. Re-run \`betterstart generate ${selectedSchemaName}${options.skipMigration ? " --skip-migration" : ""}\` after resolving the issue.`
|
|
21692
21748
|
);
|
|
21693
21749
|
process.exit(1);
|
|
@@ -21697,7 +21753,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
21697
21753
|
// adapters/next/commands/create.ts
|
|
21698
21754
|
import fs25 from "fs";
|
|
21699
21755
|
import path31 from "path";
|
|
21700
|
-
import * as
|
|
21756
|
+
import * as p11 from "@clack/prompts";
|
|
21701
21757
|
var CREATE_PROMPT_CONTEXT = {
|
|
21702
21758
|
cancelMessage: "Create schema cancelled."
|
|
21703
21759
|
};
|
|
@@ -21888,7 +21944,7 @@ async function promptTabSlotFields(kind, schemasDir, titleField) {
|
|
|
21888
21944
|
);
|
|
21889
21945
|
if (!addField) {
|
|
21890
21946
|
if (!hasFields) {
|
|
21891
|
-
|
|
21947
|
+
p11.log.warn("Tabs need at least one child field.");
|
|
21892
21948
|
continue;
|
|
21893
21949
|
}
|
|
21894
21950
|
return { main, sidebar };
|
|
@@ -21936,7 +21992,7 @@ async function promptSchemaTab(kind, schemasDir, titleField, existingTabs) {
|
|
|
21936
21992
|
while (fields.length === 0) {
|
|
21937
21993
|
await promptAdditionalSchemaFields(kind, schemasDir, fields);
|
|
21938
21994
|
if (fields.length === 0) {
|
|
21939
|
-
|
|
21995
|
+
p11.log.warn("Tabs need at least one child field.");
|
|
21940
21996
|
}
|
|
21941
21997
|
}
|
|
21942
21998
|
}
|
|
@@ -22026,7 +22082,7 @@ async function promptFormSubmissionColumns(schema) {
|
|
|
22026
22082
|
}
|
|
22027
22083
|
const existingColumnNames = schema.columns ? new Set(schema.columns.map((column) => column.accessorKey)) : void 0;
|
|
22028
22084
|
const initialValues = existingColumnNames ? fields.filter((field) => existingColumnNames.has(field.name)).map((field) => field.name) : fields.map((field) => field.name);
|
|
22029
|
-
const selected = await
|
|
22085
|
+
const selected = await p11.multiselect({
|
|
22030
22086
|
message: "Submission table columns",
|
|
22031
22087
|
options: fields.map((field) => ({
|
|
22032
22088
|
value: field.name,
|
|
@@ -22035,7 +22091,7 @@ async function promptFormSubmissionColumns(schema) {
|
|
|
22035
22091
|
initialValues,
|
|
22036
22092
|
required: false
|
|
22037
22093
|
});
|
|
22038
|
-
if (
|
|
22094
|
+
if (p11.isCancel(selected)) {
|
|
22039
22095
|
cancel2(CREATE_PROMPT_CONTEXT);
|
|
22040
22096
|
}
|
|
22041
22097
|
const selectedNames = new Set(selected);
|
|
@@ -22095,7 +22151,7 @@ function schemaFilePath(kind, schemasDir, schemaName) {
|
|
|
22095
22151
|
return kind === "form" ? path31.join(schemasDir, "forms", `${schemaName}.json`) : path31.join(schemasDir, `${schemaName}.json`);
|
|
22096
22152
|
}
|
|
22097
22153
|
function printPreview2(loaded, cwd) {
|
|
22098
|
-
|
|
22154
|
+
p11.note(
|
|
22099
22155
|
`schema: ${loaded.name} (${loaded.kind})
|
|
22100
22156
|
path: ${path31.relative(cwd, loaded.filePath)}
|
|
22101
22157
|
owner: user
|
|
@@ -22108,14 +22164,12 @@ async function runCreateCommand(kindInput, schemaName, options) {
|
|
|
22108
22164
|
const kind = normalizeKind(kindInput);
|
|
22109
22165
|
const cwd = options.cwd ? path31.resolve(options.cwd) : process.cwd();
|
|
22110
22166
|
if (!snapshotRootExists(cwd)) {
|
|
22111
|
-
fail(
|
|
22112
|
-
"Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
|
|
22113
|
-
);
|
|
22167
|
+
fail(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
22114
22168
|
}
|
|
22115
22169
|
if (!isInteractiveSession3()) {
|
|
22116
22170
|
fail("Interactive create requires a TTY.");
|
|
22117
22171
|
}
|
|
22118
|
-
const config = await
|
|
22172
|
+
const config = await resolveConfigOrExit(cwd);
|
|
22119
22173
|
const paths = resolveProjectPaths(config);
|
|
22120
22174
|
const schemasDir = path31.join(cwd, ...paths.schemasDir.split("/"));
|
|
22121
22175
|
const metadata = await promptMetadata(kind, schemaName, schemasDir);
|
|
@@ -22150,7 +22204,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
22150
22204
|
}
|
|
22151
22205
|
fs25.mkdirSync(path31.dirname(filePath), { recursive: true });
|
|
22152
22206
|
writeAuthoredGeneratedSchema(loaded);
|
|
22153
|
-
|
|
22207
|
+
p11.log.success(`Created ${path31.relative(cwd, filePath)}`);
|
|
22154
22208
|
try {
|
|
22155
22209
|
await runGenerateCommand(metadata.name, {
|
|
22156
22210
|
force: false,
|
|
@@ -22161,8 +22215,8 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
|
|
|
22161
22215
|
cwd
|
|
22162
22216
|
});
|
|
22163
22217
|
} catch (error) {
|
|
22164
|
-
|
|
22165
|
-
|
|
22218
|
+
p11.log.error(error instanceof Error ? error.message : String(error));
|
|
22219
|
+
p11.log.info(
|
|
22166
22220
|
`Schema JSON was kept. Re-run \`betterstart generate ${metadata.name}${options.skipMigration ? " --skip-migration" : ""}\` after resolving the issue.`
|
|
22167
22221
|
);
|
|
22168
22222
|
process.exit(1);
|
|
@@ -22174,14 +22228,14 @@ import { execFileSync as execFileSync5, spawn as spawn6 } from "child_process";
|
|
|
22174
22228
|
import fs41 from "fs";
|
|
22175
22229
|
import path52 from "path";
|
|
22176
22230
|
import { PassThrough } from "stream";
|
|
22177
|
-
import * as
|
|
22231
|
+
import * as p24 from "@clack/prompts";
|
|
22178
22232
|
|
|
22179
22233
|
// core-engine/utils/cancel-guard.ts
|
|
22180
|
-
import * as
|
|
22234
|
+
import * as p12 from "@clack/prompts";
|
|
22181
22235
|
function installSetupCancelGuard() {
|
|
22182
22236
|
const onSignal = () => {
|
|
22183
22237
|
if (!hasActiveSpinner()) {
|
|
22184
|
-
|
|
22238
|
+
p12.cancel("Setup cancelled.");
|
|
22185
22239
|
}
|
|
22186
22240
|
process.exit(0);
|
|
22187
22241
|
};
|
|
@@ -22193,6 +22247,15 @@ function installSetupCancelGuard() {
|
|
|
22193
22247
|
};
|
|
22194
22248
|
}
|
|
22195
22249
|
|
|
22250
|
+
// core-engine/utils/json-mode.ts
|
|
22251
|
+
function redirectStdoutToStderr() {
|
|
22252
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
22253
|
+
process.stdout.write = ((chunk, encodingOrCallback, callback) => typeof encodingOrCallback === "function" ? process.stderr.write(chunk, encodingOrCallback) : process.stderr.write(chunk, encodingOrCallback, callback));
|
|
22254
|
+
return () => {
|
|
22255
|
+
process.stdout.write = originalWrite;
|
|
22256
|
+
};
|
|
22257
|
+
}
|
|
22258
|
+
|
|
22196
22259
|
// core-engine/utils/prompt-theme.ts
|
|
22197
22260
|
var GREEN_STEP_SUBMIT = "\x1B[32m\u25C7\x1B[39m";
|
|
22198
22261
|
var GREEN_CHECK = "\x1B[32m\u2713\x1B[39m";
|
|
@@ -22216,18 +22279,27 @@ function installPromptCheckmarks() {
|
|
|
22216
22279
|
};
|
|
22217
22280
|
}
|
|
22218
22281
|
|
|
22282
|
+
// core-engine/utils/redact.ts
|
|
22283
|
+
var REDACTED = "[redacted]";
|
|
22284
|
+
function redactSecrets(text7) {
|
|
22285
|
+
return text7.replace(/(\b[a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:)[^\s@]+@/gi, `$1${REDACTED}@`).replace(/\b(bearer\s+)[a-z0-9._~+/=-]+/gi, `$1${REDACTED}`).replace(
|
|
22286
|
+
/(\b[\w-]*(?:token|secret|password|passwd|api[-_]?key|apikey)[\w-]*\s*[=:]\s*)(["']?)[^\s"']{4,}\2/gi,
|
|
22287
|
+
`$1$2${REDACTED}$2`
|
|
22288
|
+
);
|
|
22289
|
+
}
|
|
22290
|
+
|
|
22219
22291
|
// adapters/next/init/prompts/database.ts
|
|
22220
22292
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
22221
|
-
import * as
|
|
22293
|
+
import * as p13 from "@clack/prompts";
|
|
22222
22294
|
import pc from "picocolors";
|
|
22223
22295
|
var VERCEL_NEON_URL = "https://vercel.com/dashboard/integrations/checkout/neon";
|
|
22224
22296
|
async function promptServices() {
|
|
22225
|
-
const choice = await
|
|
22297
|
+
const choice = await p13.select({
|
|
22226
22298
|
message: "Connect a PostgreSQL Database",
|
|
22227
22299
|
options: [
|
|
22228
22300
|
{
|
|
22229
22301
|
value: "vercel",
|
|
22230
|
-
label: "Vercel (Neon
|
|
22302
|
+
label: "Vercel (Neon Postgres)",
|
|
22231
22303
|
hint: "Automatically provision a Neon Postgres database"
|
|
22232
22304
|
},
|
|
22233
22305
|
{
|
|
@@ -22243,8 +22315,8 @@ async function promptServices() {
|
|
|
22243
22315
|
],
|
|
22244
22316
|
initialValue: "vercel"
|
|
22245
22317
|
});
|
|
22246
|
-
if (
|
|
22247
|
-
|
|
22318
|
+
if (p13.isCancel(choice)) {
|
|
22319
|
+
p13.cancel("Setup cancelled.");
|
|
22248
22320
|
process.exit(0);
|
|
22249
22321
|
}
|
|
22250
22322
|
if (choice === "vercel") {
|
|
@@ -22258,7 +22330,7 @@ async function promptServices() {
|
|
|
22258
22330
|
}
|
|
22259
22331
|
function openBrowserVercelNeon() {
|
|
22260
22332
|
openBrowser(VERCEL_NEON_URL);
|
|
22261
|
-
|
|
22333
|
+
p13.log.info(
|
|
22262
22334
|
`Opening Vercel... Create a Neon Postgres database, then copy the ${pc.cyan("DATABASE_URL")} from the dashboard.`
|
|
22263
22335
|
);
|
|
22264
22336
|
}
|
|
@@ -22268,12 +22340,12 @@ function openBrowserVercelNeonResource(url) {
|
|
|
22268
22340
|
return;
|
|
22269
22341
|
}
|
|
22270
22342
|
openBrowser(url);
|
|
22271
|
-
|
|
22343
|
+
p13.log.info(
|
|
22272
22344
|
`Opening Vercel... Copy the Neon ${pc.cyan("DATABASE_URL")} from the database dashboard, then paste it below.`
|
|
22273
22345
|
);
|
|
22274
22346
|
}
|
|
22275
22347
|
async function promptConnectionString() {
|
|
22276
|
-
const input = await
|
|
22348
|
+
const input = await p13.text({
|
|
22277
22349
|
message: "Paste your PostgreSQL connection string",
|
|
22278
22350
|
placeholder: "postgres://user:pass@host/db",
|
|
22279
22351
|
validate(val) {
|
|
@@ -22287,8 +22359,8 @@ async function promptConnectionString() {
|
|
|
22287
22359
|
}
|
|
22288
22360
|
}
|
|
22289
22361
|
});
|
|
22290
|
-
if (
|
|
22291
|
-
|
|
22362
|
+
if (p13.isCancel(input)) {
|
|
22363
|
+
p13.cancel("Setup cancelled.");
|
|
22292
22364
|
process.exit(0);
|
|
22293
22365
|
}
|
|
22294
22366
|
return input.replace(/^['"]|['"]$/g, "").trim();
|
|
@@ -22308,8 +22380,7 @@ function openBrowser(url) {
|
|
|
22308
22380
|
}
|
|
22309
22381
|
|
|
22310
22382
|
// adapters/next/init/prompts/presets.ts
|
|
22311
|
-
import
|
|
22312
|
-
import * as p12 from "@clack/prompts";
|
|
22383
|
+
import * as p14 from "@clack/prompts";
|
|
22313
22384
|
|
|
22314
22385
|
// adapters/next/init/scaffolders/env.ts
|
|
22315
22386
|
import crypto2 from "crypto";
|
|
@@ -22459,6 +22530,7 @@ function scaffoldEnv(cwd, options) {
|
|
|
22459
22530
|
}
|
|
22460
22531
|
|
|
22461
22532
|
// adapters/next/init/prompts/presets.ts
|
|
22533
|
+
import pc2 from "picocolors";
|
|
22462
22534
|
function maskBlobToken(token) {
|
|
22463
22535
|
const match = /^(vercel_blob_rw_[A-Za-z0-9]+)_/.exec(token);
|
|
22464
22536
|
if (match) return `${match[1]}_***`;
|
|
@@ -22473,7 +22545,7 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22473
22545
|
overwriteKeys.add(key);
|
|
22474
22546
|
}
|
|
22475
22547
|
};
|
|
22476
|
-
const storage = await
|
|
22548
|
+
const storage = await p14.select({
|
|
22477
22549
|
message: "Choose a file storage",
|
|
22478
22550
|
options: [
|
|
22479
22551
|
{
|
|
@@ -22492,13 +22564,13 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22492
22564
|
{
|
|
22493
22565
|
value: "local",
|
|
22494
22566
|
label: "Local filesystem (public/media)",
|
|
22495
|
-
hint: "\
|
|
22567
|
+
hint: "\u25B2 For testing only"
|
|
22496
22568
|
}
|
|
22497
22569
|
],
|
|
22498
22570
|
initialValue: "vercel-blob"
|
|
22499
22571
|
});
|
|
22500
|
-
if (
|
|
22501
|
-
|
|
22572
|
+
if (p14.isCancel(storage)) {
|
|
22573
|
+
p14.cancel("Setup cancelled.");
|
|
22502
22574
|
process.exit(0);
|
|
22503
22575
|
}
|
|
22504
22576
|
if (storage === "r2") {
|
|
@@ -22508,7 +22580,7 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22508
22580
|
if (flow?.ok && flow.config) {
|
|
22509
22581
|
mergeIntegrationConfig(flow.config);
|
|
22510
22582
|
} else if (flow) {
|
|
22511
|
-
|
|
22583
|
+
p14.log.warn(
|
|
22512
22584
|
"Continuing without Railway bucket credentials \u2014 rerun betterstart add --integration railway-bucket after fixing Railway access."
|
|
22513
22585
|
);
|
|
22514
22586
|
} else {
|
|
@@ -22525,7 +22597,7 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22525
22597
|
});
|
|
22526
22598
|
overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
|
|
22527
22599
|
} else if (flow) {
|
|
22528
|
-
|
|
22600
|
+
p14.log.warn(
|
|
22529
22601
|
"Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
|
|
22530
22602
|
);
|
|
22531
22603
|
sections.push({
|
|
@@ -22534,9 +22606,8 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22534
22606
|
});
|
|
22535
22607
|
} else {
|
|
22536
22608
|
if (existingToken) {
|
|
22537
|
-
|
|
22538
|
-
`Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${
|
|
22539
|
-
"dim",
|
|
22609
|
+
p14.log.info(
|
|
22610
|
+
`Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${pc2.dim(
|
|
22540
22611
|
`(${maskBlobToken(existingToken)})`
|
|
22541
22612
|
)}`
|
|
22542
22613
|
);
|
|
@@ -22544,7 +22615,7 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22544
22615
|
mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["vercel-blob"]));
|
|
22545
22616
|
}
|
|
22546
22617
|
}
|
|
22547
|
-
const selectedPresets = await
|
|
22618
|
+
const selectedPresets = await p14.multiselect({
|
|
22548
22619
|
message: "Select presets",
|
|
22549
22620
|
options: [
|
|
22550
22621
|
{
|
|
@@ -22556,8 +22627,8 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22556
22627
|
required: false,
|
|
22557
22628
|
initialValues: ["blog"]
|
|
22558
22629
|
});
|
|
22559
|
-
if (
|
|
22560
|
-
|
|
22630
|
+
if (p14.isCancel(selectedPresets)) {
|
|
22631
|
+
p14.cancel("Setup cancelled.");
|
|
22561
22632
|
process.exit(0);
|
|
22562
22633
|
}
|
|
22563
22634
|
const integrations = [];
|
|
@@ -22584,9 +22655,9 @@ async function promptPresets(cwd, options = {}) {
|
|
|
22584
22655
|
}
|
|
22585
22656
|
|
|
22586
22657
|
// adapters/next/init/prompts/project.ts
|
|
22587
|
-
import * as
|
|
22658
|
+
import * as p15 from "@clack/prompts";
|
|
22588
22659
|
async function promptProject(defaultName) {
|
|
22589
|
-
const projectName = await
|
|
22660
|
+
const projectName = await p15.text({
|
|
22590
22661
|
message: "What is your project named?",
|
|
22591
22662
|
placeholder: defaultName ?? "(Press Enter to use default)",
|
|
22592
22663
|
defaultValue: defaultName ?? ".",
|
|
@@ -22599,15 +22670,15 @@ async function promptProject(defaultName) {
|
|
|
22599
22670
|
return void 0;
|
|
22600
22671
|
}
|
|
22601
22672
|
});
|
|
22602
|
-
if (
|
|
22603
|
-
|
|
22673
|
+
if (p15.isCancel(projectName)) {
|
|
22674
|
+
p15.cancel("Setup cancelled.");
|
|
22604
22675
|
process.exit(0);
|
|
22605
22676
|
}
|
|
22606
22677
|
return { projectName: projectName.trim() || "." };
|
|
22607
22678
|
}
|
|
22608
22679
|
|
|
22609
22680
|
// adapters/next/init/providers.ts
|
|
22610
|
-
import * as
|
|
22681
|
+
import * as p16 from "@clack/prompts";
|
|
22611
22682
|
var DATABASE_PROVIDERS = ["vercel", "railway", "manual"];
|
|
22612
22683
|
var STORAGE_PROVIDERS = ["vercel-blob", "railway-bucket", "r2", "local"];
|
|
22613
22684
|
var DEPLOY_PROVIDERS = ["vercel", "railway", "none"];
|
|
@@ -22664,7 +22735,7 @@ function validateResolvedDatabaseProvider(provider, options) {
|
|
|
22664
22735
|
}
|
|
22665
22736
|
}
|
|
22666
22737
|
async function promptDeployProvider(initialValue = "none") {
|
|
22667
|
-
const provider = await
|
|
22738
|
+
const provider = await p16.select({
|
|
22668
22739
|
message: "Deploy this project",
|
|
22669
22740
|
options: [
|
|
22670
22741
|
{
|
|
@@ -22682,8 +22753,8 @@ async function promptDeployProvider(initialValue = "none") {
|
|
|
22682
22753
|
],
|
|
22683
22754
|
initialValue
|
|
22684
22755
|
});
|
|
22685
|
-
if (
|
|
22686
|
-
|
|
22756
|
+
if (p16.isCancel(provider)) {
|
|
22757
|
+
p16.cancel("Setup cancelled.");
|
|
22687
22758
|
process.exit(0);
|
|
22688
22759
|
}
|
|
22689
22760
|
return provider;
|
|
@@ -22692,7 +22763,7 @@ async function promptDeployProvider(initialValue = "none") {
|
|
|
22692
22763
|
// adapters/next/init/railway/deploy.ts
|
|
22693
22764
|
import fs28 from "fs";
|
|
22694
22765
|
import path34 from "path";
|
|
22695
|
-
import * as
|
|
22766
|
+
import * as p18 from "@clack/prompts";
|
|
22696
22767
|
|
|
22697
22768
|
// adapters/next/init/deploy/guard.ts
|
|
22698
22769
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -22789,7 +22860,7 @@ function guardBetterstartConfig(cwd, restores) {
|
|
|
22789
22860
|
}
|
|
22790
22861
|
|
|
22791
22862
|
// adapters/next/init/railway/project.ts
|
|
22792
|
-
import * as
|
|
22863
|
+
import * as p17 from "@clack/prompts";
|
|
22793
22864
|
|
|
22794
22865
|
// adapters/next/init/railway/runner.ts
|
|
22795
22866
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -22930,7 +23001,7 @@ function railwayOutputTail(output, maxLines = 20) {
|
|
|
22930
23001
|
}
|
|
22931
23002
|
|
|
22932
23003
|
// adapters/next/init/railway/project.ts
|
|
22933
|
-
import
|
|
23004
|
+
import pc3 from "picocolors";
|
|
22934
23005
|
function isString(value) {
|
|
22935
23006
|
return typeof value === "string" && value.trim().length > 0;
|
|
22936
23007
|
}
|
|
@@ -23073,22 +23144,22 @@ async function ensureRailwayProject(runner, options) {
|
|
|
23073
23144
|
createWorkspace = options.workspaces[0]?.id;
|
|
23074
23145
|
} else if (!createWorkspace && options.interactive && options.workspaces?.length) {
|
|
23075
23146
|
projectSpinner.clear();
|
|
23076
|
-
const workspace = await
|
|
23147
|
+
const workspace = await p17.select({
|
|
23077
23148
|
message: "Choose a Railway workspace",
|
|
23078
23149
|
options: options.workspaces.map((candidate) => ({
|
|
23079
23150
|
value: candidate.id,
|
|
23080
23151
|
label: candidate.name
|
|
23081
23152
|
}))
|
|
23082
23153
|
});
|
|
23083
|
-
if (
|
|
23084
|
-
|
|
23154
|
+
if (p17.isCancel(workspace)) {
|
|
23155
|
+
p17.cancel("Setup cancelled.");
|
|
23085
23156
|
process.exit(0);
|
|
23086
23157
|
}
|
|
23087
23158
|
createWorkspace = workspace;
|
|
23088
23159
|
projectSpinner.start("Checking Railway project names");
|
|
23089
23160
|
}
|
|
23090
23161
|
const projects = await listRailwayProjects(runner, options.cwd, options.env);
|
|
23091
|
-
projectSpinner.message(`Creating Railway project ${
|
|
23162
|
+
projectSpinner.message(`Creating Railway project ${pc3.cyan(options.projectName)}`);
|
|
23092
23163
|
const created = await createRailwayProject(
|
|
23093
23164
|
runner,
|
|
23094
23165
|
options.cwd,
|
|
@@ -23368,7 +23439,7 @@ ${credentialsResult.stderr}`) ?? `Could not retrieve credentials for Railway buc
|
|
|
23368
23439
|
}
|
|
23369
23440
|
|
|
23370
23441
|
// adapters/next/init/railway/deploy.ts
|
|
23371
|
-
import
|
|
23442
|
+
import pc4 from "picocolors";
|
|
23372
23443
|
var DEPLOY_TIMEOUT_MS = 9e5;
|
|
23373
23444
|
var ENV_TIMEOUT_MS = 3e4;
|
|
23374
23445
|
var ENV_SYNC_SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -23607,7 +23678,7 @@ async function runRailwayDeployFlow(options) {
|
|
|
23607
23678
|
);
|
|
23608
23679
|
envSpinner.clear();
|
|
23609
23680
|
for (const key of sync.failed) {
|
|
23610
|
-
|
|
23681
|
+
p18.log.warn(`Could not set ${pc4.cyan(key)} on Railway.`);
|
|
23611
23682
|
}
|
|
23612
23683
|
if (sync.failed.length > 0) {
|
|
23613
23684
|
return {
|
|
@@ -23622,13 +23693,13 @@ async function runRailwayDeployFlow(options) {
|
|
|
23622
23693
|
const packageGuard = await guardProjectForDeploy(options.cwd);
|
|
23623
23694
|
guardSpinner.clear();
|
|
23624
23695
|
if (packageGuard.lockfile === "failed") {
|
|
23625
|
-
|
|
23696
|
+
p18.log.warn(
|
|
23626
23697
|
"Could not refresh pnpm-lock.yaml after removing betterstart-cli \u2014 the remote install may fail."
|
|
23627
23698
|
);
|
|
23628
23699
|
}
|
|
23629
23700
|
for (const dependency of packageGuard.localSpecDeps) {
|
|
23630
|
-
|
|
23631
|
-
`Dependency ${
|
|
23701
|
+
p18.log.warn(
|
|
23702
|
+
`Dependency ${pc4.cyan(dependency)} uses a local spec that cannot install on Railway.`
|
|
23632
23703
|
);
|
|
23633
23704
|
}
|
|
23634
23705
|
const deploySpinner = spinner2();
|
|
@@ -23662,7 +23733,7 @@ async function runRailwayDeployFlow(options) {
|
|
|
23662
23733
|
packageGuard.restore();
|
|
23663
23734
|
}
|
|
23664
23735
|
if (!deploy.success) {
|
|
23665
|
-
deploySpinner.stop(`${
|
|
23736
|
+
deploySpinner.stop(`${pc4.yellow("\u25B2")} Railway deploy did not complete`);
|
|
23666
23737
|
const logs = await readFailedDeploymentLogs(options.session, options.cwd, service);
|
|
23667
23738
|
return {
|
|
23668
23739
|
ok: false,
|
|
@@ -23672,7 +23743,7 @@ async function runRailwayDeployFlow(options) {
|
|
|
23672
23743
|
${deploy.stderr}`) ?? deploy.errorMessage
|
|
23673
23744
|
};
|
|
23674
23745
|
}
|
|
23675
|
-
deploySpinner.stop(url ? `Deployed ${
|
|
23746
|
+
deploySpinner.stop(url ? `Deployed ${pc4.cyan(url)}` : "Deployed to Railway");
|
|
23676
23747
|
return { ok: true, url, service, syncedEnvKeys: sync.synced };
|
|
23677
23748
|
} catch (error) {
|
|
23678
23749
|
return { ok: false, detail: error instanceof Error ? error.message : String(error) };
|
|
@@ -23680,8 +23751,8 @@ ${deploy.stderr}`) ?? deploy.errorMessage
|
|
|
23680
23751
|
}
|
|
23681
23752
|
|
|
23682
23753
|
// adapters/next/init/railway/auth.ts
|
|
23683
|
-
import * as
|
|
23684
|
-
import
|
|
23754
|
+
import * as p19 from "@clack/prompts";
|
|
23755
|
+
import pc5 from "picocolors";
|
|
23685
23756
|
var WHOAMI_TIMEOUT_MS = 3e4;
|
|
23686
23757
|
var LOGIN_TIMEOUT_MS = 3e5;
|
|
23687
23758
|
function isRailwayAccount(value) {
|
|
@@ -23732,7 +23803,7 @@ async function ensureRailwayAuth(runner, cwd, options) {
|
|
|
23732
23803
|
checkSpinner.clear();
|
|
23733
23804
|
} else {
|
|
23734
23805
|
checkSpinner.stop(
|
|
23735
|
-
existing.account?.email ? `Signed in to Railway as ${
|
|
23806
|
+
existing.account?.email ? `Signed in to Railway as ${pc5.cyan(existing.account.email)}` : "Signed in to Railway"
|
|
23736
23807
|
);
|
|
23737
23808
|
}
|
|
23738
23809
|
return existing;
|
|
@@ -23750,7 +23821,7 @@ async function ensureRailwayAuth(runner, cwd, options) {
|
|
|
23750
23821
|
return { authed: false, reason: existing.reason };
|
|
23751
23822
|
}
|
|
23752
23823
|
checkSpinner.clear();
|
|
23753
|
-
|
|
23824
|
+
p19.log.info(
|
|
23754
23825
|
"Sign in to Railway to continue. Railway will open your browser or show a device code."
|
|
23755
23826
|
);
|
|
23756
23827
|
const login = await runRailway(runner, ["login"], {
|
|
@@ -23767,7 +23838,7 @@ async function ensureRailwayAuth(runner, cwd, options) {
|
|
|
23767
23838
|
const verified = await checkRailwayAuth(runner, cwd, env);
|
|
23768
23839
|
if (verified.authed) {
|
|
23769
23840
|
verifySpinner.stop(
|
|
23770
|
-
verified.account?.email ? `Signed in to Railway as ${
|
|
23841
|
+
verified.account?.email ? `Signed in to Railway as ${pc5.cyan(verified.account.email)}` : "Signed in to Railway"
|
|
23771
23842
|
);
|
|
23772
23843
|
return verified;
|
|
23773
23844
|
}
|
|
@@ -25637,12 +25708,12 @@ function scaffoldTsconfig(cwd, config) {
|
|
|
25637
25708
|
}
|
|
25638
25709
|
|
|
25639
25710
|
// adapters/next/init/vercel/flow.ts
|
|
25640
|
-
import * as
|
|
25641
|
-
import
|
|
25711
|
+
import * as p23 from "@clack/prompts";
|
|
25712
|
+
import pc9 from "picocolors";
|
|
25642
25713
|
|
|
25643
25714
|
// adapters/next/init/vercel/auth.ts
|
|
25644
|
-
import * as
|
|
25645
|
-
import
|
|
25715
|
+
import * as p20 from "@clack/prompts";
|
|
25716
|
+
import pc6 from "picocolors";
|
|
25646
25717
|
|
|
25647
25718
|
// adapters/next/init/vercel/runner.ts
|
|
25648
25719
|
import { spawn as spawn5 } from "child_process";
|
|
@@ -25883,9 +25954,9 @@ async function ensureVercelAuth(runner, cwd, options) {
|
|
|
25883
25954
|
return { authed: false, reason: existing.reason };
|
|
25884
25955
|
}
|
|
25885
25956
|
checkSpinner.clear();
|
|
25886
|
-
const signInMessage = `Sign in to Vercel to continue ${
|
|
25957
|
+
const signInMessage = `Sign in to Vercel to continue ${pc6.dim("(or press Ctrl-C to enter a connection string manually)")}`;
|
|
25887
25958
|
const signInRows = clackLogRows(signInMessage);
|
|
25888
|
-
|
|
25959
|
+
p20.log.info(signInMessage);
|
|
25889
25960
|
let streamedRows = 0;
|
|
25890
25961
|
const login = await runVercel(runner, ["login"], {
|
|
25891
25962
|
cwd,
|
|
@@ -25913,12 +25984,12 @@ async function ensureVercelAuth(runner, cwd, options) {
|
|
|
25913
25984
|
return { authed: false, reason: "login-failed" };
|
|
25914
25985
|
}
|
|
25915
25986
|
function signedInMessage(username) {
|
|
25916
|
-
return username ? `Signed in to Vercel as ${
|
|
25987
|
+
return username ? `Signed in to Vercel as ${pc6.cyan(username)}` : "Signed in to Vercel";
|
|
25917
25988
|
}
|
|
25918
25989
|
|
|
25919
25990
|
// adapters/next/init/vercel/blob.ts
|
|
25920
|
-
import * as
|
|
25921
|
-
import
|
|
25991
|
+
import * as p21 from "@clack/prompts";
|
|
25992
|
+
import pc7 from "picocolors";
|
|
25922
25993
|
|
|
25923
25994
|
// adapters/next/init/vercel/env-pull.ts
|
|
25924
25995
|
import fs36 from "fs";
|
|
@@ -26159,8 +26230,8 @@ async function provisionBlobStoreInteractive(runner, cwd, options) {
|
|
|
26159
26230
|
if (terminalFallbackRan) break;
|
|
26160
26231
|
terminalFallbackRan = true;
|
|
26161
26232
|
quietSpinner.clear();
|
|
26162
|
-
|
|
26163
|
-
`Create your Blob store in the Vercel prompts below ${
|
|
26233
|
+
p21.log.info(
|
|
26234
|
+
`Create your Blob store in the Vercel prompts below ${pc7.dim(
|
|
26164
26235
|
"(connect it to all environments)."
|
|
26165
26236
|
)}`
|
|
26166
26237
|
);
|
|
@@ -26358,8 +26429,8 @@ function guardEnvLocal(cwd) {
|
|
|
26358
26429
|
}
|
|
26359
26430
|
|
|
26360
26431
|
// adapters/next/init/vercel/neon.ts
|
|
26361
|
-
import * as
|
|
26362
|
-
import
|
|
26432
|
+
import * as p22 from "@clack/prompts";
|
|
26433
|
+
import pc8 from "picocolors";
|
|
26363
26434
|
var PROVISION_TIMEOUT_MS2 = 18e4;
|
|
26364
26435
|
var INTERACTIVE_PROVISION_TIMEOUT_MS2 = 6e5;
|
|
26365
26436
|
var ACCEPT_TERMS_URL_PATTERN = /https:\/\/vercel\.com\/\S+\/integrations\/accept-terms\/\S+/i;
|
|
@@ -26377,7 +26448,7 @@ async function provisionNeonInteractive(runner, cwd, options) {
|
|
|
26377
26448
|
termsResolved = true;
|
|
26378
26449
|
quietSpinner.clear();
|
|
26379
26450
|
eraseRows(termsNotice.rows);
|
|
26380
|
-
|
|
26451
|
+
p22.log.step(termsNotice.text);
|
|
26381
26452
|
};
|
|
26382
26453
|
const quiet = await runVercel(runner, neonAddArgs(options), {
|
|
26383
26454
|
cwd,
|
|
@@ -26392,11 +26463,11 @@ async function provisionNeonInteractive(runner, cwd, options) {
|
|
|
26392
26463
|
const message = "Please accept the Neon terms in your browser";
|
|
26393
26464
|
termsNotice = {
|
|
26394
26465
|
text: `${message}
|
|
26395
|
-
${
|
|
26466
|
+
${pc8.dim(termsUrl)}`,
|
|
26396
26467
|
rows: clackLogRows(message) + clackLogRows(termsUrl) - 1
|
|
26397
26468
|
};
|
|
26398
26469
|
quietSpinner.clear();
|
|
26399
|
-
|
|
26470
|
+
p22.log.info(termsNotice.text);
|
|
26400
26471
|
quietSpinner.start("Waiting for the terms to be accepted");
|
|
26401
26472
|
return;
|
|
26402
26473
|
}
|
|
@@ -26412,8 +26483,8 @@ ${pc7.dim(termsUrl)}`,
|
|
|
26412
26483
|
return readNeonProvisionInfo(quiet.stdout);
|
|
26413
26484
|
}
|
|
26414
26485
|
quietSpinner.clear();
|
|
26415
|
-
|
|
26416
|
-
`Create your Neon database in the Vercel prompts below ${
|
|
26486
|
+
p22.log.info(
|
|
26487
|
+
`Create your Neon database in the Vercel prompts below ${pc8.dim(
|
|
26417
26488
|
"(the Free plan is recommended)."
|
|
26418
26489
|
)}`
|
|
26419
26490
|
);
|
|
@@ -26489,14 +26560,14 @@ async function runVercelNeonFlow(options) {
|
|
|
26489
26560
|
allowLogin: options.interactive
|
|
26490
26561
|
});
|
|
26491
26562
|
if (!auth.authed) {
|
|
26492
|
-
|
|
26563
|
+
p23.log.warn(authFailureMessage(auth.reason));
|
|
26493
26564
|
return { ok: false };
|
|
26494
26565
|
}
|
|
26495
26566
|
await ensureLinkedProject(runner, options.cwd, options.projectName, env, auth.username);
|
|
26496
26567
|
const neon = await provisionNeonForMode(runner, options);
|
|
26497
26568
|
if (neon.failure) {
|
|
26498
|
-
|
|
26499
|
-
if (neon.detail)
|
|
26569
|
+
p23.log.warn(neonFailureMessage(neon.failure));
|
|
26570
|
+
if (neon.detail) p23.log.message(pc9.dim(redactSecrets(neon.detail)));
|
|
26500
26571
|
return { ok: false };
|
|
26501
26572
|
}
|
|
26502
26573
|
const databaseUrl = await pullNeonDatabaseUrl(runner, options.cwd, env);
|
|
@@ -26509,7 +26580,7 @@ async function runVercelNeonFlow(options) {
|
|
|
26509
26580
|
dismissSignedInNote
|
|
26510
26581
|
};
|
|
26511
26582
|
} catch (error) {
|
|
26512
|
-
|
|
26583
|
+
p23.log.warn(
|
|
26513
26584
|
`Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
|
|
26514
26585
|
);
|
|
26515
26586
|
return { ok: false };
|
|
@@ -26531,14 +26602,14 @@ async function runVercelBlobFlow(options) {
|
|
|
26531
26602
|
allowLogin: options.interactive
|
|
26532
26603
|
});
|
|
26533
26604
|
if (!auth.authed) {
|
|
26534
|
-
|
|
26605
|
+
p23.log.warn(authFailureMessage(auth.reason));
|
|
26535
26606
|
return { ok: false };
|
|
26536
26607
|
}
|
|
26537
26608
|
await ensureLinkedProject(runner, options.cwd, options.projectName, env, auth.username);
|
|
26538
26609
|
const blob = await provisionBlobForMode(runner, options);
|
|
26539
26610
|
if (blob.failure || !blob.token) {
|
|
26540
|
-
|
|
26541
|
-
if (blob.detail)
|
|
26611
|
+
p23.log.warn(blobFailureMessage(blob.failure));
|
|
26612
|
+
if (blob.detail) p23.log.message(pc9.dim(redactSecrets(blob.detail)));
|
|
26542
26613
|
return { ok: false };
|
|
26543
26614
|
}
|
|
26544
26615
|
return {
|
|
@@ -26547,7 +26618,7 @@ async function runVercelBlobFlow(options) {
|
|
|
26547
26618
|
blobStoreName: blob.storeName
|
|
26548
26619
|
};
|
|
26549
26620
|
} catch (error) {
|
|
26550
|
-
|
|
26621
|
+
p23.log.warn(
|
|
26551
26622
|
`Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
|
|
26552
26623
|
);
|
|
26553
26624
|
return { ok: false };
|
|
@@ -26569,7 +26640,7 @@ async function runVercelDeployFlow(options) {
|
|
|
26569
26640
|
allowLogin: options.interactive ?? true
|
|
26570
26641
|
});
|
|
26571
26642
|
if (!auth.authed) {
|
|
26572
|
-
|
|
26643
|
+
p23.log.warn(authFailureMessage(auth.reason));
|
|
26573
26644
|
printManualDeployHint();
|
|
26574
26645
|
return { ok: false };
|
|
26575
26646
|
}
|
|
@@ -26579,8 +26650,8 @@ async function runVercelDeployFlow(options) {
|
|
|
26579
26650
|
const sync = await syncVercelProductionEnv(runner, options.cwd, env);
|
|
26580
26651
|
envSpinner.clear();
|
|
26581
26652
|
for (const key of sync.failed) {
|
|
26582
|
-
|
|
26583
|
-
`Could not set ${
|
|
26653
|
+
p23.log.warn(
|
|
26654
|
+
`Could not set ${pc9.cyan(key)} on Vercel \u2014 add it in the project's environment settings.`
|
|
26584
26655
|
);
|
|
26585
26656
|
}
|
|
26586
26657
|
ensureVercelJsonFramework(options.cwd);
|
|
@@ -26589,13 +26660,13 @@ async function runVercelDeployFlow(options) {
|
|
|
26589
26660
|
const packageGuard = await guardProjectForDeploy(options.cwd);
|
|
26590
26661
|
guardSpinner.clear();
|
|
26591
26662
|
if (packageGuard.lockfile === "failed") {
|
|
26592
|
-
|
|
26663
|
+
p23.log.warn(
|
|
26593
26664
|
"Could not refresh pnpm-lock.yaml after removing betterstart-cli \u2014 the remote install may fail."
|
|
26594
26665
|
);
|
|
26595
26666
|
}
|
|
26596
26667
|
for (const dep of packageGuard.localSpecDeps) {
|
|
26597
|
-
|
|
26598
|
-
`Dependency ${
|
|
26668
|
+
p23.log.warn(
|
|
26669
|
+
`Dependency ${pc9.cyan(dep)} uses a local spec that cannot install on Vercel \u2014 the remote build may fail.`
|
|
26599
26670
|
);
|
|
26600
26671
|
}
|
|
26601
26672
|
const deploySpinner = spinner2();
|
|
@@ -26610,16 +26681,16 @@ async function runVercelDeployFlow(options) {
|
|
|
26610
26681
|
packageGuard.restore();
|
|
26611
26682
|
}
|
|
26612
26683
|
if (deploy.failure) {
|
|
26613
|
-
deploySpinner.stop(`${
|
|
26614
|
-
if (deploy.detail)
|
|
26684
|
+
deploySpinner.stop(`${pc9.yellow("\u25B2")} ${deployFailureMessage(deploy.failure)}`);
|
|
26685
|
+
if (deploy.detail) p23.log.message(pc9.dim(redactSecrets(deploy.detail)));
|
|
26615
26686
|
printManualDeployHint();
|
|
26616
26687
|
return { ok: false };
|
|
26617
26688
|
}
|
|
26618
26689
|
const url = deploy.url;
|
|
26619
|
-
deploySpinner.stop(url ? `Deployed ${
|
|
26690
|
+
deploySpinner.stop(url ? `Deployed ${pc9.cyan(url)}` : "Deployed to Vercel");
|
|
26620
26691
|
return { ok: true, url, syncedEnvKeys: sync.synced };
|
|
26621
26692
|
} catch (error) {
|
|
26622
|
-
|
|
26693
|
+
p23.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
26623
26694
|
printManualDeployHint();
|
|
26624
26695
|
return { ok: false };
|
|
26625
26696
|
} finally {
|
|
@@ -26627,12 +26698,12 @@ async function runVercelDeployFlow(options) {
|
|
|
26627
26698
|
}
|
|
26628
26699
|
}
|
|
26629
26700
|
function printManualDeployHint() {
|
|
26630
|
-
|
|
26701
|
+
p23.log.info(`You can deploy manually: ${pc9.cyan("vercel deploy --prod")}`);
|
|
26631
26702
|
}
|
|
26632
26703
|
async function ensureLinkedProject(runner, cwd, projectName, env, scope) {
|
|
26633
26704
|
if (readLinkedProjectId(cwd)) return;
|
|
26634
26705
|
const projectSpinner = spinner2();
|
|
26635
|
-
projectSpinner.start(`Creating a Vercel project ${
|
|
26706
|
+
projectSpinner.start(`Creating a Vercel project ${pc9.cyan(projectName)}`);
|
|
26636
26707
|
const project2 = await createVercelProject(runner, cwd, projectName, { env, scope });
|
|
26637
26708
|
projectSpinner.clear();
|
|
26638
26709
|
if (!project2.linked || !project2.projectId) {
|
|
@@ -26644,7 +26715,7 @@ async function pullNeonDatabaseUrl(runner, cwd, env) {
|
|
|
26644
26715
|
neonSpinner.start("Retrieving DATABASE_URL from Vercel");
|
|
26645
26716
|
const databaseUrl = await pullVercelEnvValue(runner, cwd, readDbUrlFromDotenv, env);
|
|
26646
26717
|
if (!databaseUrl) {
|
|
26647
|
-
neonSpinner.stop(`${
|
|
26718
|
+
neonSpinner.stop(`${pc9.yellow("\u25B2")} No DATABASE_URL came back from Vercel`);
|
|
26648
26719
|
return void 0;
|
|
26649
26720
|
}
|
|
26650
26721
|
neonSpinner.clear();
|
|
@@ -26704,12 +26775,19 @@ function neonFailureMessage(reason) {
|
|
|
26704
26775
|
}
|
|
26705
26776
|
|
|
26706
26777
|
// adapters/next/commands/init.ts
|
|
26707
|
-
import
|
|
26778
|
+
import pc10 from "picocolors";
|
|
26708
26779
|
|
|
26709
26780
|
// adapters/next/commands/seed.ts
|
|
26710
26781
|
import fs40 from "fs";
|
|
26711
26782
|
import path51 from "path";
|
|
26712
|
-
import * as
|
|
26783
|
+
import * as clack2 from "@clack/prompts";
|
|
26784
|
+
|
|
26785
|
+
// core-engine/utils/interactive.ts
|
|
26786
|
+
function isInteractiveSession4() {
|
|
26787
|
+
return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
|
|
26788
|
+
}
|
|
26789
|
+
|
|
26790
|
+
// adapters/next/commands/seed.ts
|
|
26713
26791
|
function buildSeedScript(authBasePath = "/api/admin/auth") {
|
|
26714
26792
|
return `/**
|
|
26715
26793
|
* BetterStart Admin \u2014 Seed Script
|
|
@@ -26882,44 +26960,80 @@ main().catch((err) => {
|
|
|
26882
26960
|
}
|
|
26883
26961
|
async function runSeedCommand(options) {
|
|
26884
26962
|
const cwd = options.cwd ? path51.resolve(options.cwd) : process.cwd();
|
|
26885
|
-
|
|
26963
|
+
clack2.intro("BetterStart Seed");
|
|
26886
26964
|
let config;
|
|
26887
26965
|
try {
|
|
26888
26966
|
config = await resolveConfig(cwd);
|
|
26889
26967
|
} catch (err) {
|
|
26890
|
-
|
|
26968
|
+
clack2.cancel(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
|
|
26891
26969
|
process.exit(1);
|
|
26892
26970
|
}
|
|
26893
26971
|
const adminDir = config.paths?.admin ?? "./admin";
|
|
26894
|
-
const
|
|
26895
|
-
|
|
26896
|
-
|
|
26897
|
-
|
|
26898
|
-
|
|
26972
|
+
const interactive = isInteractiveSession4();
|
|
26973
|
+
let email;
|
|
26974
|
+
if (options.email) {
|
|
26975
|
+
if (!options.email.includes("@")) {
|
|
26976
|
+
clack2.log.error(`Invalid email "${options.email}".`);
|
|
26977
|
+
process.exit(1);
|
|
26899
26978
|
}
|
|
26900
|
-
|
|
26901
|
-
if (
|
|
26902
|
-
|
|
26903
|
-
|
|
26979
|
+
email = options.email;
|
|
26980
|
+
} else if (!interactive) {
|
|
26981
|
+
clack2.log.error("Provide --email to run seed without a terminal.");
|
|
26982
|
+
process.exit(1);
|
|
26983
|
+
} else {
|
|
26984
|
+
const emailInput = await clack2.text({
|
|
26985
|
+
message: "Admin email",
|
|
26986
|
+
placeholder: "admin@example.com",
|
|
26987
|
+
validate: (v) => {
|
|
26988
|
+
if (!v || !v.includes("@")) return "Please enter a valid email";
|
|
26989
|
+
}
|
|
26990
|
+
});
|
|
26991
|
+
if (clack2.isCancel(emailInput)) {
|
|
26992
|
+
clack2.cancel("Cancelled.");
|
|
26993
|
+
return;
|
|
26994
|
+
}
|
|
26995
|
+
email = emailInput;
|
|
26904
26996
|
}
|
|
26905
|
-
const
|
|
26906
|
-
|
|
26907
|
-
|
|
26908
|
-
|
|
26997
|
+
const envPassword = process.env.BETTERSTART_ADMIN_PASSWORD;
|
|
26998
|
+
let password4;
|
|
26999
|
+
if (envPassword) {
|
|
27000
|
+
if (envPassword.length < 8) {
|
|
27001
|
+
clack2.log.error("BETTERSTART_ADMIN_PASSWORD must be at least 8 characters.");
|
|
27002
|
+
process.exit(1);
|
|
26909
27003
|
}
|
|
26910
|
-
|
|
26911
|
-
if (
|
|
26912
|
-
|
|
26913
|
-
|
|
27004
|
+
password4 = envPassword;
|
|
27005
|
+
} else if (!interactive) {
|
|
27006
|
+
clack2.log.error("Set BETTERSTART_ADMIN_PASSWORD to run seed without a terminal.");
|
|
27007
|
+
process.exit(1);
|
|
27008
|
+
} else {
|
|
27009
|
+
const passwordInput = await clack2.password({
|
|
27010
|
+
message: "Admin password",
|
|
27011
|
+
validate: (v) => {
|
|
27012
|
+
if (!v || v.length < 8) return "Password must be at least 8 characters";
|
|
27013
|
+
}
|
|
27014
|
+
});
|
|
27015
|
+
if (clack2.isCancel(passwordInput)) {
|
|
27016
|
+
clack2.cancel("Cancelled.");
|
|
27017
|
+
return;
|
|
27018
|
+
}
|
|
27019
|
+
password4 = passwordInput;
|
|
26914
27020
|
}
|
|
26915
|
-
|
|
26916
|
-
|
|
26917
|
-
|
|
26918
|
-
|
|
26919
|
-
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
27021
|
+
let name;
|
|
27022
|
+
if (options.name) {
|
|
27023
|
+
name = options.name;
|
|
27024
|
+
} else if (!interactive) {
|
|
27025
|
+
name = "Admin";
|
|
27026
|
+
} else {
|
|
27027
|
+
const nameInput = await clack2.text({
|
|
27028
|
+
message: "Admin name",
|
|
27029
|
+
placeholder: "Admin",
|
|
27030
|
+
defaultValue: "Admin"
|
|
27031
|
+
});
|
|
27032
|
+
if (clack2.isCancel(nameInput)) {
|
|
27033
|
+
clack2.cancel("Cancelled.");
|
|
27034
|
+
return;
|
|
27035
|
+
}
|
|
27036
|
+
name = nameInput;
|
|
26923
27037
|
}
|
|
26924
27038
|
const scriptsDir = path51.join(cwd, adminDir, "scripts");
|
|
26925
27039
|
const seedPath = path51.join(scriptsDir, "seed.ts");
|
|
@@ -26962,11 +27076,21 @@ async function runSeedCommand(options) {
|
|
|
26962
27076
|
if (result.code === 2) {
|
|
26963
27077
|
const existingName = result.stdout.split("\n").find((l) => l.startsWith("EXISTING_USER:"))?.replace("EXISTING_USER:", "")?.trim() || "unknown";
|
|
26964
27078
|
seedSpinner.stop(`Account already exists for ${email}`);
|
|
26965
|
-
|
|
27079
|
+
if (!interactive) {
|
|
27080
|
+
clack2.log.error(
|
|
27081
|
+
`An admin account (${existingName}) already exists for ${email}. Replacing it needs a terminal.`
|
|
27082
|
+
);
|
|
27083
|
+
try {
|
|
27084
|
+
fs40.unlinkSync(seedPath);
|
|
27085
|
+
} catch {
|
|
27086
|
+
}
|
|
27087
|
+
process.exit(1);
|
|
27088
|
+
}
|
|
27089
|
+
const overwrite = await clack2.confirm({
|
|
26966
27090
|
message: `An admin account (${existingName}) already exists with this email. Replace it?`
|
|
26967
27091
|
});
|
|
26968
|
-
if (
|
|
26969
|
-
|
|
27092
|
+
if (clack2.isCancel(overwrite) || !overwrite) {
|
|
27093
|
+
clack2.cancel("Seed cancelled.");
|
|
26970
27094
|
try {
|
|
26971
27095
|
fs40.unlinkSync(seedPath);
|
|
26972
27096
|
} catch {
|
|
@@ -26982,12 +27106,12 @@ async function runSeedCommand(options) {
|
|
|
26982
27106
|
} catch (err) {
|
|
26983
27107
|
seedSpinner.stop("Failed to create admin user");
|
|
26984
27108
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26985
|
-
|
|
26986
|
-
|
|
26987
|
-
|
|
27109
|
+
clack2.log.error(errMsg);
|
|
27110
|
+
clack2.log.info("You can run the seed script manually:");
|
|
27111
|
+
clack2.log.info(
|
|
26988
27112
|
` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${path51.relative(cwd, seedPath)}`
|
|
26989
27113
|
);
|
|
26990
|
-
|
|
27114
|
+
clack2.outro("");
|
|
26991
27115
|
process.exit(1);
|
|
26992
27116
|
}
|
|
26993
27117
|
try {
|
|
@@ -26997,7 +27121,7 @@ async function runSeedCommand(options) {
|
|
|
26997
27121
|
}
|
|
26998
27122
|
} catch {
|
|
26999
27123
|
}
|
|
27000
|
-
|
|
27124
|
+
clack2.outro(`Admin user ready: ${email}`);
|
|
27001
27125
|
}
|
|
27002
27126
|
|
|
27003
27127
|
// adapters/next/commands/init.ts
|
|
@@ -27010,16 +27134,16 @@ function addVersionToBoxBottomBorder(box2, version2) {
|
|
|
27010
27134
|
if (!bottomLine) {
|
|
27011
27135
|
return box2;
|
|
27012
27136
|
}
|
|
27013
|
-
const leftCornerIndex = bottomLine.indexOf(
|
|
27014
|
-
const rightCornerIndex = bottomLine.lastIndexOf(
|
|
27137
|
+
const leftCornerIndex = bottomLine.indexOf(p24.S_CORNER_BOTTOM_LEFT);
|
|
27138
|
+
const rightCornerIndex = bottomLine.lastIndexOf(p24.S_CORNER_BOTTOM_RIGHT);
|
|
27015
27139
|
const label = ` v${version2} `;
|
|
27016
|
-
const borderWidth = rightCornerIndex - leftCornerIndex -
|
|
27140
|
+
const borderWidth = rightCornerIndex - leftCornerIndex - p24.S_CORNER_BOTTOM_LEFT.length;
|
|
27017
27141
|
if (leftCornerIndex === -1 || rightCornerIndex === -1 || borderWidth < label.length) {
|
|
27018
27142
|
return box2;
|
|
27019
27143
|
}
|
|
27020
27144
|
const leftBorderWidth = Math.floor((borderWidth - label.length) / 2);
|
|
27021
27145
|
const rightBorderWidth = borderWidth - label.length - leftBorderWidth;
|
|
27022
|
-
lines[bottomLineIndex] = `${bottomLine.slice(0, leftCornerIndex +
|
|
27146
|
+
lines[bottomLineIndex] = `${bottomLine.slice(0, leftCornerIndex + p24.S_CORNER_BOTTOM_LEFT.length)}${p24.S_BAR_H.repeat(leftBorderWidth)}${label}${p24.S_BAR_H.repeat(rightBorderWidth)}${bottomLine.slice(rightCornerIndex)}`;
|
|
27023
27147
|
return lines.join("\n");
|
|
27024
27148
|
}
|
|
27025
27149
|
function renderInitBanner() {
|
|
@@ -27029,7 +27153,7 @@ function renderInitBanner() {
|
|
|
27029
27153
|
output.on("data", (chunk) => {
|
|
27030
27154
|
box2 += chunk.toString();
|
|
27031
27155
|
});
|
|
27032
|
-
|
|
27156
|
+
p24.box(
|
|
27033
27157
|
`
|
|
27034
27158
|
\u2584 \u2597 \u2597 \u2584\u2596\u2597 \u2597
|
|
27035
27159
|
\u2599\u2598\u2588\u258C\u259C\u2598\u259C\u2598\u2588\u258C\u259B\u2598\u259A \u259C\u2598\u2580\u258C\u259B\u2598\u259C\u2598
|
|
@@ -27122,6 +27246,14 @@ function removeExistingAdminPaths(cwd, namespaces) {
|
|
|
27122
27246
|
return removed;
|
|
27123
27247
|
}
|
|
27124
27248
|
async function runInitCommand(name, options) {
|
|
27249
|
+
let restoreStdout;
|
|
27250
|
+
if (options.json) {
|
|
27251
|
+
if (!options.yes) {
|
|
27252
|
+
p24.log.error("--json requires --yes.");
|
|
27253
|
+
process.exit(1);
|
|
27254
|
+
}
|
|
27255
|
+
restoreStdout = redirectStdoutToStderr();
|
|
27256
|
+
}
|
|
27125
27257
|
installPromptCheckmarks();
|
|
27126
27258
|
const disposeCancelGuard = installSetupCancelGuard();
|
|
27127
27259
|
renderInitBanner();
|
|
@@ -27138,7 +27270,7 @@ async function runInitCommand(name, options) {
|
|
|
27138
27270
|
}
|
|
27139
27271
|
} catch (error) {
|
|
27140
27272
|
disposeCancelGuard();
|
|
27141
|
-
|
|
27273
|
+
p24.log.error(error instanceof Error ? error.message : String(error));
|
|
27142
27274
|
process.exit(1);
|
|
27143
27275
|
}
|
|
27144
27276
|
let cwd = process.cwd();
|
|
@@ -27149,7 +27281,7 @@ async function runInitCommand(name, options) {
|
|
|
27149
27281
|
try {
|
|
27150
27282
|
namespace = validateAdminNamespace(options.namespace);
|
|
27151
27283
|
} catch (error) {
|
|
27152
|
-
|
|
27284
|
+
p24.log.error(error instanceof Error ? error.message : String(error));
|
|
27153
27285
|
process.exit(1);
|
|
27154
27286
|
}
|
|
27155
27287
|
}
|
|
@@ -27158,13 +27290,13 @@ async function runInitCommand(name, options) {
|
|
|
27158
27290
|
try {
|
|
27159
27291
|
projectPrompt = options.yes ? resolveNonInteractiveProject(name) : await promptProject(name);
|
|
27160
27292
|
} catch (error) {
|
|
27161
|
-
|
|
27293
|
+
p24.log.error(error instanceof Error ? error.message : String(error));
|
|
27162
27294
|
process.exit(1);
|
|
27163
27295
|
}
|
|
27164
27296
|
}
|
|
27165
27297
|
if (!options.yes && !options.namespace) {
|
|
27166
27298
|
const defaultDashboardPath = resolveAdminNamespace(DEFAULT_ADMIN_NAMESPACE).routePath;
|
|
27167
|
-
const namespaceInput = await
|
|
27299
|
+
const namespaceInput = await p24.text({
|
|
27168
27300
|
message: "Enter the dashboard path",
|
|
27169
27301
|
placeholder: `eg. ${defaultDashboardPath}`,
|
|
27170
27302
|
defaultValue: defaultDashboardPath,
|
|
@@ -27177,8 +27309,8 @@ async function runInitCommand(name, options) {
|
|
|
27177
27309
|
}
|
|
27178
27310
|
}
|
|
27179
27311
|
});
|
|
27180
|
-
if (
|
|
27181
|
-
|
|
27312
|
+
if (p24.isCancel(namespaceInput)) {
|
|
27313
|
+
p24.cancel("Setup cancelled.");
|
|
27182
27314
|
process.exit(0);
|
|
27183
27315
|
}
|
|
27184
27316
|
namespace = validateAdminDashboardPath(namespaceInput);
|
|
@@ -27190,51 +27322,52 @@ async function runInitCommand(name, options) {
|
|
|
27190
27322
|
if (project2.isExisting) {
|
|
27191
27323
|
srcDir = project2.hasSrcDir;
|
|
27192
27324
|
if (!project2.hasTypeScript) {
|
|
27193
|
-
|
|
27325
|
+
p24.log.error("TypeScript is required. Please add a tsconfig.json first.");
|
|
27194
27326
|
process.exit(1);
|
|
27195
27327
|
}
|
|
27196
27328
|
if (forceMode) {
|
|
27197
27329
|
const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
|
|
27198
27330
|
if (nuked > 0) {
|
|
27199
|
-
|
|
27331
|
+
p24.log.warn(`${pc10.yellow("Force mode:")} removed ${nuked} existing admin paths`);
|
|
27200
27332
|
}
|
|
27201
27333
|
project2 = detectProject(cwd, namespace);
|
|
27202
27334
|
} else if (project2.conflicts.length > 0) {
|
|
27203
|
-
const conflictLines = project2.conflicts.map((c) => `${
|
|
27335
|
+
const conflictLines = project2.conflicts.map((c) => `${pc10.yellow("\u25B2")} ${c}`);
|
|
27204
27336
|
conflictLines.push(
|
|
27205
27337
|
"",
|
|
27206
|
-
|
|
27338
|
+
pc10.dim(`Use ${pc10.bold("--force")} to remove existing admin files before scaffolding.`)
|
|
27207
27339
|
);
|
|
27208
|
-
|
|
27209
|
-
if (
|
|
27210
|
-
|
|
27211
|
-
|
|
27212
|
-
`Continue with ${pc9.bold(pc9.cyan("--force"))}?`,
|
|
27213
|
-
`${pc9.cyan("\u2502")} ${pc9.dim("This will force overwrite the existing admin code.")}`,
|
|
27214
|
-
pc9.cyan("\u2502")
|
|
27215
|
-
].join("\n"),
|
|
27216
|
-
initialValue: true
|
|
27217
|
-
});
|
|
27218
|
-
if (p22.isCancel(proceed) || !proceed) {
|
|
27219
|
-
p22.cancel("Setup cancelled.");
|
|
27220
|
-
process.exit(0);
|
|
27221
|
-
}
|
|
27222
|
-
forceMode = true;
|
|
27223
|
-
const nuked = removeExistingAdminPaths(
|
|
27224
|
-
cwd,
|
|
27225
|
-
await resolveForceInitNamespaces(cwd, namespace)
|
|
27340
|
+
p24.note(conflictLines.join("\n"), pc10.yellow("Conflicts"));
|
|
27341
|
+
if (options.yes) {
|
|
27342
|
+
p24.log.error(
|
|
27343
|
+
"Can't continue with --yes while admin files conflict. Re-run with --force to remove them first."
|
|
27226
27344
|
);
|
|
27227
|
-
|
|
27228
|
-
p22.log.warn(`${pc9.yellow("Force mode:")} removed ${nuked} existing admin paths`);
|
|
27229
|
-
}
|
|
27230
|
-
project2 = detectProject(cwd, namespace);
|
|
27345
|
+
process.exit(1);
|
|
27231
27346
|
}
|
|
27347
|
+
const proceed = await p24.confirm({
|
|
27348
|
+
message: [
|
|
27349
|
+
`Continue with ${pc10.bold(pc10.cyan("--force"))}?`,
|
|
27350
|
+
`${pc10.cyan("\u2502")} ${pc10.dim("This will force overwrite the existing admin code.")}`,
|
|
27351
|
+
pc10.cyan("\u2502")
|
|
27352
|
+
].join("\n"),
|
|
27353
|
+
initialValue: true
|
|
27354
|
+
});
|
|
27355
|
+
if (p24.isCancel(proceed) || !proceed) {
|
|
27356
|
+
p24.cancel("Setup cancelled.");
|
|
27357
|
+
process.exit(0);
|
|
27358
|
+
}
|
|
27359
|
+
forceMode = true;
|
|
27360
|
+
const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
|
|
27361
|
+
if (nuked > 0) {
|
|
27362
|
+
p24.log.warn(`${pc10.yellow("Force mode:")} removed ${nuked} existing admin paths`);
|
|
27363
|
+
}
|
|
27364
|
+
project2 = detectProject(cwd, namespace);
|
|
27232
27365
|
}
|
|
27233
27366
|
} else {
|
|
27234
27367
|
const freshProject = projectPrompt;
|
|
27235
27368
|
srcDir = false;
|
|
27236
27369
|
if (!options.yes) {
|
|
27237
|
-
const pmChoice = await
|
|
27370
|
+
const pmChoice = await p24.select({
|
|
27238
27371
|
message: "Which package manager do you want to use?",
|
|
27239
27372
|
options: [
|
|
27240
27373
|
{ value: "pnpm", label: "pnpm", hint: "recommended" },
|
|
@@ -27242,8 +27375,8 @@ async function runInitCommand(name, options) {
|
|
|
27242
27375
|
{ value: "bun", label: "bun" }
|
|
27243
27376
|
]
|
|
27244
27377
|
});
|
|
27245
|
-
if (
|
|
27246
|
-
|
|
27378
|
+
if (p24.isCancel(pmChoice)) {
|
|
27379
|
+
p24.cancel("Setup cancelled.");
|
|
27247
27380
|
process.exit(0);
|
|
27248
27381
|
}
|
|
27249
27382
|
pm = pmChoice;
|
|
@@ -27278,11 +27411,11 @@ async function runInitCommand(name, options) {
|
|
|
27278
27411
|
process.stderr.write(`${createNextAppResult.output.trimEnd()}
|
|
27279
27412
|
`);
|
|
27280
27413
|
}
|
|
27281
|
-
|
|
27282
|
-
|
|
27414
|
+
p24.log.error(createNextAppResult.error);
|
|
27415
|
+
p24.log.info(
|
|
27283
27416
|
`You can create the project manually:
|
|
27284
|
-
${
|
|
27285
|
-
Then run ${
|
|
27417
|
+
${pc10.cyan(`npx create-next-app@latest ${freshProject.projectName} --typescript --tailwind --app`)}
|
|
27418
|
+
Then run ${pc10.cyan("betterstart init")} inside it.`
|
|
27286
27419
|
);
|
|
27287
27420
|
process.exit(1);
|
|
27288
27421
|
}
|
|
@@ -27293,14 +27426,14 @@ async function runInitCommand(name, options) {
|
|
|
27293
27426
|
);
|
|
27294
27427
|
if (!hasPackageJson || !hasNextConfig) {
|
|
27295
27428
|
createNextAppSpinner.stop(`Failed to create Next.js app: ${displayName}`);
|
|
27296
|
-
|
|
27429
|
+
p24.log.error(
|
|
27297
27430
|
"create-next-app completed but the project was not created. This can happen with nested npx calls."
|
|
27298
27431
|
);
|
|
27299
27432
|
const manualCmd = `npx create-next-app@latest ${freshProject.projectName} --typescript --tailwind --app`;
|
|
27300
|
-
|
|
27433
|
+
p24.log.info(
|
|
27301
27434
|
`Create the project manually:
|
|
27302
|
-
${
|
|
27303
|
-
Then run ${
|
|
27435
|
+
${pc10.cyan(manualCmd)}
|
|
27436
|
+
Then run ${pc10.cyan("betterstart init")} inside it.`
|
|
27304
27437
|
);
|
|
27305
27438
|
process.exit(1);
|
|
27306
27439
|
}
|
|
@@ -27346,22 +27479,22 @@ async function runInitCommand(name, options) {
|
|
|
27346
27479
|
try {
|
|
27347
27480
|
validateResolvedDatabaseProvider(databaseProvider, options);
|
|
27348
27481
|
} catch (error) {
|
|
27349
|
-
|
|
27482
|
+
p24.log.error(error instanceof Error ? error.message : String(error));
|
|
27350
27483
|
process.exit(1);
|
|
27351
27484
|
}
|
|
27352
27485
|
if (databaseProvider === "manual") {
|
|
27353
27486
|
const candidate = options.databaseUrl ?? existingDbUrl ?? promptedManualUrl;
|
|
27354
27487
|
if (candidate && !isValidDbUrl(candidate)) {
|
|
27355
|
-
|
|
27356
|
-
`Invalid database URL. Must start with ${
|
|
27488
|
+
p24.log.error(
|
|
27489
|
+
`Invalid database URL. Must start with ${pc10.cyan("postgres://")} or ${pc10.cyan("postgresql://")}`
|
|
27357
27490
|
);
|
|
27358
27491
|
process.exit(1);
|
|
27359
27492
|
}
|
|
27360
27493
|
if (candidate) {
|
|
27361
27494
|
databaseUrl = candidate;
|
|
27362
27495
|
if (existingDbUrl === candidate && !options.databaseUrl) {
|
|
27363
|
-
|
|
27364
|
-
`Using the existing DATABASE_URL from .env.local ${
|
|
27496
|
+
p24.log.info(
|
|
27497
|
+
`Using the existing DATABASE_URL from .env.local ${pc10.dim(`(${maskDbUrl(candidate)})`)}`
|
|
27365
27498
|
);
|
|
27366
27499
|
}
|
|
27367
27500
|
} else if (!options.yes) {
|
|
@@ -27380,7 +27513,7 @@ async function runInitCommand(name, options) {
|
|
|
27380
27513
|
persistDatabaseUrl(cwd, databaseUrl);
|
|
27381
27514
|
dismissVercelSignedInNote = flow.dismissSignedInNote;
|
|
27382
27515
|
} else if (options.yes) {
|
|
27383
|
-
|
|
27516
|
+
p24.log.error(
|
|
27384
27517
|
flow.ok ? "Created a Neon database, but DATABASE_URL could not be retrieved from Vercel." : "Vercel database provisioning did not complete."
|
|
27385
27518
|
);
|
|
27386
27519
|
process.exit(1);
|
|
@@ -27389,7 +27522,7 @@ async function runInitCommand(name, options) {
|
|
|
27389
27522
|
databaseUrl = await promptConnectionString();
|
|
27390
27523
|
persistDatabaseUrl(cwd, databaseUrl);
|
|
27391
27524
|
} else {
|
|
27392
|
-
|
|
27525
|
+
p24.log.info("Falling back to a manual database connection string.");
|
|
27393
27526
|
openBrowserVercelNeon();
|
|
27394
27527
|
databaseUrl = await promptConnectionString();
|
|
27395
27528
|
}
|
|
@@ -27403,11 +27536,11 @@ async function runInitCommand(name, options) {
|
|
|
27403
27536
|
} catch (error) {
|
|
27404
27537
|
const message = error instanceof Error ? error.message : String(error);
|
|
27405
27538
|
if (options.yes) {
|
|
27406
|
-
|
|
27539
|
+
p24.log.error(`Railway database provisioning failed: ${message}`);
|
|
27407
27540
|
process.exit(1);
|
|
27408
27541
|
}
|
|
27409
|
-
|
|
27410
|
-
|
|
27542
|
+
p24.log.warn(`Railway database provisioning failed: ${message}`);
|
|
27543
|
+
p24.log.info("Falling back to a manual database connection string.");
|
|
27411
27544
|
databaseUrl = await promptConnectionString();
|
|
27412
27545
|
}
|
|
27413
27546
|
}
|
|
@@ -27437,7 +27570,7 @@ async function runInitCommand(name, options) {
|
|
|
27437
27570
|
return { ok: true, config: config2 };
|
|
27438
27571
|
} catch (error) {
|
|
27439
27572
|
const message = error instanceof Error ? error.message : String(error);
|
|
27440
|
-
|
|
27573
|
+
p24.log.warn(`Railway bucket provisioning failed: ${message}`);
|
|
27441
27574
|
return { ok: false };
|
|
27442
27575
|
}
|
|
27443
27576
|
};
|
|
@@ -27451,7 +27584,7 @@ async function runInitCommand(name, options) {
|
|
|
27451
27584
|
if (storage === "r2") {
|
|
27452
27585
|
const missingR2Keys = R2_ENV_KEYS.filter((key) => !readEnvVar(cwd, key)?.trim());
|
|
27453
27586
|
if (options.yes && missingR2Keys.length > 0) {
|
|
27454
|
-
|
|
27587
|
+
p24.log.error(
|
|
27455
27588
|
`Cloudflare R2 is missing required environment variables: ${missingR2Keys.join(", ")}.`
|
|
27456
27589
|
);
|
|
27457
27590
|
process.exit(1);
|
|
@@ -27465,7 +27598,7 @@ async function runInitCommand(name, options) {
|
|
|
27465
27598
|
if (flow.ok && flow.config) {
|
|
27466
27599
|
mergeCollectedIntegrationConfig(flow.config);
|
|
27467
27600
|
} else if (options.yes) {
|
|
27468
|
-
|
|
27601
|
+
p24.log.error("Railway bucket provisioning did not complete.");
|
|
27469
27602
|
process.exit(1);
|
|
27470
27603
|
}
|
|
27471
27604
|
}
|
|
@@ -27484,10 +27617,10 @@ async function runInitCommand(name, options) {
|
|
|
27484
27617
|
});
|
|
27485
27618
|
collectedIntegrationConfig.overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
|
|
27486
27619
|
} else if (options.yes) {
|
|
27487
|
-
|
|
27620
|
+
p24.log.error("Vercel Blob provisioning did not complete.");
|
|
27488
27621
|
process.exit(1);
|
|
27489
27622
|
} else {
|
|
27490
|
-
|
|
27623
|
+
p24.log.warn(
|
|
27491
27624
|
"Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
|
|
27492
27625
|
);
|
|
27493
27626
|
collectedIntegrationConfig.sections.push({
|
|
@@ -27561,7 +27694,7 @@ async function runInitCommand(name, options) {
|
|
|
27561
27694
|
const nextConfigResult = scaffoldNextConfig({ cwd, nextMajorVersion });
|
|
27562
27695
|
s.clear();
|
|
27563
27696
|
if (nextConfigResult.status === "unsupported") {
|
|
27564
|
-
|
|
27697
|
+
p24.log.warn("The Next.js config could not be updated automatically \u2014 review it manually.");
|
|
27565
27698
|
}
|
|
27566
27699
|
const drizzleConfigPath = path52.join(cwd, "drizzle.config.ts");
|
|
27567
27700
|
if (!dbFiles.includes("drizzle.config.ts") && fs41.existsSync(drizzleConfigPath)) {
|
|
@@ -27572,20 +27705,20 @@ async function runInitCommand(name, options) {
|
|
|
27572
27705
|
readNamespacedTemplate("drizzle.config.ts", namespace),
|
|
27573
27706
|
"utf-8"
|
|
27574
27707
|
);
|
|
27575
|
-
|
|
27708
|
+
p24.log.success("Updated drizzle.config.ts");
|
|
27576
27709
|
} else if (!options.yes) {
|
|
27577
|
-
const overwrite = await
|
|
27710
|
+
const overwrite = await p24.confirm({
|
|
27578
27711
|
message: "drizzle.config.ts already exists. Overwrite with latest version?",
|
|
27579
27712
|
initialValue: true
|
|
27580
27713
|
});
|
|
27581
|
-
if (!
|
|
27714
|
+
if (!p24.isCancel(overwrite) && overwrite) {
|
|
27582
27715
|
const { readNamespacedTemplate } = await import("./template-reader-X6KFP7B3.js");
|
|
27583
27716
|
fs41.writeFileSync(
|
|
27584
27717
|
drizzleConfigPath,
|
|
27585
27718
|
readNamespacedTemplate("drizzle.config.ts", namespace),
|
|
27586
27719
|
"utf-8"
|
|
27587
27720
|
);
|
|
27588
|
-
|
|
27721
|
+
p24.log.success("Updated drizzle.config.ts");
|
|
27589
27722
|
}
|
|
27590
27723
|
}
|
|
27591
27724
|
}
|
|
@@ -27610,11 +27743,11 @@ async function runInitCommand(name, options) {
|
|
|
27610
27743
|
s.stop("");
|
|
27611
27744
|
} else {
|
|
27612
27745
|
s.stop("Failed to install dependencies");
|
|
27613
|
-
|
|
27614
|
-
|
|
27746
|
+
p24.log.warn(depsResult.error ?? "Unknown error");
|
|
27747
|
+
p24.log.info(
|
|
27615
27748
|
`You can install them manually:
|
|
27616
|
-
${
|
|
27617
|
-
${
|
|
27749
|
+
${pc10.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
|
|
27750
|
+
${pc10.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
|
|
27618
27751
|
);
|
|
27619
27752
|
disposeCancelGuard();
|
|
27620
27753
|
process.exit(1);
|
|
@@ -27667,22 +27800,22 @@ async function runInitCommand(name, options) {
|
|
|
27667
27800
|
writeConfigFile(cwd, resolvedIntegrationInstallResult.config);
|
|
27668
27801
|
const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider === "local";
|
|
27669
27802
|
for (const err of coreSchemasResult.errors) {
|
|
27670
|
-
|
|
27803
|
+
p24.log.warn(`Core schemas: ${err}`);
|
|
27671
27804
|
}
|
|
27672
27805
|
for (const warning of resolvedPresetInstallResult.warnings) {
|
|
27673
|
-
|
|
27806
|
+
p24.log.warn(warning);
|
|
27674
27807
|
}
|
|
27675
27808
|
for (const warning of resolvedIntegrationInstallResult.warnings) {
|
|
27676
|
-
|
|
27809
|
+
p24.log.warn(warning);
|
|
27677
27810
|
}
|
|
27678
27811
|
if (usesLocalStorage) {
|
|
27679
|
-
|
|
27812
|
+
p24.log.warn(
|
|
27680
27813
|
"Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob, Railway Bucket, or Cloudflare R2 for hosted production."
|
|
27681
27814
|
);
|
|
27682
27815
|
}
|
|
27683
27816
|
let dbPushed = false;
|
|
27684
27817
|
if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
|
|
27685
|
-
|
|
27818
|
+
p24.log.info(`Skipping database schema push ${pc10.dim("(--skip-migration)")}`);
|
|
27686
27819
|
} else if (depsResult.success && hasDbUrl(cwd)) {
|
|
27687
27820
|
let driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
|
|
27688
27821
|
if (!driverReady) {
|
|
@@ -27697,9 +27830,9 @@ async function runInitCommand(name, options) {
|
|
|
27697
27830
|
});
|
|
27698
27831
|
if (!driverResult.success) {
|
|
27699
27832
|
s.stop("Database push failed");
|
|
27700
|
-
|
|
27701
|
-
|
|
27702
|
-
`Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${
|
|
27833
|
+
p24.log.warn(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
|
|
27834
|
+
p24.log.info(
|
|
27835
|
+
`Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${pc10.cyan(drizzlePushCommand(pm))}`
|
|
27703
27836
|
);
|
|
27704
27837
|
} else {
|
|
27705
27838
|
driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
|
|
@@ -27726,8 +27859,8 @@ async function runInitCommand(name, options) {
|
|
|
27726
27859
|
const verification = await verifyDatabaseReachable(cwd);
|
|
27727
27860
|
clearDbSpinner();
|
|
27728
27861
|
if (!verification.success) {
|
|
27729
|
-
|
|
27730
|
-
|
|
27862
|
+
p24.log.warn(verification.error);
|
|
27863
|
+
p24.log.error("Database was not reachable. Aborting setup.");
|
|
27731
27864
|
process.exit(1);
|
|
27732
27865
|
}
|
|
27733
27866
|
} else {
|
|
@@ -27740,22 +27873,21 @@ async function runInitCommand(name, options) {
|
|
|
27740
27873
|
s.stop("Database push failed");
|
|
27741
27874
|
}
|
|
27742
27875
|
const pushError = pushResult.error ?? "Unknown error";
|
|
27743
|
-
|
|
27876
|
+
p24.log.warn(pushError);
|
|
27744
27877
|
if (isDatabaseReachabilityError(pushError)) {
|
|
27745
|
-
|
|
27878
|
+
p24.log.error("Database was not reachable. Aborting setup.");
|
|
27746
27879
|
process.exit(1);
|
|
27747
27880
|
}
|
|
27748
|
-
|
|
27881
|
+
p24.log.info(`You can run it manually: ${pc10.cyan(drizzlePushCommand(pm))}`);
|
|
27749
27882
|
}
|
|
27750
27883
|
}
|
|
27751
27884
|
}
|
|
27752
27885
|
let seedEmail;
|
|
27753
|
-
let seedPassword;
|
|
27754
27886
|
let seedSuccess = false;
|
|
27755
27887
|
let adminAccountReady = false;
|
|
27756
27888
|
if (dbPushed && options.skipAdminCreation) {
|
|
27757
|
-
|
|
27758
|
-
`Skipping admin user creation ${
|
|
27889
|
+
p24.log.info(
|
|
27890
|
+
`Skipping admin user creation ${pc10.dim(`(use ${betterstartExecCommand(pm, "seed")} later)`)}`
|
|
27759
27891
|
);
|
|
27760
27892
|
}
|
|
27761
27893
|
if (dbPushed && !options.yes && !options.skipAdminCreation) {
|
|
@@ -27766,14 +27898,14 @@ async function runInitCommand(name, options) {
|
|
|
27766
27898
|
const adminCheck = await checkExistingAdmin(cwd, adminDir, authBasePath);
|
|
27767
27899
|
s.clear();
|
|
27768
27900
|
if (adminCheck.error) {
|
|
27769
|
-
|
|
27901
|
+
p24.log.warn(`Could not verify existing admin account ${pc10.dim(`(${adminCheck.error})`)}`);
|
|
27770
27902
|
if (isDatabaseReachabilityError(adminCheck.error)) {
|
|
27771
|
-
|
|
27903
|
+
p24.log.error("Database was not reachable. Aborting setup.");
|
|
27772
27904
|
process.exit(1);
|
|
27773
27905
|
}
|
|
27774
27906
|
} else if (adminCheck.existingAdmin) {
|
|
27775
27907
|
const existingAdminLabel = formatAdminIdentity(adminCheck.existingAdmin);
|
|
27776
|
-
const adminAction = await
|
|
27908
|
+
const adminAction = await p24.select({
|
|
27777
27909
|
message: "Found an already existing admin account. Do you want to replace it or skip?",
|
|
27778
27910
|
options: [
|
|
27779
27911
|
{
|
|
@@ -27787,28 +27919,28 @@ async function runInitCommand(name, options) {
|
|
|
27787
27919
|
}
|
|
27788
27920
|
]
|
|
27789
27921
|
});
|
|
27790
|
-
if (
|
|
27791
|
-
|
|
27922
|
+
if (p24.isCancel(adminAction)) {
|
|
27923
|
+
p24.cancel("Setup cancelled.");
|
|
27792
27924
|
process.exit(0);
|
|
27793
27925
|
}
|
|
27794
27926
|
if (adminAction === "skip") {
|
|
27795
27927
|
adminAccountReady = true;
|
|
27796
|
-
|
|
27928
|
+
p24.log.info(`Keeping existing admin account ${pc10.dim(`(${existingAdminLabel})`)}`);
|
|
27797
27929
|
} else {
|
|
27798
27930
|
replaceExistingAdmin = true;
|
|
27799
27931
|
}
|
|
27800
27932
|
}
|
|
27801
27933
|
if (!adminAccountReady) {
|
|
27802
|
-
const credentials = await
|
|
27934
|
+
const credentials = await p24.group(
|
|
27803
27935
|
{
|
|
27804
|
-
email: () =>
|
|
27936
|
+
email: () => p24.text({
|
|
27805
27937
|
message: "Admin email",
|
|
27806
27938
|
placeholder: "admin@example.com",
|
|
27807
27939
|
validate: (v) => {
|
|
27808
27940
|
if (!v || !v.includes("@")) return "Please enter a valid email";
|
|
27809
27941
|
}
|
|
27810
27942
|
}),
|
|
27811
|
-
password: () =>
|
|
27943
|
+
password: () => p24.password({
|
|
27812
27944
|
message: "Admin password",
|
|
27813
27945
|
validate: (v) => {
|
|
27814
27946
|
if (!v || v.length < 8) return "Password must be at least 8 characters";
|
|
@@ -27817,14 +27949,13 @@ async function runInitCommand(name, options) {
|
|
|
27817
27949
|
},
|
|
27818
27950
|
{
|
|
27819
27951
|
onCancel: () => {
|
|
27820
|
-
|
|
27952
|
+
p24.cancel("Setup cancelled.");
|
|
27821
27953
|
process.exit(0);
|
|
27822
27954
|
}
|
|
27823
27955
|
}
|
|
27824
27956
|
);
|
|
27825
27957
|
seedEmail = credentials.email;
|
|
27826
|
-
|
|
27827
|
-
const credentialPromptRows = clackPromptRows("Admin email", credentials.email) + clackPromptRows("Admin password", p22.S_PASSWORD_MASK.repeat(credentials.password.length));
|
|
27958
|
+
const credentialPromptRows = clackPromptRows("Admin email", credentials.email) + clackPromptRows("Admin password", p24.S_PASSWORD_MASK.repeat(credentials.password.length));
|
|
27828
27959
|
let rowsBelowCredentialPrompts = 0;
|
|
27829
27960
|
let seedOverwriteMode = replaceExistingAdmin ? "admin" : void 0;
|
|
27830
27961
|
s.start(replaceExistingAdmin ? "Replacing admin user" : "Creating admin user");
|
|
@@ -27837,11 +27968,11 @@ async function runInitCommand(name, options) {
|
|
|
27837
27968
|
seedOverwriteMode
|
|
27838
27969
|
);
|
|
27839
27970
|
if (seedResult.existingUser) {
|
|
27840
|
-
const existingAccountMessage = `${
|
|
27971
|
+
const existingAccountMessage = `${pc10.yellow("\u25B2")} An account already exists for ${credentials.email}`;
|
|
27841
27972
|
s.stop(existingAccountMessage);
|
|
27842
27973
|
rowsBelowCredentialPrompts += clackLogRows(existingAccountMessage);
|
|
27843
27974
|
const replaceAccountMessage = "Replace the existing account with this email?";
|
|
27844
|
-
const replace = await
|
|
27975
|
+
const replace = await p24.confirm({
|
|
27845
27976
|
message: replaceAccountMessage,
|
|
27846
27977
|
initialValue: false
|
|
27847
27978
|
});
|
|
@@ -27849,7 +27980,7 @@ async function runInitCommand(name, options) {
|
|
|
27849
27980
|
replaceAccountMessage,
|
|
27850
27981
|
replace === true ? "Yes" : "No"
|
|
27851
27982
|
);
|
|
27852
|
-
if (!
|
|
27983
|
+
if (!p24.isCancel(replace) && replace) {
|
|
27853
27984
|
seedOverwriteMode = "email";
|
|
27854
27985
|
s.start("Replacing admin user");
|
|
27855
27986
|
seedResult = await runSeed(
|
|
@@ -27863,7 +27994,7 @@ async function runInitCommand(name, options) {
|
|
|
27863
27994
|
}
|
|
27864
27995
|
}
|
|
27865
27996
|
if (seedResult.success) {
|
|
27866
|
-
const seedSuccessMessage = seedOverwriteMode === "admin" ? `Admin user replaced` : `Admin user ${
|
|
27997
|
+
const seedSuccessMessage = seedOverwriteMode === "admin" ? `Admin user replaced` : `Admin user ${pc10.green(seedEmail)} created`;
|
|
27867
27998
|
s.stop(seedSuccessMessage);
|
|
27868
27999
|
rowsBelowCredentialPrompts += clackLogRows(seedSuccessMessage);
|
|
27869
28000
|
eraseRowsAbove(credentialPromptRows, rowsBelowCredentialPrompts);
|
|
@@ -27871,14 +28002,14 @@ async function runInitCommand(name, options) {
|
|
|
27871
28002
|
adminAccountReady = true;
|
|
27872
28003
|
} else if (seedResult.error) {
|
|
27873
28004
|
s.stop(`Failed to create admin user`);
|
|
27874
|
-
|
|
27875
|
-
`${
|
|
28005
|
+
p24.note(
|
|
28006
|
+
`${pc10.red(seedResult.error)}
|
|
27876
28007
|
|
|
27877
|
-
Run manually: ${
|
|
27878
|
-
|
|
28008
|
+
Run manually: ${pc10.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
28009
|
+
pc10.red("Seed failed")
|
|
27879
28010
|
);
|
|
27880
28011
|
if (isDatabaseReachabilityError(seedResult.error)) {
|
|
27881
|
-
|
|
28012
|
+
p24.log.error("Database was not reachable. Aborting setup.");
|
|
27882
28013
|
process.exit(1);
|
|
27883
28014
|
}
|
|
27884
28015
|
}
|
|
@@ -27909,6 +28040,7 @@ Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
27909
28040
|
const initialDeployProvider = railwaySessionPromise ? "railway" : readLinkedProjectId(cwd) ? "vercel" : "none";
|
|
27910
28041
|
deployProvider = await promptDeployProvider(initialDeployProvider);
|
|
27911
28042
|
}
|
|
28043
|
+
let deployedUrl;
|
|
27912
28044
|
if (deployProvider === "vercel") {
|
|
27913
28045
|
const deployFlow = await runVercelDeployFlow({
|
|
27914
28046
|
cwd,
|
|
@@ -27916,12 +28048,13 @@ Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
27916
28048
|
interactive: !options.yes,
|
|
27917
28049
|
env: process.env
|
|
27918
28050
|
});
|
|
28051
|
+
deployedUrl = deployFlow.url;
|
|
27919
28052
|
if (!deployFlow.ok) {
|
|
27920
28053
|
if (options.yes) {
|
|
27921
|
-
|
|
28054
|
+
p24.log.error("Vercel deploy did not complete.");
|
|
27922
28055
|
process.exit(1);
|
|
27923
28056
|
}
|
|
27924
|
-
|
|
28057
|
+
p24.log.warn("Vercel deploy did not complete; continuing.");
|
|
27925
28058
|
}
|
|
27926
28059
|
} else if (deployProvider === "railway") {
|
|
27927
28060
|
try {
|
|
@@ -27937,40 +28070,65 @@ Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
|
|
|
27937
28070
|
bucketUrlStyle: railwayBucketUrlStyle
|
|
27938
28071
|
})
|
|
27939
28072
|
});
|
|
28073
|
+
deployedUrl = deployFlow.url;
|
|
27940
28074
|
if (!deployFlow.ok) {
|
|
27941
|
-
if (deployFlow.detail)
|
|
28075
|
+
if (deployFlow.detail) p24.log.message(pc10.dim(redactSecrets(deployFlow.detail)));
|
|
27942
28076
|
if (options.yes) {
|
|
27943
|
-
|
|
28077
|
+
p24.log.error("Railway deploy did not complete.");
|
|
27944
28078
|
process.exit(1);
|
|
27945
28079
|
}
|
|
27946
|
-
|
|
28080
|
+
p24.log.warn("Railway deploy did not complete; continuing.");
|
|
27947
28081
|
}
|
|
27948
28082
|
} catch (error) {
|
|
27949
28083
|
const message = error instanceof Error ? error.message : String(error);
|
|
27950
28084
|
if (options.yes) {
|
|
27951
|
-
|
|
28085
|
+
p24.log.error(`Railway deploy failed: ${message}`);
|
|
27952
28086
|
process.exit(1);
|
|
27953
28087
|
}
|
|
27954
|
-
|
|
28088
|
+
p24.log.warn(`Railway deploy failed: ${message}`);
|
|
27955
28089
|
}
|
|
27956
28090
|
}
|
|
27957
28091
|
if (!options.yes && !options.skipDevServerStart) {
|
|
27958
28092
|
const devCmd = runCommand(pm, "dev");
|
|
27959
|
-
const startDev = await
|
|
28093
|
+
const startDev = await p24.confirm({
|
|
27960
28094
|
message: "Start the development server?",
|
|
27961
28095
|
initialValue: true
|
|
27962
28096
|
});
|
|
27963
|
-
if (!
|
|
28097
|
+
if (!p24.isCancel(startDev) && startDev) {
|
|
27964
28098
|
disposeCancelGuard();
|
|
27965
|
-
await startManagedDevServer(
|
|
27966
|
-
|
|
27967
|
-
|
|
27968
|
-
|
|
28099
|
+
await startManagedDevServer(
|
|
28100
|
+
cwd,
|
|
28101
|
+
devCmd,
|
|
28102
|
+
adminLoginUrl,
|
|
28103
|
+
seedSuccess && seedEmail ? seedEmail : void 0
|
|
28104
|
+
);
|
|
27969
28105
|
return;
|
|
27970
28106
|
}
|
|
27971
28107
|
}
|
|
27972
28108
|
disposeCancelGuard();
|
|
27973
|
-
|
|
28109
|
+
if (options.json && restoreStdout) {
|
|
28110
|
+
restoreStdout();
|
|
28111
|
+
console.log(
|
|
28112
|
+
JSON.stringify(
|
|
28113
|
+
{
|
|
28114
|
+
namespace,
|
|
28115
|
+
configPath: CONFIG_FILE_NAME,
|
|
28116
|
+
databaseProvider,
|
|
28117
|
+
storageProvider: presetSelection.storage,
|
|
28118
|
+
deployProvider,
|
|
28119
|
+
presets: presetSelection.presets,
|
|
28120
|
+
integrations: presetSelection.integrations,
|
|
28121
|
+
adminUrl: adminLoginUrl,
|
|
28122
|
+
adminEmail: seedEmail ?? null,
|
|
28123
|
+
deployedUrl: deployedUrl ?? null
|
|
28124
|
+
},
|
|
28125
|
+
null,
|
|
28126
|
+
2
|
|
28127
|
+
)
|
|
28128
|
+
);
|
|
28129
|
+
return;
|
|
28130
|
+
}
|
|
28131
|
+
p24.outro(`Admin ready at ${adminNamespace.routePath}`);
|
|
27974
28132
|
}
|
|
27975
28133
|
function isValidDbUrl(url) {
|
|
27976
28134
|
return url.startsWith("postgres://") || url.startsWith("postgresql://");
|
|
@@ -28356,12 +28514,11 @@ function stripAnsi2(value) {
|
|
|
28356
28514
|
return value.replace(ANSI_ESCAPE_PATTERN2, "");
|
|
28357
28515
|
}
|
|
28358
28516
|
function printAdminReadyNote(state) {
|
|
28359
|
-
const lines = [`Admin: ${
|
|
28360
|
-
if (state.adminEmail
|
|
28361
|
-
lines.unshift(`
|
|
28362
|
-
lines.unshift(`Admin user: ${pc9.cyan(state.adminEmail)}`);
|
|
28517
|
+
const lines = [`Admin: ${pc10.cyan(state.adminLoginUrl)}`];
|
|
28518
|
+
if (state.adminEmail) {
|
|
28519
|
+
lines.unshift(`Admin user: ${pc10.cyan(state.adminEmail)}`);
|
|
28363
28520
|
}
|
|
28364
|
-
|
|
28521
|
+
p24.note(lines.join("\n"), "Admin ready");
|
|
28365
28522
|
}
|
|
28366
28523
|
function shouldSuppressDevServerStartupLine(line) {
|
|
28367
28524
|
const plain = stripAnsi2(line).trim();
|
|
@@ -28424,7 +28581,7 @@ function pipeManagedDevServerStream(stream, target, state) {
|
|
|
28424
28581
|
flushBuffer(true);
|
|
28425
28582
|
});
|
|
28426
28583
|
}
|
|
28427
|
-
function startManagedDevServer(cwd, devCmd, adminLoginUrl,
|
|
28584
|
+
function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminEmail) {
|
|
28428
28585
|
return new Promise((resolve, reject) => {
|
|
28429
28586
|
const [bin, ...args] = devCmd.split(" ");
|
|
28430
28587
|
const child = spawn6(bin, args, {
|
|
@@ -28435,8 +28592,7 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
|
|
|
28435
28592
|
suppressStartup: true,
|
|
28436
28593
|
readyLogged: false,
|
|
28437
28594
|
adminLoginUrl,
|
|
28438
|
-
adminEmail
|
|
28439
|
-
adminPassword: adminCredentials?.password
|
|
28595
|
+
adminEmail
|
|
28440
28596
|
};
|
|
28441
28597
|
const forwardSignal = (signal) => {
|
|
28442
28598
|
if (!child.killed) {
|
|
@@ -28473,74 +28629,133 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
|
|
|
28473
28629
|
|
|
28474
28630
|
// adapters/next/commands/list-integrations.ts
|
|
28475
28631
|
import path53 from "path";
|
|
28476
|
-
import * as
|
|
28632
|
+
import * as p25 from "@clack/prompts";
|
|
28633
|
+
|
|
28634
|
+
// core-engine/utils/table.ts
|
|
28635
|
+
function renderTableRows(rows) {
|
|
28636
|
+
const widths = [];
|
|
28637
|
+
for (const row of rows) {
|
|
28638
|
+
row.forEach((cell, index) => {
|
|
28639
|
+
if (index < row.length - 1) {
|
|
28640
|
+
widths[index] = Math.max(widths[index] ?? 0, cell.length);
|
|
28641
|
+
}
|
|
28642
|
+
});
|
|
28643
|
+
}
|
|
28644
|
+
return rows.map(
|
|
28645
|
+
(row) => row.map((cell, index) => index < row.length - 1 ? cell.padEnd(widths[index]) : cell).join(" ").trimEnd()
|
|
28646
|
+
);
|
|
28647
|
+
}
|
|
28648
|
+
|
|
28649
|
+
// adapters/next/commands/list-integrations.ts
|
|
28477
28650
|
async function runListIntegrationsCommand(options) {
|
|
28478
28651
|
const cwd = options.cwd ? path53.resolve(options.cwd) : process.cwd();
|
|
28479
|
-
|
|
28652
|
+
if (options.json) {
|
|
28653
|
+
let installed2;
|
|
28654
|
+
try {
|
|
28655
|
+
installed2 = new Set((await resolveConfig(cwd)).integrations.installed);
|
|
28656
|
+
} catch (error) {
|
|
28657
|
+
console.error(
|
|
28658
|
+
`Error loading config: ${error instanceof Error ? error.message : String(error)}`
|
|
28659
|
+
);
|
|
28660
|
+
process.exit(1);
|
|
28661
|
+
}
|
|
28662
|
+
const items = listAvailableIntegrations().map((integration) => ({
|
|
28663
|
+
id: integration.id,
|
|
28664
|
+
status: installed2.has(integration.id) ? "installed" : "available",
|
|
28665
|
+
kind: integration.kind,
|
|
28666
|
+
description: integration.description
|
|
28667
|
+
}));
|
|
28668
|
+
console.log(JSON.stringify(items, null, 2));
|
|
28669
|
+
return;
|
|
28670
|
+
}
|
|
28671
|
+
const config = await resolveConfigOrExit(cwd);
|
|
28480
28672
|
const installedIntegrations = new Set(config.integrations.installed);
|
|
28481
|
-
const
|
|
28482
|
-
|
|
28483
|
-
|
|
28484
|
-
|
|
28485
|
-
|
|
28486
|
-
|
|
28487
|
-
|
|
28673
|
+
const rows = listAvailableIntegrations().map((integration) => [
|
|
28674
|
+
integration.id,
|
|
28675
|
+
installedIntegrations.has(integration.id) ? "installed" : "available",
|
|
28676
|
+
integration.kind,
|
|
28677
|
+
integration.description
|
|
28678
|
+
]);
|
|
28679
|
+
p25.note(renderTableRows(rows).join("\n"), "BetterStart integrations");
|
|
28680
|
+
p25.outro(
|
|
28681
|
+
`${installedIntegrations.size} installed, ${rows.length - installedIntegrations.size} available`
|
|
28488
28682
|
);
|
|
28489
28683
|
}
|
|
28490
28684
|
|
|
28491
28685
|
// adapters/next/commands/list-presets.ts
|
|
28492
28686
|
import path54 from "path";
|
|
28493
|
-
import * as
|
|
28687
|
+
import * as p26 from "@clack/prompts";
|
|
28494
28688
|
async function runListPresetsCommand(options) {
|
|
28495
28689
|
const cwd = options.cwd ? path54.resolve(options.cwd) : process.cwd();
|
|
28496
|
-
|
|
28690
|
+
if (options.json) {
|
|
28691
|
+
let installed2;
|
|
28692
|
+
try {
|
|
28693
|
+
installed2 = new Set((await resolveConfig(cwd)).presets.installed);
|
|
28694
|
+
} catch (error) {
|
|
28695
|
+
console.error(
|
|
28696
|
+
`Error loading config: ${error instanceof Error ? error.message : String(error)}`
|
|
28697
|
+
);
|
|
28698
|
+
process.exit(1);
|
|
28699
|
+
}
|
|
28700
|
+
const items = listAvailablePresets().map((preset) => ({
|
|
28701
|
+
id: preset.id,
|
|
28702
|
+
status: installed2.has(preset.id) ? "installed" : "available",
|
|
28703
|
+
kind: preset.kind,
|
|
28704
|
+
description: preset.description
|
|
28705
|
+
}));
|
|
28706
|
+
console.log(JSON.stringify(items, null, 2));
|
|
28707
|
+
return;
|
|
28708
|
+
}
|
|
28709
|
+
const config = await resolveConfigOrExit(cwd);
|
|
28497
28710
|
const installedPresets = new Set(config.presets.installed);
|
|
28498
|
-
const
|
|
28499
|
-
|
|
28500
|
-
|
|
28501
|
-
|
|
28502
|
-
|
|
28503
|
-
|
|
28711
|
+
const rows = listAvailablePresets().map((preset) => [
|
|
28712
|
+
preset.id,
|
|
28713
|
+
installedPresets.has(preset.id) ? "installed" : "available",
|
|
28714
|
+
preset.kind,
|
|
28715
|
+
preset.description
|
|
28716
|
+
]);
|
|
28717
|
+
p26.note(renderTableRows(rows).join("\n"), "BetterStart presets");
|
|
28718
|
+
p26.outro(`${installedPresets.size} installed, ${rows.length - installedPresets.size} available`);
|
|
28504
28719
|
}
|
|
28505
28720
|
|
|
28506
28721
|
// adapters/next/commands/remove.ts
|
|
28507
28722
|
import path55 from "path";
|
|
28508
|
-
import * as
|
|
28723
|
+
import * as p27 from "@clack/prompts";
|
|
28509
28724
|
async function runRemoveCommand(items, options) {
|
|
28510
28725
|
const removeIntegrationsMode = Boolean(options.integration);
|
|
28511
28726
|
if (!removeIntegrationsMode && items.includes("core")) {
|
|
28512
|
-
|
|
28727
|
+
p27.log.error("The core Admin cannot be removed.");
|
|
28513
28728
|
process.exit(1);
|
|
28514
28729
|
}
|
|
28515
28730
|
const presetIds = items.filter(isPresetId);
|
|
28516
28731
|
const integrationIds = items.filter(isIntegrationId);
|
|
28517
28732
|
if (!removeIntegrationsMode && integrationIds.length > 0) {
|
|
28518
|
-
|
|
28733
|
+
p27.log.error(
|
|
28519
28734
|
`Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
|
|
28520
28735
|
);
|
|
28521
28736
|
process.exit(1);
|
|
28522
28737
|
}
|
|
28523
28738
|
if (removeIntegrationsMode && presetIds.length > 0) {
|
|
28524
|
-
|
|
28739
|
+
p27.log.error(`Preset IDs cannot be removed with --integration: ${presetIds.join(", ")}`);
|
|
28525
28740
|
process.exit(1);
|
|
28526
28741
|
}
|
|
28527
28742
|
const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((presetId) => !isPresetId(presetId));
|
|
28528
28743
|
if (invalidItems.length > 0) {
|
|
28529
|
-
|
|
28744
|
+
p27.log.error(
|
|
28530
28745
|
removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPresetMessage(invalidItems)
|
|
28531
28746
|
);
|
|
28532
28747
|
process.exit(1);
|
|
28533
28748
|
}
|
|
28534
28749
|
const cwd = options.cwd ? path55.resolve(options.cwd) : process.cwd();
|
|
28535
|
-
const config = await
|
|
28750
|
+
const config = await resolveConfigOrExit(cwd);
|
|
28536
28751
|
const pm = detectPackageManager(cwd);
|
|
28537
28752
|
if (!options.force) {
|
|
28538
|
-
const confirmed = await
|
|
28753
|
+
const confirmed = await p27.confirm({
|
|
28539
28754
|
message: `Remove ${removeIntegrationsMode ? "integration" : "preset"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
|
|
28540
28755
|
initialValue: false
|
|
28541
28756
|
});
|
|
28542
|
-
if (
|
|
28543
|
-
|
|
28757
|
+
if (p27.isCancel(confirmed) || !confirmed) {
|
|
28758
|
+
p27.cancel(`${removeIntegrationsMode ? "Integration" : "Preset"} removal cancelled.`);
|
|
28544
28759
|
process.exit(0);
|
|
28545
28760
|
}
|
|
28546
28761
|
}
|
|
@@ -28553,13 +28768,13 @@ async function runRemoveCommand(items, options) {
|
|
|
28553
28768
|
});
|
|
28554
28769
|
writeConfigFile(cwd, result2.config);
|
|
28555
28770
|
if (result2.removed.length === 0) {
|
|
28556
|
-
|
|
28771
|
+
p27.outro("No integrations were removed.");
|
|
28557
28772
|
return;
|
|
28558
28773
|
}
|
|
28559
28774
|
if (result2.warnings.length > 0) {
|
|
28560
|
-
|
|
28775
|
+
p27.note(result2.warnings.join("\n"), "Warnings");
|
|
28561
28776
|
}
|
|
28562
|
-
|
|
28777
|
+
p27.outro(
|
|
28563
28778
|
`Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
|
|
28564
28779
|
);
|
|
28565
28780
|
return;
|
|
@@ -28572,31 +28787,19 @@ async function runRemoveCommand(items, options) {
|
|
|
28572
28787
|
});
|
|
28573
28788
|
writeConfigFile(cwd, result.config);
|
|
28574
28789
|
if (result.removed.length === 0) {
|
|
28575
|
-
|
|
28790
|
+
p27.outro("No presets were removed.");
|
|
28576
28791
|
return;
|
|
28577
28792
|
}
|
|
28578
28793
|
if (result.warnings.length > 0) {
|
|
28579
|
-
|
|
28794
|
+
p27.note(result.warnings.join("\n"), "Warnings");
|
|
28580
28795
|
}
|
|
28581
|
-
|
|
28796
|
+
p27.outro(`Removed preset${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
|
|
28582
28797
|
}
|
|
28583
28798
|
|
|
28584
28799
|
// adapters/next/commands/remove-schema.ts
|
|
28585
28800
|
import fs42 from "fs";
|
|
28586
28801
|
import path56 from "path";
|
|
28587
|
-
import
|
|
28588
|
-
async function promptConfirm4(message) {
|
|
28589
|
-
const rl = readline.createInterface({
|
|
28590
|
-
input: process.stdin,
|
|
28591
|
-
output: process.stdout
|
|
28592
|
-
});
|
|
28593
|
-
return new Promise((resolve) => {
|
|
28594
|
-
rl.question(`${message} (y/N) `, (answer) => {
|
|
28595
|
-
rl.close();
|
|
28596
|
-
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
28597
|
-
});
|
|
28598
|
-
});
|
|
28599
|
-
}
|
|
28802
|
+
import * as clack3 from "@clack/prompts";
|
|
28600
28803
|
function removePath2(cwd, filePath) {
|
|
28601
28804
|
const fullPath = path56.join(cwd, ...filePath.split("/"));
|
|
28602
28805
|
const existed = fs42.existsSync(fullPath);
|
|
@@ -28642,35 +28845,40 @@ async function runRemoveSchemaCommand(schemaName, options) {
|
|
|
28642
28845
|
schemaName
|
|
28643
28846
|
);
|
|
28644
28847
|
if (owner === "core") {
|
|
28645
|
-
|
|
28646
|
-
`
|
|
28848
|
+
clack3.log.error(
|
|
28849
|
+
`"${schemaName}" is a core Admin module and cannot be removed with remove-schema.`
|
|
28647
28850
|
);
|
|
28648
28851
|
process.exit(1);
|
|
28649
28852
|
}
|
|
28650
28853
|
if (owner.startsWith("preset:")) {
|
|
28651
|
-
|
|
28854
|
+
clack3.log.error(`"${schemaName}" is owned by ${owner}. Remove the owning preset instead.`);
|
|
28652
28855
|
process.exit(1);
|
|
28653
28856
|
}
|
|
28654
28857
|
const cwd = options.cwd ? path56.resolve(options.cwd) : process.cwd();
|
|
28655
|
-
const config = await
|
|
28858
|
+
const config = await resolveConfigOrExit(cwd);
|
|
28656
28859
|
const paths = resolveProjectPaths(config);
|
|
28657
28860
|
const manifest = loadManifest(cwd, schemaName);
|
|
28658
28861
|
if (!snapshotRootExists(cwd)) {
|
|
28659
|
-
|
|
28660
|
-
" Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
|
|
28661
|
-
);
|
|
28862
|
+
clack3.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
28662
28863
|
process.exit(1);
|
|
28663
28864
|
}
|
|
28664
28865
|
if (!manifest) {
|
|
28665
|
-
|
|
28666
|
-
" Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
|
|
28667
|
-
);
|
|
28866
|
+
clack3.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
|
|
28668
28867
|
process.exit(1);
|
|
28669
28868
|
}
|
|
28670
28869
|
if (!options.force) {
|
|
28671
|
-
|
|
28672
|
-
|
|
28673
|
-
|
|
28870
|
+
if (!isInteractiveSession4()) {
|
|
28871
|
+
clack3.log.error(
|
|
28872
|
+
`Removing generated files for ${schemaName} needs confirmation. Re-run with --force.`
|
|
28873
|
+
);
|
|
28874
|
+
process.exit(1);
|
|
28875
|
+
}
|
|
28876
|
+
const confirmed = await clack3.confirm({
|
|
28877
|
+
message: `Remove generated files for ${schemaName}?`,
|
|
28878
|
+
initialValue: false
|
|
28879
|
+
});
|
|
28880
|
+
if (clack3.isCancel(confirmed) || !confirmed) {
|
|
28881
|
+
clack3.cancel("Cancelled.");
|
|
28674
28882
|
return;
|
|
28675
28883
|
}
|
|
28676
28884
|
}
|
|
@@ -28718,18 +28926,18 @@ async function runRemoveSchemaCommand(schemaName, options) {
|
|
|
28718
28926
|
force: false,
|
|
28719
28927
|
interactive: false
|
|
28720
28928
|
});
|
|
28721
|
-
|
|
28722
|
-
|
|
28723
|
-
|
|
28724
|
-
|
|
28725
|
-
|
|
28929
|
+
clack3.log.info(
|
|
28930
|
+
`Tombstone written: .betterstart/snapshots/_removed/${schemaName}
|
|
28931
|
+
Schema JSON preserved.`
|
|
28932
|
+
);
|
|
28933
|
+
clack3.outro(`Removed generated files for ${schemaName}`);
|
|
28726
28934
|
}
|
|
28727
28935
|
|
|
28728
28936
|
// adapters/next/commands/uninstall.ts
|
|
28729
28937
|
import fs44 from "fs";
|
|
28730
28938
|
import path57 from "path";
|
|
28731
|
-
import * as
|
|
28732
|
-
import
|
|
28939
|
+
import * as p28 from "@clack/prompts";
|
|
28940
|
+
import pc11 from "picocolors";
|
|
28733
28941
|
|
|
28734
28942
|
// adapters/next/commands/uninstall-cleaners.ts
|
|
28735
28943
|
import fs43 from "fs";
|
|
@@ -28950,8 +29158,8 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
28950
29158
|
count: configFiles.length,
|
|
28951
29159
|
unit: configFiles.length === 1 ? "file" : "files",
|
|
28952
29160
|
execute() {
|
|
28953
|
-
for (const
|
|
28954
|
-
if (fs44.existsSync(
|
|
29161
|
+
for (const p30 of configPaths) {
|
|
29162
|
+
if (fs44.existsSync(p30)) fs44.unlinkSync(p30);
|
|
28955
29163
|
}
|
|
28956
29164
|
}
|
|
28957
29165
|
});
|
|
@@ -29015,7 +29223,7 @@ function buildUninstallPlan(cwd, namespaceValue) {
|
|
|
29015
29223
|
}
|
|
29016
29224
|
async function runUninstallCommand(options) {
|
|
29017
29225
|
const cwd = options.cwd ? path57.resolve(options.cwd) : process.cwd();
|
|
29018
|
-
|
|
29226
|
+
p28.intro(pc11.bgRed(pc11.white(" BetterStart Uninstall ")));
|
|
29019
29227
|
let namespace = DEFAULT_ADMIN_NAMESPACE;
|
|
29020
29228
|
try {
|
|
29021
29229
|
const config = await resolveConfig(cwd);
|
|
@@ -29024,23 +29232,23 @@ async function runUninstallCommand(options) {
|
|
|
29024
29232
|
}
|
|
29025
29233
|
const steps = buildUninstallPlan(cwd, namespace);
|
|
29026
29234
|
if (steps.length === 0) {
|
|
29027
|
-
|
|
29028
|
-
|
|
29235
|
+
p28.log.success(`${pc11.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
|
|
29236
|
+
p28.outro("Project already clean");
|
|
29029
29237
|
return;
|
|
29030
29238
|
}
|
|
29031
29239
|
const planLines = steps.map((step) => {
|
|
29032
29240
|
const names = step.items.join(" ");
|
|
29033
|
-
const countLabel =
|
|
29034
|
-
return `${
|
|
29241
|
+
const countLabel = pc11.dim(`${step.count} ${step.unit}`);
|
|
29242
|
+
return `${pc11.red("\u2717")} ${names} ${countLabel}`;
|
|
29035
29243
|
});
|
|
29036
|
-
|
|
29244
|
+
p28.note(planLines.join("\n"), "Uninstall plan");
|
|
29037
29245
|
if (!options.force) {
|
|
29038
|
-
const confirmed = await
|
|
29246
|
+
const confirmed = await p28.confirm({
|
|
29039
29247
|
message: "Proceed with uninstall?",
|
|
29040
29248
|
initialValue: false
|
|
29041
29249
|
});
|
|
29042
|
-
if (
|
|
29043
|
-
|
|
29250
|
+
if (p28.isCancel(confirmed) || !confirmed) {
|
|
29251
|
+
p28.cancel("Uninstall cancelled.");
|
|
29044
29252
|
process.exit(0);
|
|
29045
29253
|
}
|
|
29046
29254
|
}
|
|
@@ -29052,15 +29260,15 @@ async function runUninstallCommand(options) {
|
|
|
29052
29260
|
}
|
|
29053
29261
|
const parts = steps.map((step) => `${step.count} ${step.unit}`);
|
|
29054
29262
|
s.stop(`Removed ${parts.join(", ")}`);
|
|
29055
|
-
|
|
29056
|
-
|
|
29263
|
+
p28.note(pc11.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
|
|
29264
|
+
p28.outro("Uninstall complete");
|
|
29057
29265
|
}
|
|
29058
29266
|
|
|
29059
29267
|
// adapters/next/commands/update-component.ts
|
|
29060
29268
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
29061
29269
|
import fs45 from "fs";
|
|
29062
29270
|
import path58 from "path";
|
|
29063
|
-
import * as
|
|
29271
|
+
import * as clack4 from "@clack/prompts";
|
|
29064
29272
|
import fsExtra from "fs-extra";
|
|
29065
29273
|
var STATIC_CUSTOM_DEPENDENCIES = {
|
|
29066
29274
|
"content-editor": [
|
|
@@ -29239,6 +29447,7 @@ var TIPTAP_TEMPLATE_DEPENDENCIES = [
|
|
|
29239
29447
|
"media-field"
|
|
29240
29448
|
];
|
|
29241
29449
|
var TIPTAP_CONTENT_EDITOR_DIRECTORIES = ["tiptap-extension", "tiptap-node", "tiptap-ui"];
|
|
29450
|
+
var MAX_OVERWRITE_PREVIEW_ROWS = 20;
|
|
29242
29451
|
var CODEMIRROR_PACKAGE_DEPS = [
|
|
29243
29452
|
"@codemirror/commands",
|
|
29244
29453
|
"@codemirror/lang-javascript",
|
|
@@ -30928,50 +31137,67 @@ async function runUpdateCommand(components, options) {
|
|
|
30928
31137
|
const cwd = options.cwd ? path58.resolve(options.cwd) : process.cwd();
|
|
30929
31138
|
const normalizedOnly = normalizeShadcnPresetOnly(options.only);
|
|
30930
31139
|
validateShadcnPresetOptions(components, options);
|
|
31140
|
+
if (options.json && !options.list) {
|
|
31141
|
+
clack4.cancel("--json can only be used with --list.");
|
|
31142
|
+
process.exit(1);
|
|
31143
|
+
}
|
|
30931
31144
|
if (options.list) {
|
|
30932
|
-
const all = getAllComponentNames();
|
|
30933
|
-
clack2.intro("Available components");
|
|
30934
31145
|
const uiComponents = getStaticUiComponentEntries();
|
|
30935
31146
|
const customComponents = getStaticCustomComponentEntries();
|
|
30936
31147
|
const templateKeys = Object.keys(TEMPLATE_REGISTRY).sort();
|
|
30937
|
-
|
|
30938
|
-
console.log(` Shadcn UI Components (${uiComponents.length})`);
|
|
30939
|
-
console.log(` ${"\u2500".repeat(50)}`);
|
|
30940
|
-
console.log(` ${"Name".padEnd(28)} ${"Path"}`);
|
|
30941
|
-
console.log(` ${"\u2500".repeat(50)}`);
|
|
30942
|
-
for (const component of uiComponents) {
|
|
30943
|
-
console.log(` ${component.name.padEnd(28)} components/ui/${component.file}`);
|
|
30944
|
-
}
|
|
30945
|
-
console.log();
|
|
30946
|
-
console.log(` Custom Components (${customComponents.length})`);
|
|
30947
|
-
console.log(` ${"\u2500".repeat(56)}`);
|
|
30948
|
-
console.log(` ${"Name".padEnd(28)} ${"Path"}`);
|
|
30949
|
-
console.log(` ${"\u2500".repeat(56)}`);
|
|
30950
|
-
for (const component of customComponents) {
|
|
30951
|
-
console.log(` ${component.name.padEnd(28)} components/custom/${component.file}`);
|
|
30952
|
-
}
|
|
30953
|
-
console.log();
|
|
30954
|
-
console.log(` Template Components (${templateKeys.length})`);
|
|
30955
|
-
console.log(` ${"\u2500".repeat(56)}`);
|
|
30956
|
-
console.log(` ${"Name".padEnd(28)} ${"Path"}`);
|
|
30957
|
-
console.log(` ${"\u2500".repeat(56)}`);
|
|
30958
|
-
for (const name of templateKeys) {
|
|
31148
|
+
const templatePath = (name) => {
|
|
30959
31149
|
const entry = TEMPLATE_REGISTRY[name];
|
|
30960
|
-
|
|
30961
|
-
|
|
30962
|
-
|
|
30963
|
-
|
|
30964
|
-
|
|
30965
|
-
|
|
30966
|
-
|
|
30967
|
-
|
|
30968
|
-
|
|
31150
|
+
return entry.displayPath ?? (typeof entry.relPath === "string" ? entry.relPath : "");
|
|
31151
|
+
};
|
|
31152
|
+
if (options.json) {
|
|
31153
|
+
const items = [
|
|
31154
|
+
...uiComponents.map((component) => ({
|
|
31155
|
+
name: component.name,
|
|
31156
|
+
path: `components/ui/${component.file}`,
|
|
31157
|
+
kind: "ui"
|
|
31158
|
+
})),
|
|
31159
|
+
...customComponents.map((component) => ({
|
|
31160
|
+
name: component.name,
|
|
31161
|
+
path: `components/custom/${component.file}`,
|
|
31162
|
+
kind: "custom"
|
|
31163
|
+
})),
|
|
31164
|
+
...templateKeys.map((name) => ({ name, path: templatePath(name), kind: "template" })),
|
|
31165
|
+
{ name: "tiptap", path: "components/custom/content-editor/tiptap-*/", kind: "special" }
|
|
31166
|
+
];
|
|
31167
|
+
console.log(JSON.stringify(items, null, 2));
|
|
31168
|
+
return;
|
|
31169
|
+
}
|
|
31170
|
+
const all = getAllComponentNames();
|
|
31171
|
+
clack4.intro("Available components");
|
|
31172
|
+
clack4.note(
|
|
31173
|
+
renderTableRows(
|
|
31174
|
+
uiComponents.map((component) => [component.name, `components/ui/${component.file}`])
|
|
31175
|
+
).join("\n"),
|
|
31176
|
+
`Shadcn UI Components (${uiComponents.length})`
|
|
31177
|
+
);
|
|
31178
|
+
clack4.note(
|
|
31179
|
+
renderTableRows(
|
|
31180
|
+
customComponents.map((component) => [component.name, `components/custom/${component.file}`])
|
|
31181
|
+
).join("\n"),
|
|
31182
|
+
`Custom Components (${customComponents.length})`
|
|
31183
|
+
);
|
|
31184
|
+
clack4.note(
|
|
31185
|
+
renderTableRows(templateKeys.map((name) => [name, templatePath(name)])).join("\n"),
|
|
31186
|
+
`Template Components (${templateKeys.length})`
|
|
31187
|
+
);
|
|
31188
|
+
clack4.note(
|
|
31189
|
+
renderTableRows([["tiptap", "components/custom/content-editor/tiptap-*/ (all files)"]]).join(
|
|
31190
|
+
"\n"
|
|
31191
|
+
),
|
|
31192
|
+
"Special"
|
|
31193
|
+
);
|
|
31194
|
+
clack4.outro(`${all.length} components available`);
|
|
30969
31195
|
return;
|
|
30970
31196
|
}
|
|
30971
|
-
const config = await
|
|
31197
|
+
const config = await resolveConfigOrExit(cwd);
|
|
30972
31198
|
const admin = path58.resolve(cwd, config.paths.admin);
|
|
30973
31199
|
if (!fs45.existsSync(admin)) {
|
|
30974
|
-
|
|
31200
|
+
clack4.cancel(
|
|
30975
31201
|
`Admin directory not found at ${config.paths.admin}. Run 'betterstart init' first.`
|
|
30976
31202
|
);
|
|
30977
31203
|
process.exit(1);
|
|
@@ -30986,12 +31212,12 @@ async function runUpdateCommand(components, options) {
|
|
|
30986
31212
|
return;
|
|
30987
31213
|
}
|
|
30988
31214
|
if (!options.all && components.length === 0) {
|
|
30989
|
-
|
|
31215
|
+
clack4.log.error(
|
|
30990
31216
|
"Provide component names or use --all. Run with --list to see available components."
|
|
30991
31217
|
);
|
|
30992
31218
|
process.exit(1);
|
|
30993
31219
|
}
|
|
30994
|
-
|
|
31220
|
+
clack4.intro("BetterStart Update Components");
|
|
30995
31221
|
const toUpdate = options.all ? getAllComponentNamesForConfig(config) : components;
|
|
30996
31222
|
const uiDir = resolveCliAssetPath("shared-assets", "react-admin", "ui");
|
|
30997
31223
|
const customDir = resolveCliAssetPath("shared-assets", "react-admin", "custom");
|
|
@@ -31000,6 +31226,7 @@ async function runUpdateCommand(components, options) {
|
|
|
31000
31226
|
const updatedTemplateNames = /* @__PURE__ */ new Set();
|
|
31001
31227
|
const updatedStaticNames = /* @__PURE__ */ new Set();
|
|
31002
31228
|
const requiredPackageDependencies = /* @__PURE__ */ new Set();
|
|
31229
|
+
const pendingWrites = [];
|
|
31003
31230
|
function trackPackageDependencies(name) {
|
|
31004
31231
|
for (const dependency of COMPONENT_PACKAGE_DEPENDENCIES[name] ?? []) {
|
|
31005
31232
|
requiredPackageDependencies.add(dependency);
|
|
@@ -31010,12 +31237,12 @@ async function runUpdateCommand(components, options) {
|
|
|
31010
31237
|
return false;
|
|
31011
31238
|
}
|
|
31012
31239
|
if (entry.requiredPreset && !hasPreset(config, entry.requiredPreset)) {
|
|
31013
|
-
|
|
31240
|
+
clack4.log.warn(`${name} requires the ${entry.requiredPreset} preset and was skipped.`);
|
|
31014
31241
|
skipped++;
|
|
31015
31242
|
return false;
|
|
31016
31243
|
}
|
|
31017
31244
|
if (entry.requiredIntegration && !hasIntegration(config, entry.requiredIntegration)) {
|
|
31018
|
-
|
|
31245
|
+
clack4.log.warn(
|
|
31019
31246
|
`${name} requires the ${entry.requiredIntegration} integration and was skipped.`
|
|
31020
31247
|
);
|
|
31021
31248
|
skipped++;
|
|
@@ -31025,14 +31252,19 @@ async function runUpdateCommand(components, options) {
|
|
|
31025
31252
|
const baseDir = entry.base === "cwd" ? cwd : admin;
|
|
31026
31253
|
const destPath = path58.join(baseDir, relPath);
|
|
31027
31254
|
if (entry.preserveExisting && fs45.existsSync(destPath)) {
|
|
31028
|
-
|
|
31255
|
+
clack4.log.info(`Preserved ${relPath}`);
|
|
31029
31256
|
updatedTemplateNames.add(name);
|
|
31030
31257
|
skipped++;
|
|
31031
31258
|
return false;
|
|
31032
31259
|
}
|
|
31033
|
-
|
|
31034
|
-
|
|
31035
|
-
|
|
31260
|
+
pendingWrites.push({
|
|
31261
|
+
displayPath: relPath,
|
|
31262
|
+
write: () => {
|
|
31263
|
+
fsExtra.ensureDirSync(path58.dirname(destPath));
|
|
31264
|
+
fs45.writeFileSync(destPath, content, "utf-8");
|
|
31265
|
+
clack4.log.success(`Updated ${relPath}`);
|
|
31266
|
+
}
|
|
31267
|
+
});
|
|
31036
31268
|
updatedTemplateNames.add(name);
|
|
31037
31269
|
trackPackageDependencies(name);
|
|
31038
31270
|
updated++;
|
|
@@ -31061,17 +31293,29 @@ async function runUpdateCommand(components, options) {
|
|
|
31061
31293
|
const namespace = config.frameworkConfig.next.namespace;
|
|
31062
31294
|
const namespacedAssetFile = applyAdminNamespaceToPath(assetFile, namespace);
|
|
31063
31295
|
const destPath = path58.join(admin, "components", assetDirectory, namespacedAssetFile);
|
|
31064
|
-
|
|
31065
|
-
|
|
31066
|
-
|
|
31296
|
+
pendingWrites.push({
|
|
31297
|
+
displayPath: `components/${assetDirectory}/${namespacedAssetFile}`,
|
|
31298
|
+
write: () => {
|
|
31299
|
+
fsExtra.ensureDirSync(path58.dirname(destPath));
|
|
31300
|
+
writeNamespacedFile(path58.join(assetDir, assetFile), destPath, namespace);
|
|
31301
|
+
clack4.log.success(`Updated components/${assetDirectory}/${namespacedAssetFile}`);
|
|
31302
|
+
}
|
|
31303
|
+
});
|
|
31067
31304
|
if (assetDirectory === "custom") {
|
|
31068
31305
|
const assetSubdir = path58.join(assetDir, name);
|
|
31069
31306
|
if (fs45.existsSync(assetSubdir) && fs45.statSync(assetSubdir).isDirectory()) {
|
|
31070
31307
|
const namespacedName = applyAdminNamespaceToPath(name, namespace);
|
|
31071
31308
|
const destSubdir = path58.join(admin, "components", assetDirectory, namespacedName);
|
|
31072
|
-
|
|
31073
|
-
|
|
31074
|
-
|
|
31309
|
+
pendingWrites.push({
|
|
31310
|
+
displayPath: `components/${assetDirectory}/${namespacedName}/ (template files)`,
|
|
31311
|
+
write: () => {
|
|
31312
|
+
fsExtra.ensureDirSync(destSubdir);
|
|
31313
|
+
copyNamespacedDirectory(assetSubdir, destSubdir, namespace);
|
|
31314
|
+
clack4.log.success(
|
|
31315
|
+
`Updated components/${assetDirectory}/${namespacedName}/ (template files)`
|
|
31316
|
+
);
|
|
31317
|
+
}
|
|
31318
|
+
});
|
|
31075
31319
|
}
|
|
31076
31320
|
}
|
|
31077
31321
|
updatedStaticNames.add(key);
|
|
@@ -31100,25 +31344,34 @@ async function runUpdateCommand(components, options) {
|
|
|
31100
31344
|
if (!fs45.existsSync(srcBaseDir)) {
|
|
31101
31345
|
return false;
|
|
31102
31346
|
}
|
|
31103
|
-
|
|
31347
|
+
const dirsToCopy = [];
|
|
31104
31348
|
for (const directory of TIPTAP_CONTENT_EDITOR_DIRECTORIES) {
|
|
31105
31349
|
const srcDir = path58.join(srcBaseDir, directory);
|
|
31106
31350
|
if (!fs45.existsSync(srcDir)) {
|
|
31107
31351
|
continue;
|
|
31108
31352
|
}
|
|
31109
|
-
|
|
31110
|
-
|
|
31111
|
-
|
|
31112
|
-
|
|
31353
|
+
dirsToCopy.push({
|
|
31354
|
+
srcDir,
|
|
31355
|
+
destDir: path58.join(destBaseDir, applyAdminNamespaceToPath(directory, namespace))
|
|
31356
|
+
});
|
|
31113
31357
|
}
|
|
31114
|
-
if (
|
|
31358
|
+
if (dirsToCopy.length === 0) {
|
|
31115
31359
|
return false;
|
|
31116
31360
|
}
|
|
31117
|
-
|
|
31361
|
+
pendingWrites.push({
|
|
31362
|
+
displayPath: "components/custom/content-editor/tiptap-*/ (template files)",
|
|
31363
|
+
write: () => {
|
|
31364
|
+
for (const { srcDir, destDir } of dirsToCopy) {
|
|
31365
|
+
fsExtra.ensureDirSync(destDir);
|
|
31366
|
+
copyNamespacedDirectory(srcDir, destDir, namespace);
|
|
31367
|
+
}
|
|
31368
|
+
clack4.log.success("Updated components/custom/content-editor/tiptap-*/ (template files)");
|
|
31369
|
+
removeLegacyDirectory(path58.join(admin, "components", "custom", "tiptap"));
|
|
31370
|
+
}
|
|
31371
|
+
});
|
|
31118
31372
|
updatedStaticNames.add(key);
|
|
31119
31373
|
trackPackageDependencies("tiptap");
|
|
31120
31374
|
updated++;
|
|
31121
|
-
removeLegacyDirectory(path58.join(admin, "components", "custom", "tiptap"));
|
|
31122
31375
|
for (const dependencyName of TIPTAP_TEMPLATE_DEPENDENCIES) {
|
|
31123
31376
|
writeNamedDependency(dependencyName);
|
|
31124
31377
|
}
|
|
@@ -31154,14 +31407,44 @@ async function runUpdateCommand(components, options) {
|
|
|
31154
31407
|
}
|
|
31155
31408
|
if (name === "tiptap") {
|
|
31156
31409
|
if (!writeTiptapTemplates()) {
|
|
31157
|
-
|
|
31410
|
+
clack4.log.warn("tiptap templates not found");
|
|
31158
31411
|
skipped++;
|
|
31159
31412
|
}
|
|
31160
31413
|
continue;
|
|
31161
31414
|
}
|
|
31162
|
-
|
|
31415
|
+
clack4.log.warn(`Unknown component: ${name}`);
|
|
31163
31416
|
skipped++;
|
|
31164
31417
|
}
|
|
31418
|
+
if (pendingWrites.length > 0) {
|
|
31419
|
+
const displayPaths = pendingWrites.map((entry) => entry.displayPath);
|
|
31420
|
+
const preview = displayPaths.slice(0, MAX_OVERWRITE_PREVIEW_ROWS);
|
|
31421
|
+
if (displayPaths.length > preview.length) {
|
|
31422
|
+
preview.push(`\u2026 and ${displayPaths.length - preview.length} more`);
|
|
31423
|
+
}
|
|
31424
|
+
clack4.note(
|
|
31425
|
+
preview.join("\n"),
|
|
31426
|
+
`Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"}`
|
|
31427
|
+
);
|
|
31428
|
+
if (!options.yes) {
|
|
31429
|
+
if (!isInteractiveSession4()) {
|
|
31430
|
+
clack4.log.error(
|
|
31431
|
+
`Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"} needs confirmation. Re-run with --yes.`
|
|
31432
|
+
);
|
|
31433
|
+
process.exit(1);
|
|
31434
|
+
}
|
|
31435
|
+
const proceed = await clack4.confirm({
|
|
31436
|
+
message: "Overwrite these files with the latest templates?",
|
|
31437
|
+
initialValue: true
|
|
31438
|
+
});
|
|
31439
|
+
if (clack4.isCancel(proceed) || !proceed) {
|
|
31440
|
+
clack4.cancel("Update cancelled.");
|
|
31441
|
+
process.exit(0);
|
|
31442
|
+
}
|
|
31443
|
+
}
|
|
31444
|
+
for (const entry of pendingWrites) {
|
|
31445
|
+
entry.write();
|
|
31446
|
+
}
|
|
31447
|
+
}
|
|
31165
31448
|
syncInstalledPresetManifests(cwd, config);
|
|
31166
31449
|
syncInstalledIntegrationManifests(cwd, config);
|
|
31167
31450
|
const projectPackageJson = readProjectPackageJson2(cwd);
|
|
@@ -31169,11 +31452,11 @@ async function runUpdateCommand(components, options) {
|
|
|
31169
31452
|
(dependency) => !hasDeclaredPackage2(projectPackageJson, dependency)
|
|
31170
31453
|
);
|
|
31171
31454
|
if (missingPackageDependencies.length > 0) {
|
|
31172
|
-
|
|
31455
|
+
clack4.log.warn(
|
|
31173
31456
|
`Updated components require package dependencies that are not declared: ${missingPackageDependencies.join(", ")}. Run 'pnpm betterstart update-deps' before building.`
|
|
31174
31457
|
);
|
|
31175
31458
|
}
|
|
31176
|
-
|
|
31459
|
+
clack4.outro(
|
|
31177
31460
|
`Updated ${updated} component${updated !== 1 ? "s" : ""}${skipped > 0 ? `, ${skipped} skipped` : ""}`
|
|
31178
31461
|
);
|
|
31179
31462
|
}
|
|
@@ -31184,22 +31467,22 @@ function removeLegacyDirectory(dirPath) {
|
|
|
31184
31467
|
}
|
|
31185
31468
|
function validateShadcnPresetOptions(components, options) {
|
|
31186
31469
|
if (options.only && !options.shadcnPreset) {
|
|
31187
|
-
|
|
31470
|
+
clack4.cancel("--only can only be used with --shadcn-preset.");
|
|
31188
31471
|
process.exit(1);
|
|
31189
31472
|
}
|
|
31190
31473
|
if (!options.shadcnPreset) {
|
|
31191
31474
|
return;
|
|
31192
31475
|
}
|
|
31193
31476
|
if (options.list) {
|
|
31194
|
-
|
|
31477
|
+
clack4.cancel("--list cannot be combined with --shadcn-preset.");
|
|
31195
31478
|
process.exit(1);
|
|
31196
31479
|
}
|
|
31197
31480
|
if (options.all) {
|
|
31198
|
-
|
|
31481
|
+
clack4.cancel("--all cannot be combined with --shadcn-preset.");
|
|
31199
31482
|
process.exit(1);
|
|
31200
31483
|
}
|
|
31201
31484
|
if (components.length > 0) {
|
|
31202
|
-
|
|
31485
|
+
clack4.cancel("Component names cannot be combined with --shadcn-preset.");
|
|
31203
31486
|
process.exit(1);
|
|
31204
31487
|
}
|
|
31205
31488
|
}
|
|
@@ -31209,7 +31492,7 @@ function normalizeShadcnPresetOnly(value) {
|
|
|
31209
31492
|
}
|
|
31210
31493
|
const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
|
|
31211
31494
|
if (parts.length !== 1 || parts[0] !== "theme") {
|
|
31212
|
-
|
|
31495
|
+
clack4.cancel(
|
|
31213
31496
|
"Admin-only shadcn preset updates currently support --only theme. Omit --only to apply the full preset."
|
|
31214
31497
|
);
|
|
31215
31498
|
process.exit(1);
|
|
@@ -31233,11 +31516,11 @@ function runShadcnPresetUpdate({
|
|
|
31233
31516
|
...getHostProjectFilesToRestore(cwd)
|
|
31234
31517
|
];
|
|
31235
31518
|
if (!preset) {
|
|
31236
|
-
|
|
31519
|
+
clack4.cancel("--shadcn-preset requires a preset code, preset name, or preset URL.");
|
|
31237
31520
|
process.exit(1);
|
|
31238
31521
|
}
|
|
31239
31522
|
if (!fs45.existsSync(adminGlobalsPath)) {
|
|
31240
|
-
|
|
31523
|
+
clack4.cancel(
|
|
31241
31524
|
`Admin globals file not found at ${path58.relative(cwd, adminGlobalsPath)}. Run 'betterstart update admin-globals' first.`
|
|
31242
31525
|
);
|
|
31243
31526
|
process.exit(1);
|
|
@@ -31247,8 +31530,8 @@ function runShadcnPresetUpdate({
|
|
|
31247
31530
|
filePath,
|
|
31248
31531
|
snapshot: snapshotFile(filePath)
|
|
31249
31532
|
}));
|
|
31250
|
-
|
|
31251
|
-
|
|
31533
|
+
clack4.intro("BetterStart Shadcn Preset");
|
|
31534
|
+
clack4.log.info(`Applying preset to ${path58.join(config.paths.admin, "components/ui")}`);
|
|
31252
31535
|
let failed = false;
|
|
31253
31536
|
try {
|
|
31254
31537
|
fs45.writeFileSync(
|
|
@@ -31270,10 +31553,10 @@ function runShadcnPresetUpdate({
|
|
|
31270
31553
|
}
|
|
31271
31554
|
}
|
|
31272
31555
|
if (failed) {
|
|
31273
|
-
|
|
31556
|
+
clack4.cancel("shadcn preset application failed.");
|
|
31274
31557
|
process.exit(1);
|
|
31275
31558
|
}
|
|
31276
|
-
|
|
31559
|
+
clack4.outro(
|
|
31277
31560
|
only ? `Applied shadcn preset parts (${only}) to the Admin.` : "Applied shadcn preset to Admin UI components and styles."
|
|
31278
31561
|
);
|
|
31279
31562
|
}
|
|
@@ -31309,7 +31592,7 @@ function resolveLocalShadcnBin(cwd) {
|
|
|
31309
31592
|
const binName = process.platform === "win32" ? "shadcn.cmd" : "shadcn";
|
|
31310
31593
|
const shadcnBin = path58.join(cwd, "node_modules", ".bin", binName);
|
|
31311
31594
|
if (!fs45.existsSync(shadcnBin)) {
|
|
31312
|
-
|
|
31595
|
+
clack4.cancel(
|
|
31313
31596
|
`shadcn is not installed in this project. Run 'betterstart update-deps' and try again.`
|
|
31314
31597
|
);
|
|
31315
31598
|
process.exit(1);
|
|
@@ -31349,13 +31632,13 @@ function restoreFile(filePath, snapshot) {
|
|
|
31349
31632
|
|
|
31350
31633
|
// adapters/next/commands/update-deps.ts
|
|
31351
31634
|
import path59 from "path";
|
|
31352
|
-
import * as
|
|
31635
|
+
import * as clack5 from "@clack/prompts";
|
|
31353
31636
|
async function runUpdateDepsCommand(options) {
|
|
31354
31637
|
const cwd = options.cwd ? path59.resolve(options.cwd) : process.cwd();
|
|
31355
|
-
|
|
31638
|
+
clack5.intro("BetterStart Update Dependencies");
|
|
31356
31639
|
const pm = detectPackageManager(cwd);
|
|
31357
|
-
|
|
31358
|
-
const config = await
|
|
31640
|
+
clack5.log.info(`Package manager: ${pm}`);
|
|
31641
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31359
31642
|
const dependencyPlan = getDependencyPlan(
|
|
31360
31643
|
getInstalledPresets(config),
|
|
31361
31644
|
getInstalledIntegrations(config),
|
|
@@ -31378,25 +31661,25 @@ async function runUpdateDepsCommand(options) {
|
|
|
31378
31661
|
s.stop(`Installed ${result.dependencies.length} deps + ${result.devDeps.length} dev deps`);
|
|
31379
31662
|
} else {
|
|
31380
31663
|
s.stop("Dependency install failed");
|
|
31381
|
-
|
|
31664
|
+
clack5.log.error(result.error ?? "Unknown error");
|
|
31382
31665
|
process.exit(1);
|
|
31383
31666
|
}
|
|
31384
|
-
|
|
31667
|
+
clack5.outro("Dependencies updated");
|
|
31385
31668
|
}
|
|
31386
31669
|
|
|
31387
31670
|
// adapters/next/commands/update-styles.ts
|
|
31388
31671
|
import fs46 from "fs";
|
|
31389
31672
|
import path60 from "path";
|
|
31390
|
-
import * as
|
|
31673
|
+
import * as clack6 from "@clack/prompts";
|
|
31391
31674
|
async function runUpdateStylesCommand(options) {
|
|
31392
31675
|
const cwd = options.cwd ? path60.resolve(options.cwd) : process.cwd();
|
|
31393
|
-
|
|
31394
|
-
const config = await
|
|
31676
|
+
clack6.intro("BetterStart Update Styles");
|
|
31677
|
+
const config = await resolveConfigOrExit(cwd);
|
|
31395
31678
|
const adminDir = config.paths.admin;
|
|
31396
31679
|
const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
|
|
31397
31680
|
const targetPath = path60.join(cwd, adminDir, namespace.globalsFile);
|
|
31398
31681
|
if (!fs46.existsSync(targetPath)) {
|
|
31399
|
-
|
|
31682
|
+
clack6.cancel(`${namespace.globalsFile} not found at ${path60.relative(cwd, targetPath)}`);
|
|
31400
31683
|
process.exit(1);
|
|
31401
31684
|
}
|
|
31402
31685
|
fs46.writeFileSync(
|
|
@@ -31404,8 +31687,8 @@ async function runUpdateStylesCommand(options) {
|
|
|
31404
31687
|
applyAdminNamespaceToContent(readTemplate("admin-globals.css"), namespace.segment),
|
|
31405
31688
|
"utf-8"
|
|
31406
31689
|
);
|
|
31407
|
-
|
|
31408
|
-
|
|
31690
|
+
clack6.log.success(`Updated ${path60.relative(cwd, targetPath)}`);
|
|
31691
|
+
clack6.outro("Styles updated");
|
|
31409
31692
|
}
|
|
31410
31693
|
|
|
31411
31694
|
// adapters/next/commands-runtime.ts
|
|
@@ -31428,11 +31711,12 @@ var nextCommandRuntime = {
|
|
|
31428
31711
|
|
|
31429
31712
|
// cli.ts
|
|
31430
31713
|
import { Command as Command2 } from "commander";
|
|
31714
|
+
import pc12 from "picocolors";
|
|
31431
31715
|
var { version } = JSON.parse(
|
|
31432
31716
|
readFileSync(new URL("../package.json", import.meta.url), "utf-8")
|
|
31433
31717
|
);
|
|
31434
31718
|
var program = new Command2();
|
|
31435
|
-
program.name("betterstart").description("Scaffold a preset and integration
|
|
31719
|
+
program.name("betterstart").description("Scaffold a preset- and integration-based Admin into any Next.js 16 application").version(version);
|
|
31436
31720
|
program.hook("preAction", (_command, actionCommand) => {
|
|
31437
31721
|
requireInitializedProject(actionCommand);
|
|
31438
31722
|
});
|
|
@@ -31450,5 +31734,16 @@ program.addCommand(createUninstallCommand(nextCommandRuntime));
|
|
|
31450
31734
|
program.addCommand(createUpdateCommand(nextCommandRuntime));
|
|
31451
31735
|
program.addCommand(createUpdateDepsCommand(nextCommandRuntime));
|
|
31452
31736
|
program.addCommand(createUpdateStylesCommand(nextCommandRuntime));
|
|
31453
|
-
|
|
31737
|
+
try {
|
|
31738
|
+
await program.parseAsync();
|
|
31739
|
+
} catch (error) {
|
|
31740
|
+
if (process.env.BETTERSTART_DEBUG) {
|
|
31741
|
+
console.error(error);
|
|
31742
|
+
} else {
|
|
31743
|
+
const message = error instanceof Error && error.message ? error.message : String(error);
|
|
31744
|
+
p29.log.error(message);
|
|
31745
|
+
p29.log.message(pc12.dim("Re-run with BETTERSTART_DEBUG=1 for the full stack trace."));
|
|
31746
|
+
}
|
|
31747
|
+
process.exit(1);
|
|
31748
|
+
}
|
|
31454
31749
|
//# sourceMappingURL=cli.js.map
|