betterstart-cli 0.0.31 → 0.0.32

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
@@ -21900,6 +21900,155 @@ function openBrowser(url) {
21900
21900
  // adapters/next/init/prompts/plugins.ts
21901
21901
  import { styleText } from "util";
21902
21902
  import * as p11 from "@clack/prompts";
21903
+
21904
+ // adapters/next/init/scaffolders/env.ts
21905
+ import crypto2 from "crypto";
21906
+
21907
+ // adapters/next/init/scaffolders/dev-server.ts
21908
+ import fs26 from "fs";
21909
+ import path32 from "path";
21910
+ var DEFAULT_DEV_PORT = 4242;
21911
+ var DEV_PORT_FLAG_PATTERN = /(?:^|\s)(?:--port(?:=|\s+)|-p(?:=|\s+)?)(\d+)(?=\s|$)/;
21912
+ var DEV_PORT_ENV_PATTERN = /(?:^|\s)PORT=(\d+)(?=\s|$)/;
21913
+ function detectDevPort(cwd) {
21914
+ const pkg = readPackageJson2(cwd);
21915
+ if (!pkg) return DEFAULT_DEV_PORT;
21916
+ return detectDevPortFromScript(pkg.scripts?.dev) ?? DEFAULT_DEV_PORT;
21917
+ }
21918
+ function detectDevPortFromScript(devScript) {
21919
+ if (typeof devScript !== "string") return void 0;
21920
+ const trimmed = devScript.trim();
21921
+ if (trimmed.length === 0) return void 0;
21922
+ const flagMatch = trimmed.match(DEV_PORT_FLAG_PATTERN);
21923
+ const envMatch = trimmed.match(DEV_PORT_ENV_PATTERN);
21924
+ const rawPort = flagMatch?.[1] ?? envMatch?.[1];
21925
+ if (!rawPort) return void 0;
21926
+ const port = Number.parseInt(rawPort, 10);
21927
+ return isValidPort(port) ? port : void 0;
21928
+ }
21929
+ function ensureDefaultDevScriptPort(cwd) {
21930
+ const pkgPath = path32.join(cwd, "package.json");
21931
+ if (!fs26.existsSync(pkgPath)) {
21932
+ return { status: "missing-package", port: DEFAULT_DEV_PORT };
21933
+ }
21934
+ const raw = fs26.readFileSync(pkgPath, "utf-8");
21935
+ let pkg;
21936
+ try {
21937
+ pkg = JSON.parse(raw);
21938
+ } catch {
21939
+ return { status: "missing-package", port: DEFAULT_DEV_PORT };
21940
+ }
21941
+ const devScript = pkg.scripts?.dev;
21942
+ if (typeof devScript !== "string" || devScript.trim().length === 0) {
21943
+ return { status: "missing-script", port: DEFAULT_DEV_PORT };
21944
+ }
21945
+ const explicitPort = detectDevPortFromScript(devScript);
21946
+ if (explicitPort) {
21947
+ return { status: "preserved", port: explicitPort };
21948
+ }
21949
+ if (!isPlainNextDevScript(devScript)) {
21950
+ return { status: "skipped", port: DEFAULT_DEV_PORT };
21951
+ }
21952
+ const normalizedScript = `${normalizeWhitespace(devScript)} --port ${DEFAULT_DEV_PORT}`;
21953
+ pkg.scripts = {
21954
+ ...pkg.scripts,
21955
+ dev: normalizedScript
21956
+ };
21957
+ const newline = raw.includes("\r\n") ? "\r\n" : "\n";
21958
+ const indent = detectIndent(raw);
21959
+ fs26.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, indent)}${newline}`, "utf-8");
21960
+ return { status: "updated", port: DEFAULT_DEV_PORT };
21961
+ }
21962
+ function readPackageJson2(cwd) {
21963
+ const pkgPath = path32.join(cwd, "package.json");
21964
+ if (!fs26.existsSync(pkgPath)) return void 0;
21965
+ try {
21966
+ return JSON.parse(fs26.readFileSync(pkgPath, "utf-8"));
21967
+ } catch {
21968
+ return void 0;
21969
+ }
21970
+ }
21971
+ function isPlainNextDevScript(devScript) {
21972
+ const normalized = normalizeWhitespace(devScript);
21973
+ return normalized === "next dev" || normalized === "next dev --turbopack" || normalized === "next dev --turbo";
21974
+ }
21975
+ function normalizeWhitespace(value) {
21976
+ return value.trim().replace(/\s+/g, " ");
21977
+ }
21978
+ function detectIndent(raw) {
21979
+ const indentMatch = raw.match(/^[ \t]+(?=")/m);
21980
+ return indentMatch?.[0] ?? 2;
21981
+ }
21982
+ function isValidPort(port) {
21983
+ return Number.isInteger(port) && port > 0 && port <= 65535;
21984
+ }
21985
+
21986
+ // adapters/next/init/scaffolders/env.ts
21987
+ var LEGACY_DEFAULT_AUTH_URL = "http://localhost:3000";
21988
+ function getCoreEnvSections(databaseUrl, devPort, namespace) {
21989
+ const authSecret = crypto2.randomBytes(32).toString("base64");
21990
+ return [
21991
+ {
21992
+ header: "Database (PostgreSQL)",
21993
+ vars: [{ key: "DATABASE_URL", value: databaseUrl ?? "postgresql://..." }]
21994
+ },
21995
+ {
21996
+ header: "Authentication",
21997
+ vars: [
21998
+ { key: "BETTERSTART_AUTH_SECRET", value: authSecret },
21999
+ { key: "BETTERSTART_AUTH_URL", value: `http://localhost:${devPort}` },
22000
+ { key: "BETTERSTART_AUTH_BASE_PATH", value: `/api/${namespace}/auth` }
22001
+ ]
22002
+ }
22003
+ ];
22004
+ }
22005
+ function shouldOverwriteAuthUrl(cwd, devPort) {
22006
+ const existingAuthUrl = readEnvVar(cwd, "BETTERSTART_AUTH_URL");
22007
+ if (!existingAuthUrl) return false;
22008
+ return existingAuthUrl === LEGACY_DEFAULT_AUTH_URL && existingAuthUrl !== `http://localhost:${devPort}`;
22009
+ }
22010
+ function persistDatabaseUrl(cwd, databaseUrl) {
22011
+ appendEnvVars(
22012
+ cwd,
22013
+ [
22014
+ {
22015
+ header: "Database (PostgreSQL)",
22016
+ vars: [{ key: "DATABASE_URL", value: databaseUrl }]
22017
+ }
22018
+ ],
22019
+ /* @__PURE__ */ new Set(["DATABASE_URL"])
22020
+ );
22021
+ }
22022
+ function persistBlobReadWriteToken(cwd, token) {
22023
+ appendEnvVars(
22024
+ cwd,
22025
+ [
22026
+ {
22027
+ header: "Storage (Vercel Blob)",
22028
+ vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: token }]
22029
+ }
22030
+ ],
22031
+ /* @__PURE__ */ new Set(["BLOB_READ_WRITE_TOKEN"])
22032
+ );
22033
+ }
22034
+ function scaffoldEnv(cwd, options) {
22035
+ const devPort = detectDevPort(cwd);
22036
+ const sections = getCoreEnvSections(options.databaseUrl, devPort, options.namespace ?? "admin");
22037
+ const authBasePath = `/api/${options.namespace ?? "admin"}/auth`;
22038
+ const overwrite = /* @__PURE__ */ new Set();
22039
+ if (options.databaseUrl) {
22040
+ overwrite.add("DATABASE_URL");
22041
+ }
22042
+ if (shouldOverwriteAuthUrl(cwd, devPort)) {
22043
+ overwrite.add("BETTERSTART_AUTH_URL");
22044
+ }
22045
+ if (readEnvVar(cwd, "BETTERSTART_AUTH_BASE_PATH") !== authBasePath) {
22046
+ overwrite.add("BETTERSTART_AUTH_BASE_PATH");
22047
+ }
22048
+ return appendEnvVars(cwd, sections, overwrite);
22049
+ }
22050
+
22051
+ // adapters/next/init/prompts/plugins.ts
21903
22052
  async function promptPlugins(cwd, options = {}) {
21904
22053
  const sections = [];
21905
22054
  const overwriteKeys = /* @__PURE__ */ new Set();
@@ -21938,6 +22087,8 @@ async function promptPlugins(cwd, options = {}) {
21938
22087
  const existingToken = readEnvVar(cwd, "BLOB_READ_WRITE_TOKEN")?.trim();
21939
22088
  const flow = !existingToken && options.provisionVercelBlob ? await options.provisionVercelBlob() : void 0;
21940
22089
  if (flow?.ok && flow.token) {
22090
+ persistBlobReadWriteToken(cwd, flow.token);
22091
+ p11.log.success("Saved BLOB_READ_WRITE_TOKEN to .env.local");
21941
22092
  sections.push({
21942
22093
  header: "Storage (Vercel Blob)",
21943
22094
  vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
@@ -22045,53 +22196,53 @@ async function promptProject(defaultName) {
22045
22196
  }
22046
22197
 
22047
22198
  // adapters/next/init/scaffolders/api-routes.ts
22048
- import path33 from "path";
22199
+ import path34 from "path";
22049
22200
 
22050
22201
  // core-engine/utils/fs.ts
22051
- import fs26 from "fs";
22052
- import path32 from "path";
22202
+ import fs27 from "fs";
22203
+ import path33 from "path";
22053
22204
  import fse from "fs-extra";
22054
22205
  function ensureDir(dirPath) {
22055
22206
  fse.ensureDirSync(dirPath);
22056
22207
  }
22057
22208
  function safeWriteFile(filePath, content, force = false) {
22058
- if (!force && fs26.existsSync(filePath)) {
22209
+ if (!force && fs27.existsSync(filePath)) {
22059
22210
  return false;
22060
22211
  }
22061
- ensureDir(path32.dirname(filePath));
22062
- fs26.writeFileSync(filePath, content, "utf-8");
22212
+ ensureDir(path33.dirname(filePath));
22213
+ fs27.writeFileSync(filePath, content, "utf-8");
22063
22214
  return true;
22064
22215
  }
22065
22216
 
22066
22217
  // adapters/next/init/scaffolders/api-routes.ts
22067
22218
  function scaffoldApiRoutes({ cwd, config }) {
22068
22219
  const created = [];
22069
- const apiDir = path33.resolve(cwd, config.paths.api);
22220
+ const apiDir = path34.resolve(cwd, config.paths.api);
22070
22221
  const namespace = config.frameworkConfig.next.namespace;
22071
22222
  function write(relPath, content) {
22072
- const fullPath = path33.join(apiDir, relPath);
22073
- ensureDir(path33.dirname(fullPath));
22223
+ const fullPath = path34.join(apiDir, relPath);
22224
+ ensureDir(path34.dirname(fullPath));
22074
22225
  const versionedContent = applyNextCacheApiForVersion(content, config.nextMajorVersion);
22075
22226
  if (safeWriteFile(fullPath, applyAdminNamespaceToContent(versionedContent, namespace))) {
22076
- created.push(path33.join(config.paths.api, relPath));
22227
+ created.push(path34.join(config.paths.api, relPath));
22077
22228
  }
22078
22229
  }
22079
- write(path33.join("auth", "[...all]", "route.ts"), readTemplate("api/auth-route.ts"));
22080
- write(path33.join("upload", "route.ts"), readTemplate("api/upload-route.ts"));
22230
+ write(path34.join("auth", "[...all]", "route.ts"), readTemplate("api/auth-route.ts"));
22231
+ write(path34.join("upload", "route.ts"), readTemplate("api/upload-route.ts"));
22081
22232
  return created;
22082
22233
  }
22083
22234
 
22084
22235
  // adapters/next/init/scaffolders/auth.ts
22085
- import path34 from "path";
22236
+ import path35 from "path";
22086
22237
  function scaffoldAuth({ cwd, config }) {
22087
22238
  const created = [];
22088
- const authDir = path34.resolve(cwd, config.paths.admin, "lib", "actions", "auth");
22239
+ const authDir = path35.resolve(cwd, config.paths.admin, "lib", "actions", "auth");
22089
22240
  const namespace = config.frameworkConfig.next.namespace;
22090
22241
  function write(filename, content) {
22091
22242
  const outputFile = applyAdminNamespaceToPath(filename, namespace);
22092
- const fullPath = path34.join(authDir, outputFile);
22243
+ const fullPath = path35.join(authDir, outputFile);
22093
22244
  if (safeWriteFile(fullPath, applyAdminNamespaceToContent(content, namespace))) {
22094
- created.push(path34.join(config.paths.admin, "lib", "actions", "auth", outputFile));
22245
+ created.push(path35.join(config.paths.admin, "lib", "actions", "auth", outputFile));
22095
22246
  }
22096
22247
  }
22097
22248
  write("server.ts", readTemplate("lib/actions/auth/auth.ts"));
@@ -22101,7 +22252,7 @@ function scaffoldAuth({ cwd, config }) {
22101
22252
  }
22102
22253
 
22103
22254
  // adapters/next/init/scaffolders/base.ts
22104
- import path35 from "path";
22255
+ import path36 from "path";
22105
22256
  function scaffoldBase({
22106
22257
  cwd,
22107
22258
  config,
@@ -22111,58 +22262,58 @@ function scaffoldBase({
22111
22262
  const adminDirs = [
22112
22263
  config.paths.admin,
22113
22264
  config.paths.schemas,
22114
- path35.join(config.paths.admin, "lib"),
22115
- path35.join(config.paths.admin, "lib", "db"),
22116
- path35.join(config.paths.admin, "lib", "db", "core"),
22117
- path35.join(config.paths.admin, "lib", "db", "migrations"),
22118
- path35.join(config.paths.admin, "lib", "actions"),
22119
- path35.join(config.paths.admin, "lib", "actions", "auth"),
22120
- path35.join(config.paths.admin, "lib", "actions", "email"),
22121
- path35.join(config.paths.admin, "lib", "actions", "forms"),
22122
- path35.join(config.paths.admin, "lib", "actions", "media"),
22123
- path35.join(config.paths.admin, "lib", "actions", "profile"),
22124
- path35.join(config.paths.admin, "lib", "actions", "storage"),
22125
- path35.join(config.paths.admin, "lib", "actions", "upload"),
22126
- path35.join(config.paths.admin, "lib", "actions", "users"),
22127
- path35.join(config.paths.admin, "hooks"),
22128
- path35.join(config.paths.admin, "components", "ui"),
22129
- path35.join(config.paths.admin, "components", "custom"),
22130
- path35.join(config.paths.admin, "components", "layouts"),
22131
- path35.join(config.paths.admin, "components", "shared"),
22132
- path35.join(config.paths.admin, "components", "shared", "data-table"),
22133
- path35.join(config.paths.admin, "components", "shared", "media"),
22134
- path35.join(config.paths.admin, "data"),
22135
- path35.join(config.paths.admin, "data", "navigation"),
22136
- path35.join(config.paths.admin, "types"),
22137
- path35.join(config.paths.admin, "utils"),
22138
- path35.join(config.paths.admin, "utils", "core")
22265
+ path36.join(config.paths.admin, "lib"),
22266
+ path36.join(config.paths.admin, "lib", "db"),
22267
+ path36.join(config.paths.admin, "lib", "db", "core"),
22268
+ path36.join(config.paths.admin, "lib", "db", "migrations"),
22269
+ path36.join(config.paths.admin, "lib", "actions"),
22270
+ path36.join(config.paths.admin, "lib", "actions", "auth"),
22271
+ path36.join(config.paths.admin, "lib", "actions", "email"),
22272
+ path36.join(config.paths.admin, "lib", "actions", "forms"),
22273
+ path36.join(config.paths.admin, "lib", "actions", "media"),
22274
+ path36.join(config.paths.admin, "lib", "actions", "profile"),
22275
+ path36.join(config.paths.admin, "lib", "actions", "storage"),
22276
+ path36.join(config.paths.admin, "lib", "actions", "upload"),
22277
+ path36.join(config.paths.admin, "lib", "actions", "users"),
22278
+ path36.join(config.paths.admin, "hooks"),
22279
+ path36.join(config.paths.admin, "components", "ui"),
22280
+ path36.join(config.paths.admin, "components", "custom"),
22281
+ path36.join(config.paths.admin, "components", "layouts"),
22282
+ path36.join(config.paths.admin, "components", "shared"),
22283
+ path36.join(config.paths.admin, "components", "shared", "data-table"),
22284
+ path36.join(config.paths.admin, "components", "shared", "media"),
22285
+ path36.join(config.paths.admin, "data"),
22286
+ path36.join(config.paths.admin, "data", "navigation"),
22287
+ path36.join(config.paths.admin, "types"),
22288
+ path36.join(config.paths.admin, "utils"),
22289
+ path36.join(config.paths.admin, "utils", "core")
22139
22290
  ];
22140
22291
  if (includeEmailTemplatesDir) {
22141
- adminDirs.push(path35.join(config.paths.admin, "components", "email-templates"));
22292
+ adminDirs.push(path36.join(config.paths.admin, "components", "email-templates"));
22142
22293
  }
22143
22294
  for (const dir of adminDirs) {
22144
- ensureDir(path35.resolve(cwd, dir));
22295
+ ensureDir(path36.resolve(cwd, dir));
22145
22296
  }
22146
- const adminDir = path35.dirname(config.paths.pages);
22297
+ const adminDir = path36.dirname(config.paths.pages);
22147
22298
  const appDirs = [
22148
22299
  config.paths.pages,
22149
22300
  config.paths.login,
22150
22301
  config.paths.api,
22151
- path35.join(adminDir, "(account)", "profile")
22302
+ path36.join(adminDir, "(account)", "profile")
22152
22303
  ];
22153
22304
  for (const dir of appDirs) {
22154
- ensureDir(path35.resolve(cwd, dir));
22305
+ ensureDir(path36.resolve(cwd, dir));
22155
22306
  }
22156
22307
  const configContent = serializeConfig(config);
22157
- if (safeWriteFile(path35.resolve(cwd, "admin.config.ts"), configContent)) {
22308
+ if (safeWriteFile(path36.resolve(cwd, "admin.config.ts"), configContent)) {
22158
22309
  created.push("admin.config.ts");
22159
22310
  }
22160
22311
  return created;
22161
22312
  }
22162
22313
 
22163
22314
  // adapters/next/init/scaffolders/biome.ts
22164
- import fs27 from "fs";
22165
- import path36 from "path";
22315
+ import fs28 from "fs";
22316
+ import path37 from "path";
22166
22317
  function scaffoldBiome(cwd, linter) {
22167
22318
  if (linter.type !== "none") {
22168
22319
  return {
@@ -22170,8 +22321,8 @@ function scaffoldBiome(cwd, linter) {
22170
22321
  skippedReason: `${linter.type} already configured (${linter.configFile})`
22171
22322
  };
22172
22323
  }
22173
- const configPath = path36.join(cwd, "biome.json");
22174
- if (fs27.existsSync(configPath)) {
22324
+ const configPath = path37.join(cwd, "biome.json");
22325
+ if (fs28.existsSync(configPath)) {
22175
22326
  return { installed: false, skippedReason: "biome.json already exists" };
22176
22327
  }
22177
22328
  const config = {
@@ -22234,13 +22385,13 @@ function scaffoldBiome(cwd, linter) {
22234
22385
  ]
22235
22386
  }
22236
22387
  };
22237
- fs27.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
22388
+ fs28.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
22238
22389
  `, "utf-8");
22239
22390
  return { installed: true, skippedReason: null };
22240
22391
  }
22241
22392
 
22242
22393
  // adapters/next/init/scaffolders/components.ts
22243
- import path37 from "path";
22394
+ import path38 from "path";
22244
22395
 
22245
22396
  // adapters/next/init/templates/app/admin.ts
22246
22397
  function adminDataTemplate(projectName, namespaceValue = DEFAULT_ADMIN_NAMESPACE) {
@@ -22252,17 +22403,17 @@ function adminDataTemplate(projectName, namespaceValue = DEFAULT_ADMIN_NAMESPACE
22252
22403
  }
22253
22404
 
22254
22405
  // adapters/next/init/scaffolders/components.ts
22255
- import fs28 from "fs-extra";
22406
+ import fs29 from "fs-extra";
22256
22407
  function scaffoldComponents({ cwd, config }) {
22257
- const admin = path37.resolve(cwd, config.paths.admin);
22408
+ const admin = path38.resolve(cwd, config.paths.admin);
22258
22409
  const created = [];
22259
22410
  const namespace = config.frameworkConfig.next.namespace;
22260
22411
  function write(relPath, content) {
22261
22412
  const namespacedRelPath = applyAdminNamespaceToPath(relPath, namespace);
22262
- const fullPath = path37.join(admin, namespacedRelPath);
22413
+ const fullPath = path38.join(admin, namespacedRelPath);
22263
22414
  const versionedContent = applyNextCacheApiForVersion(content, config.nextMajorVersion);
22264
22415
  if (safeWriteFile(fullPath, applyAdminNamespaceToContent(versionedContent, namespace))) {
22265
- created.push(path37.join(config.paths.admin, namespacedRelPath));
22416
+ created.push(path38.join(config.paths.admin, namespacedRelPath));
22266
22417
  }
22267
22418
  }
22268
22419
  write("admin-globals.css", readTemplate("admin-globals.css"));
@@ -22695,49 +22846,49 @@ function copyCustomTemplates(cwd, config) {
22695
22846
  }
22696
22847
  function copyStaticComponentTemplates(cwd, config, sourceDirectory, targetDirectory) {
22697
22848
  const created = [];
22698
- const destDir = path37.resolve(cwd, config.paths.admin, "components", targetDirectory);
22849
+ const destDir = path38.resolve(cwd, config.paths.admin, "components", targetDirectory);
22699
22850
  const srcDir = resolveCliAssetPath("shared-assets", "react-admin", sourceDirectory);
22700
- if (!fs28.existsSync(srcDir)) {
22851
+ if (!fs29.existsSync(srcDir)) {
22701
22852
  return created;
22702
22853
  }
22703
- fs28.ensureDirSync(destDir);
22854
+ fs29.ensureDirSync(destDir);
22704
22855
  if (sourceDirectory === "custom") {
22705
22856
  copyStaticComponentTemplatesRecursive(
22706
22857
  srcDir,
22707
22858
  destDir,
22708
22859
  "",
22709
- path37.join(config.paths.admin, "components", targetDirectory),
22860
+ path38.join(config.paths.admin, "components", targetDirectory),
22710
22861
  config.frameworkConfig.next.namespace,
22711
22862
  created
22712
22863
  );
22713
22864
  return created;
22714
22865
  }
22715
- const files = fs28.readdirSync(srcDir, { withFileTypes: true }).filter(
22866
+ const files = fs29.readdirSync(srcDir, { withFileTypes: true }).filter(
22716
22867
  (entry) => entry.isFile() && (entry.name.endsWith(".tsx") || entry.name.endsWith(".ts"))
22717
22868
  ).map((entry) => entry.name);
22718
22869
  for (const file of files) {
22719
22870
  const namespacedFile = applyAdminNamespaceToPath(file, config.frameworkConfig.next.namespace);
22720
- const destPath = path37.join(destDir, namespacedFile);
22721
- if (!fs28.existsSync(destPath)) {
22722
- const content = fs28.readFileSync(path37.join(srcDir, file), "utf-8");
22723
- fs28.writeFileSync(
22871
+ const destPath = path38.join(destDir, namespacedFile);
22872
+ if (!fs29.existsSync(destPath)) {
22873
+ const content = fs29.readFileSync(path38.join(srcDir, file), "utf-8");
22874
+ fs29.writeFileSync(
22724
22875
  destPath,
22725
22876
  applyAdminNamespaceToContent(content, config.frameworkConfig.next.namespace),
22726
22877
  "utf-8"
22727
22878
  );
22728
- created.push(path37.join(config.paths.admin, "components", targetDirectory, namespacedFile));
22879
+ created.push(path38.join(config.paths.admin, "components", targetDirectory, namespacedFile));
22729
22880
  }
22730
22881
  }
22731
22882
  return created;
22732
22883
  }
22733
22884
  function copyStaticComponentTemplatesRecursive(srcDir, destDir, relBase, createdPrefix, namespace, created) {
22734
- const entries = fs28.readdirSync(srcDir, { withFileTypes: true });
22885
+ const entries = fs29.readdirSync(srcDir, { withFileTypes: true });
22735
22886
  for (const entry of entries) {
22736
- const rel = path37.join(relBase, entry.name);
22737
- const srcPath = path37.join(srcDir, entry.name);
22738
- const destPath = path37.join(destDir, entry.name);
22887
+ const rel = path38.join(relBase, entry.name);
22888
+ const srcPath = path38.join(srcDir, entry.name);
22889
+ const destPath = path38.join(destDir, entry.name);
22739
22890
  if (entry.isDirectory()) {
22740
- const namespacedDestPath2 = path37.join(
22891
+ const namespacedDestPath2 = path38.join(
22741
22892
  destDir,
22742
22893
  applyAdminNamespaceToPath(entry.name, namespace)
22743
22894
  );
@@ -22755,37 +22906,37 @@ function copyStaticComponentTemplatesRecursive(srcDir, destDir, relBase, created
22755
22906
  continue;
22756
22907
  }
22757
22908
  const namespacedRel = applyAdminNamespaceToPath(rel, namespace);
22758
- const namespacedDestPath = path37.join(
22759
- path37.dirname(destPath),
22909
+ const namespacedDestPath = path38.join(
22910
+ path38.dirname(destPath),
22760
22911
  applyAdminNamespaceToPath(entry.name, namespace)
22761
22912
  );
22762
- if (!fs28.existsSync(namespacedDestPath)) {
22763
- fs28.ensureDirSync(path37.dirname(namespacedDestPath));
22764
- const content = fs28.readFileSync(srcPath, "utf-8");
22765
- fs28.writeFileSync(
22913
+ if (!fs29.existsSync(namespacedDestPath)) {
22914
+ fs29.ensureDirSync(path38.dirname(namespacedDestPath));
22915
+ const content = fs29.readFileSync(srcPath, "utf-8");
22916
+ fs29.writeFileSync(
22766
22917
  namespacedDestPath,
22767
22918
  applyAdminNamespaceToContent(content, namespace),
22768
22919
  "utf-8"
22769
22920
  );
22770
- created.push(path37.join(createdPrefix, namespacedRel));
22921
+ created.push(path38.join(createdPrefix, namespacedRel));
22771
22922
  }
22772
22923
  }
22773
22924
  }
22774
22925
  function copySchemaMetaschema(cwd, config) {
22775
22926
  const created = [];
22776
22927
  const srcPath = resolveCliAssetPath("shared-assets", "react-admin", "schema.json");
22777
- const destPath = path37.resolve(cwd, config.paths.schemas, "schema.json");
22778
- if (fs28.existsSync(srcPath) && !fs28.existsSync(destPath)) {
22779
- fs28.ensureDirSync(path37.dirname(destPath));
22780
- fs28.copyFileSync(srcPath, destPath);
22781
- created.push(path37.join(config.paths.schemas, "schema.json"));
22928
+ const destPath = path38.resolve(cwd, config.paths.schemas, "schema.json");
22929
+ if (fs29.existsSync(srcPath) && !fs29.existsSync(destPath)) {
22930
+ fs29.ensureDirSync(path38.dirname(destPath));
22931
+ fs29.copyFileSync(srcPath, destPath);
22932
+ created.push(path38.join(config.paths.schemas, "schema.json"));
22782
22933
  }
22783
22934
  return created;
22784
22935
  }
22785
22936
 
22786
22937
  // adapters/next/init/scaffolders/core-schemas.ts
22787
- import fs29 from "fs";
22788
- import path38 from "path";
22938
+ import fs30 from "fs";
22939
+ import path39 from "path";
22789
22940
 
22790
22941
  // adapters/next/init/templates/core/default-settings.ts
22791
22942
  function defaultSettingsSchema() {
@@ -22865,9 +23016,9 @@ function scaffoldCoreSchemas({
22865
23016
  };
22866
23017
  const schemaContent = defaultSettingsSchema();
22867
23018
  const schemaName = "settings";
22868
- const schemaPath = path38.join(cwd, config.paths.schemas, `${schemaName}.json`);
22869
- fs29.mkdirSync(path38.dirname(schemaPath), { recursive: true });
22870
- fs29.writeFileSync(schemaPath, schemaContent, "utf-8");
23019
+ const schemaPath = path39.join(cwd, config.paths.schemas, `${schemaName}.json`);
23020
+ fs30.mkdirSync(path39.dirname(schemaPath), { recursive: true });
23021
+ fs30.writeFileSync(schemaPath, schemaContent, "utf-8");
22871
23022
  result.schemas.push(`${schemaName}.json`);
22872
23023
  ensureSnapshotGitFiles(cwd);
22873
23024
  try {
@@ -22912,21 +23063,21 @@ function scaffoldCoreSchemas({
22912
23063
  }
22913
23064
 
22914
23065
  // adapters/next/init/scaffolders/database.ts
22915
- import path39 from "path";
23066
+ import path40 from "path";
22916
23067
  function scaffoldDatabase({ cwd, config }) {
22917
23068
  const created = [];
22918
- const dbDir = path39.resolve(cwd, config.paths.admin, "lib", "db");
23069
+ const dbDir = path40.resolve(cwd, config.paths.admin, "lib", "db");
22919
23070
  const namespace = config.frameworkConfig.next.namespace;
22920
23071
  function write(filename, content) {
22921
- const fullPath = path39.join(dbDir, filename);
23072
+ const fullPath = path40.join(dbDir, filename);
22922
23073
  if (safeWriteFile(fullPath, applyAdminNamespaceToContent(content, namespace))) {
22923
- created.push(path39.join(config.paths.admin, "lib", "db", filename));
23074
+ created.push(path40.join(config.paths.admin, "lib", "db", filename));
22924
23075
  }
22925
23076
  }
22926
23077
  write("client.ts", readTemplate("lib/db/client.ts"));
22927
23078
  write("core/schema.ts", readTemplate("lib/db/core/schema.ts"));
22928
23079
  write("schema.ts", readTemplate("lib/db/schema.ts"));
22929
- const drizzleConfigPath = path39.resolve(cwd, "drizzle.config.ts");
23080
+ const drizzleConfigPath = path40.resolve(cwd, "drizzle.config.ts");
22930
23081
  if (safeWriteFile(
22931
23082
  drizzleConfigPath,
22932
23083
  applyAdminNamespaceToContent(readTemplate("drizzle.config.ts"), namespace)
@@ -22936,139 +23087,6 @@ function scaffoldDatabase({ cwd, config }) {
22936
23087
  return created;
22937
23088
  }
22938
23089
 
22939
- // adapters/next/init/scaffolders/dev-server.ts
22940
- import fs30 from "fs";
22941
- import path40 from "path";
22942
- var DEFAULT_DEV_PORT = 4242;
22943
- var DEV_PORT_FLAG_PATTERN = /(?:^|\s)(?:--port(?:=|\s+)|-p(?:=|\s+)?)(\d+)(?=\s|$)/;
22944
- var DEV_PORT_ENV_PATTERN = /(?:^|\s)PORT=(\d+)(?=\s|$)/;
22945
- function detectDevPort(cwd) {
22946
- const pkg = readPackageJson2(cwd);
22947
- if (!pkg) return DEFAULT_DEV_PORT;
22948
- return detectDevPortFromScript(pkg.scripts?.dev) ?? DEFAULT_DEV_PORT;
22949
- }
22950
- function detectDevPortFromScript(devScript) {
22951
- if (typeof devScript !== "string") return void 0;
22952
- const trimmed = devScript.trim();
22953
- if (trimmed.length === 0) return void 0;
22954
- const flagMatch = trimmed.match(DEV_PORT_FLAG_PATTERN);
22955
- const envMatch = trimmed.match(DEV_PORT_ENV_PATTERN);
22956
- const rawPort = flagMatch?.[1] ?? envMatch?.[1];
22957
- if (!rawPort) return void 0;
22958
- const port = Number.parseInt(rawPort, 10);
22959
- return isValidPort(port) ? port : void 0;
22960
- }
22961
- function ensureDefaultDevScriptPort(cwd) {
22962
- const pkgPath = path40.join(cwd, "package.json");
22963
- if (!fs30.existsSync(pkgPath)) {
22964
- return { status: "missing-package", port: DEFAULT_DEV_PORT };
22965
- }
22966
- const raw = fs30.readFileSync(pkgPath, "utf-8");
22967
- let pkg;
22968
- try {
22969
- pkg = JSON.parse(raw);
22970
- } catch {
22971
- return { status: "missing-package", port: DEFAULT_DEV_PORT };
22972
- }
22973
- const devScript = pkg.scripts?.dev;
22974
- if (typeof devScript !== "string" || devScript.trim().length === 0) {
22975
- return { status: "missing-script", port: DEFAULT_DEV_PORT };
22976
- }
22977
- const explicitPort = detectDevPortFromScript(devScript);
22978
- if (explicitPort) {
22979
- return { status: "preserved", port: explicitPort };
22980
- }
22981
- if (!isPlainNextDevScript(devScript)) {
22982
- return { status: "skipped", port: DEFAULT_DEV_PORT };
22983
- }
22984
- const normalizedScript = `${normalizeWhitespace(devScript)} --port ${DEFAULT_DEV_PORT}`;
22985
- pkg.scripts = {
22986
- ...pkg.scripts,
22987
- dev: normalizedScript
22988
- };
22989
- const newline = raw.includes("\r\n") ? "\r\n" : "\n";
22990
- const indent = detectIndent(raw);
22991
- fs30.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, indent)}${newline}`, "utf-8");
22992
- return { status: "updated", port: DEFAULT_DEV_PORT };
22993
- }
22994
- function readPackageJson2(cwd) {
22995
- const pkgPath = path40.join(cwd, "package.json");
22996
- if (!fs30.existsSync(pkgPath)) return void 0;
22997
- try {
22998
- return JSON.parse(fs30.readFileSync(pkgPath, "utf-8"));
22999
- } catch {
23000
- return void 0;
23001
- }
23002
- }
23003
- function isPlainNextDevScript(devScript) {
23004
- const normalized = normalizeWhitespace(devScript);
23005
- return normalized === "next dev" || normalized === "next dev --turbopack" || normalized === "next dev --turbo";
23006
- }
23007
- function normalizeWhitespace(value) {
23008
- return value.trim().replace(/\s+/g, " ");
23009
- }
23010
- function detectIndent(raw) {
23011
- const indentMatch = raw.match(/^[ \t]+(?=")/m);
23012
- return indentMatch?.[0] ?? 2;
23013
- }
23014
- function isValidPort(port) {
23015
- return Number.isInteger(port) && port > 0 && port <= 65535;
23016
- }
23017
-
23018
- // adapters/next/init/scaffolders/env.ts
23019
- import crypto2 from "crypto";
23020
- var LEGACY_DEFAULT_AUTH_URL = "http://localhost:3000";
23021
- function getCoreEnvSections(databaseUrl, devPort, namespace) {
23022
- const authSecret = crypto2.randomBytes(32).toString("base64");
23023
- return [
23024
- {
23025
- header: "Database (PostgreSQL)",
23026
- vars: [{ key: "DATABASE_URL", value: databaseUrl ?? "postgresql://..." }]
23027
- },
23028
- {
23029
- header: "Authentication",
23030
- vars: [
23031
- { key: "BETTERSTART_AUTH_SECRET", value: authSecret },
23032
- { key: "BETTERSTART_AUTH_URL", value: `http://localhost:${devPort}` },
23033
- { key: "BETTERSTART_AUTH_BASE_PATH", value: `/api/${namespace}/auth` }
23034
- ]
23035
- }
23036
- ];
23037
- }
23038
- function shouldOverwriteAuthUrl(cwd, devPort) {
23039
- const existingAuthUrl = readEnvVar(cwd, "BETTERSTART_AUTH_URL");
23040
- if (!existingAuthUrl) return false;
23041
- return existingAuthUrl === LEGACY_DEFAULT_AUTH_URL && existingAuthUrl !== `http://localhost:${devPort}`;
23042
- }
23043
- function persistDatabaseUrl(cwd, databaseUrl) {
23044
- appendEnvVars(
23045
- cwd,
23046
- [
23047
- {
23048
- header: "Database (PostgreSQL)",
23049
- vars: [{ key: "DATABASE_URL", value: databaseUrl }]
23050
- }
23051
- ],
23052
- /* @__PURE__ */ new Set(["DATABASE_URL"])
23053
- );
23054
- }
23055
- function scaffoldEnv(cwd, options) {
23056
- const devPort = detectDevPort(cwd);
23057
- const sections = getCoreEnvSections(options.databaseUrl, devPort, options.namespace ?? "admin");
23058
- const authBasePath = `/api/${options.namespace ?? "admin"}/auth`;
23059
- const overwrite = /* @__PURE__ */ new Set();
23060
- if (options.databaseUrl) {
23061
- overwrite.add("DATABASE_URL");
23062
- }
23063
- if (shouldOverwriteAuthUrl(cwd, devPort)) {
23064
- overwrite.add("BETTERSTART_AUTH_URL");
23065
- }
23066
- if (readEnvVar(cwd, "BETTERSTART_AUTH_BASE_PATH") !== authBasePath) {
23067
- overwrite.add("BETTERSTART_AUTH_BASE_PATH");
23068
- }
23069
- return appendEnvVars(cwd, sections, overwrite);
23070
- }
23071
-
23072
23090
  // adapters/next/init/scaffolders/layout.ts
23073
23091
  import path41 from "path";
23074
23092
  function scaffoldLayout({ cwd, config }) {
@@ -25627,6 +25645,8 @@ async function runInitCommand(name, options) {
25627
25645
  env: process.env
25628
25646
  });
25629
25647
  if (flow.ok && flow.token) {
25648
+ persistBlobReadWriteToken(cwd, flow.token);
25649
+ p17.log.success(`Saved BLOB_READ_WRITE_TOKEN to ${pc7.cyan(".env.local")}`);
25630
25650
  collectedIntegrationConfig.sections.push({
25631
25651
  header: "Storage (Vercel Blob)",
25632
25652
  vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
@@ -25828,8 +25848,9 @@ async function runInitCommand(name, options) {
25828
25848
  config: resolvedPluginInstallResult.config,
25829
25849
  pm,
25830
25850
  integrationIds: pluginSelection.integrations,
25831
- // Secrets were already collected up front and written to .env.local,
25832
- // so the install runs without prompting (rule: no mid-scaffold input).
25851
+ // Secrets were already collected up front and persisted/written to
25852
+ // .env.local, so the install runs without prompting (rule: no
25853
+ // mid-scaffold input).
25833
25854
  interactive: false,
25834
25855
  includeBiome: project2.linter.type === "none"
25835
25856
  });