@nextsparkjs/cli 0.1.0-beta.5 → 0.1.0-beta.51

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
@@ -10,8 +10,9 @@ import {
10
10
  } from "./chunk-ALB2C27N.js";
11
11
 
12
12
  // src/cli.ts
13
+ import { config } from "dotenv";
13
14
  import { Command } from "commander";
14
- import chalk15 from "chalk";
15
+ import chalk16 from "chalk";
15
16
 
16
17
  // src/commands/dev.ts
17
18
  import { spawn } from "child_process";
@@ -61,8 +62,8 @@ async function devCommand(options) {
61
62
  spinner.succeed(`Core found at: ${coreDir} (${mode} mode)`);
62
63
  const processes = [];
63
64
  if (options.registry) {
64
- console.log(chalk.blue("\n[Registry] Starting registry watcher..."));
65
- const registryProcess = spawn("node", ["scripts/registry-watch.js"], {
65
+ console.log(chalk.blue("\n[Registry] Starting registry builder with watch mode..."));
66
+ const registryProcess = spawn("node", ["scripts/build/registry.mjs", "--watch"], {
66
67
  cwd: coreDir,
67
68
  stdio: "inherit",
68
69
  env: {
@@ -80,6 +81,7 @@ async function devCommand(options) {
80
81
  const devProcess = spawn("npx", ["next", "dev", "-p", options.port], {
81
82
  cwd: projectRoot,
82
83
  stdio: "inherit",
84
+ shell: true,
83
85
  env: {
84
86
  ...process.env,
85
87
  NEXTSPARK_CORE_DIR: coreDir
@@ -179,6 +181,7 @@ async function buildCommand(options) {
179
181
  const buildProcess = spawn2("npx", ["next", "build"], {
180
182
  cwd: projectRoot,
181
183
  stdio: "inherit",
184
+ shell: true,
182
185
  env: {
183
186
  ...projectEnv,
184
187
  ...process.env,
@@ -422,8 +425,8 @@ Watcher exited with code ${code}`));
422
425
  }
423
426
 
424
427
  // src/commands/init.ts
425
- import { existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync6 } from "fs";
426
- import { join as join7 } from "path";
428
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync7 } from "fs";
429
+ import { join as join8 } from "path";
427
430
  import chalk12 from "chalk";
428
431
  import ora8 from "ora";
429
432
 
@@ -433,10 +436,35 @@ import ora7 from "ora";
433
436
  import { confirm as confirm5 } from "@inquirer/prompts";
434
437
  import { execSync } from "child_process";
435
438
  import { existsSync as existsSync7, readdirSync } from "fs";
436
- import { join as join6 } from "path";
439
+ import { join as join7 } from "path";
437
440
 
438
441
  // src/wizard/banner.ts
439
442
  import chalk5 from "chalk";
443
+ import { readFileSync as readFileSync4 } from "fs";
444
+ import { fileURLToPath as fileURLToPath2 } from "url";
445
+ import { dirname as dirname2, join as join4 } from "path";
446
+ var __filename2 = fileURLToPath2(import.meta.url);
447
+ var __dirname2 = dirname2(__filename2);
448
+ function getCliVersion() {
449
+ const possiblePaths = [
450
+ join4(__dirname2, "../package.json"),
451
+ // from dist/
452
+ join4(__dirname2, "../../package.json"),
453
+ // from dist/wizard/ or src/wizard/
454
+ join4(__dirname2, "../../../package.json")
455
+ // fallback
456
+ ];
457
+ for (const packageJsonPath of possiblePaths) {
458
+ try {
459
+ const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
460
+ if (packageJson.name === "@nextsparkjs/cli" && packageJson.version) {
461
+ return packageJson.version;
462
+ }
463
+ } catch {
464
+ }
465
+ }
466
+ return "unknown";
467
+ }
440
468
  var BANNER = `
441
469
  _ __ __ _____ __
442
470
  / | / /__ _ __/ /_/ ___/____ ____ ______/ /__
@@ -446,9 +474,11 @@ var BANNER = `
446
474
  /_/
447
475
  `;
448
476
  function showBanner() {
477
+ const version = getCliVersion();
449
478
  console.log(chalk5.cyan(BANNER));
450
479
  console.log(chalk5.bold.white(" Welcome to NextSpark - The Complete SaaS Framework for Next.js"));
451
- console.log(chalk5.gray(" Create production-ready SaaS applications in minutes\n"));
480
+ console.log(chalk5.gray(` Version ${version} - Create production-ready SaaS applications in minutes
481
+ `));
452
482
  console.log(chalk5.gray(" " + "=".repeat(60) + "\n"));
453
483
  }
454
484
  function showSection(title, step, totalSteps) {
@@ -1151,22 +1181,22 @@ async function runExpertPrompts() {
1151
1181
  // src/wizard/generators/index.ts
1152
1182
  import fs7 from "fs-extra";
1153
1183
  import path5 from "path";
1154
- import { fileURLToPath as fileURLToPath4 } from "url";
1184
+ import { fileURLToPath as fileURLToPath5 } from "url";
1155
1185
 
1156
1186
  // src/wizard/generators/theme-renamer.ts
1157
1187
  import fs from "fs-extra";
1158
1188
  import path from "path";
1159
- import { fileURLToPath as fileURLToPath2 } from "url";
1160
- var __filename2 = fileURLToPath2(import.meta.url);
1161
- var __dirname2 = path.dirname(__filename2);
1189
+ import { fileURLToPath as fileURLToPath3 } from "url";
1190
+ var __filename3 = fileURLToPath3(import.meta.url);
1191
+ var __dirname3 = path.dirname(__filename3);
1162
1192
  function getTemplatesDir() {
1163
1193
  try {
1164
1194
  const corePkgPath = __require.resolve("@nextsparkjs/core/package.json");
1165
1195
  return path.join(path.dirname(corePkgPath), "templates");
1166
1196
  } catch {
1167
1197
  const possiblePaths = [
1168
- path.resolve(__dirname2, "../../../../../core/templates"),
1169
- path.resolve(__dirname2, "../../../../core/templates"),
1198
+ path.resolve(__dirname3, "../../../../../core/templates"),
1199
+ path.resolve(__dirname3, "../../../../core/templates"),
1170
1200
  path.resolve(process.cwd(), "node_modules/@nextsparkjs/core/templates")
1171
1201
  ];
1172
1202
  for (const p of possiblePaths) {
@@ -1180,11 +1210,11 @@ function getTemplatesDir() {
1180
1210
  function getTargetThemesDir() {
1181
1211
  return path.resolve(process.cwd(), "contents", "themes");
1182
1212
  }
1183
- async function copyStarterTheme(config) {
1213
+ async function copyStarterTheme(config2) {
1184
1214
  const templatesDir = getTemplatesDir();
1185
1215
  const starterThemePath = path.join(templatesDir, "contents", "themes", "starter");
1186
1216
  const targetThemesDir = getTargetThemesDir();
1187
- const newThemePath = path.join(targetThemesDir, config.projectSlug);
1217
+ const newThemePath = path.join(targetThemesDir, config2.projectSlug);
1188
1218
  if (!await fs.pathExists(starterThemePath)) {
1189
1219
  throw new Error(`Starter theme not found at: ${starterThemePath}`);
1190
1220
  }
@@ -1194,99 +1224,97 @@ async function copyStarterTheme(config) {
1194
1224
  await fs.ensureDir(targetThemesDir);
1195
1225
  await fs.copy(starterThemePath, newThemePath);
1196
1226
  }
1197
- async function updateThemeConfig(config) {
1198
- const themeConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "theme.config.ts");
1227
+ async function updateThemeConfig(config2) {
1228
+ const themeConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "theme.config.ts");
1199
1229
  if (!await fs.pathExists(themeConfigPath)) {
1200
1230
  throw new Error(`theme.config.ts not found at: ${themeConfigPath}`);
1201
1231
  }
1202
1232
  let content = await fs.readFile(themeConfigPath, "utf-8");
1203
1233
  content = content.replace(
1204
1234
  /name:\s*['"]starter['"]/g,
1205
- `name: '${config.projectSlug}'`
1235
+ `name: '${config2.projectSlug}'`
1206
1236
  );
1207
1237
  content = content.replace(
1208
1238
  /displayName:\s*['"]Starter['"]/g,
1209
- `displayName: '${config.projectName}'`
1239
+ `displayName: '${config2.projectName}'`
1210
1240
  );
1211
1241
  content = content.replace(
1212
1242
  /description:\s*['"]Minimal starter theme for NextSpark['"]/g,
1213
- `description: '${config.projectDescription}'`
1243
+ `description: '${config2.projectDescription}'`
1214
1244
  );
1215
1245
  content = content.replace(
1216
1246
  /export const starterThemeConfig/g,
1217
- `export const ${toCamelCase(config.projectSlug)}ThemeConfig`
1247
+ `export const ${toCamelCase(config2.projectSlug)}ThemeConfig`
1218
1248
  );
1219
1249
  content = content.replace(
1220
1250
  /export default starterThemeConfig/g,
1221
- `export default ${toCamelCase(config.projectSlug)}ThemeConfig`
1251
+ `export default ${toCamelCase(config2.projectSlug)}ThemeConfig`
1222
1252
  );
1223
1253
  await fs.writeFile(themeConfigPath, content, "utf-8");
1224
1254
  }
1225
- async function updateDevConfig(config) {
1226
- const devConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "dev.config.ts");
1255
+ async function updateDevConfig(config2) {
1256
+ const devConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "dev.config.ts");
1227
1257
  if (!await fs.pathExists(devConfigPath)) {
1228
1258
  return;
1229
1259
  }
1230
1260
  let content = await fs.readFile(devConfigPath, "utf-8");
1231
- content = content.replace(/@starter\.dev/g, `@${config.projectSlug}.dev`);
1232
- content = content.replace(/STARTER THEME/g, `${config.projectName.toUpperCase()}`);
1233
- content = content.replace(/Starter Theme/g, config.projectName);
1234
- content = content.replace(/Starter Team/g, `${config.projectName} Team`);
1261
+ content = content.replace(/STARTER THEME/g, `${config2.projectName.toUpperCase()}`);
1262
+ content = content.replace(/Starter Theme/g, config2.projectName);
1235
1263
  await fs.writeFile(devConfigPath, content, "utf-8");
1236
1264
  }
1237
- async function updateAppConfig(config) {
1238
- const appConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "app.config.ts");
1265
+ async function updateAppConfig(config2) {
1266
+ const appConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "app.config.ts");
1239
1267
  if (!await fs.pathExists(appConfigPath)) {
1240
1268
  throw new Error(`app.config.ts not found at: ${appConfigPath}`);
1241
1269
  }
1242
1270
  let content = await fs.readFile(appConfigPath, "utf-8");
1243
1271
  content = content.replace(
1244
1272
  /name:\s*['"]Starter['"]/g,
1245
- `name: '${config.projectName}'`
1273
+ `name: '${config2.projectName}'`
1246
1274
  );
1247
1275
  content = content.replace(
1248
1276
  /mode:\s*['"]multi-tenant['"]\s*as\s*const/g,
1249
- `mode: '${config.teamMode}' as const`
1277
+ `mode: '${config2.teamMode}' as const`
1250
1278
  );
1251
- const localesArray = config.supportedLocales.map((l) => `'${l}'`).join(", ");
1279
+ const localesArray = config2.supportedLocales.map((l) => `'${l}'`).join(", ");
1252
1280
  content = content.replace(
1253
1281
  /supportedLocales:\s*\[.*?\]/g,
1254
1282
  `supportedLocales: [${localesArray}]`
1255
1283
  );
1256
1284
  content = content.replace(
1257
1285
  /defaultLocale:\s*['"]en['"]\s*as\s*const/g,
1258
- `defaultLocale: '${config.defaultLocale}' as const`
1286
+ `defaultLocale: '${config2.defaultLocale}' as const`
1259
1287
  );
1260
1288
  content = content.replace(
1261
1289
  /label:\s*['"]Starter['"]/g,
1262
- `label: '${config.projectName}'`
1290
+ `label: '${config2.projectName}'`
1263
1291
  );
1264
1292
  await fs.writeFile(appConfigPath, content, "utf-8");
1265
1293
  }
1266
- async function updateRolesConfig(config) {
1267
- const appConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "app.config.ts");
1294
+ async function updateRolesConfig(config2) {
1295
+ const appConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "app.config.ts");
1268
1296
  if (!await fs.pathExists(appConfigPath)) {
1269
1297
  return;
1270
1298
  }
1271
1299
  let content = await fs.readFile(appConfigPath, "utf-8");
1272
- const rolesArray = config.teamRoles.map((r) => `'${r}'`).join(", ");
1300
+ const rolesArray = config2.teamRoles.map((r) => `'${r}'`).join(", ");
1273
1301
  content = content.replace(
1274
1302
  /availableTeamRoles:\s*\[.*?\]/g,
1275
1303
  `availableTeamRoles: [${rolesArray}]`
1276
1304
  );
1277
1305
  await fs.writeFile(appConfigPath, content, "utf-8");
1278
1306
  }
1279
- async function updateBillingConfig(config) {
1280
- const billingConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "billing.config.ts");
1307
+ async function updateBillingConfig(config2) {
1308
+ const billingConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "billing.config.ts");
1281
1309
  if (!await fs.pathExists(billingConfigPath)) {
1282
1310
  return;
1283
1311
  }
1284
1312
  let content = await fs.readFile(billingConfigPath, "utf-8");
1285
1313
  content = content.replace(
1286
1314
  /currency:\s*['"]usd['"]/g,
1287
- `currency: '${config.currency}'`
1315
+ `currency: '${config2.currency}'`
1288
1316
  );
1289
- const plansContent = generateBillingPlans(config.billingModel, config.currency);
1317
+ const plansContent = generateBillingPlans(config2.billingModel, config2.currency);
1290
1318
  content = content.replace(
1291
1319
  /plans:\s*\[[\s\S]*?\],\s*\n\s*\/\/ ===+\s*\n\s*\/\/ ACTION MAPPINGS/,
1292
1320
  `plans: ${plansContent},
@@ -1436,8 +1464,8 @@ function generateBillingPlans(billingModel, currency) {
1436
1464
  },
1437
1465
  ]`;
1438
1466
  }
1439
- async function updateMigrations(config) {
1440
- const migrationsDir = path.join(getTargetThemesDir(), config.projectSlug, "migrations");
1467
+ async function updateMigrations(config2) {
1468
+ const migrationsDir = path.join(getTargetThemesDir(), config2.projectSlug, "migrations");
1441
1469
  if (!await fs.pathExists(migrationsDir)) {
1442
1470
  return;
1443
1471
  }
@@ -1446,12 +1474,39 @@ async function updateMigrations(config) {
1446
1474
  for (const file of sqlFiles) {
1447
1475
  const filePath = path.join(migrationsDir, file);
1448
1476
  let content = await fs.readFile(filePath, "utf-8");
1449
- content = content.replace(/@starter\.dev/g, `@${config.projectSlug}.dev`);
1450
- content = content.replace(/Starter Theme/g, config.projectName);
1451
- content = content.replace(/starter theme/g, config.projectSlug);
1477
+ content = content.replace(/@starter\.dev/g, `@${config2.projectSlug}.dev`);
1478
+ content = content.replace(/Starter Theme/g, config2.projectName);
1479
+ content = content.replace(/starter theme/g, config2.projectSlug);
1452
1480
  await fs.writeFile(filePath, content, "utf-8");
1453
1481
  }
1454
1482
  }
1483
+ async function updateTestFiles(config2) {
1484
+ const testsDir = path.join(getTargetThemesDir(), config2.projectSlug, "tests");
1485
+ if (!await fs.pathExists(testsDir)) {
1486
+ return;
1487
+ }
1488
+ const processDir = async (dir) => {
1489
+ const items = await fs.readdir(dir);
1490
+ for (const item of items) {
1491
+ const itemPath = path.join(dir, item);
1492
+ const stat = await fs.stat(itemPath);
1493
+ if (stat.isDirectory()) {
1494
+ await processDir(itemPath);
1495
+ } else if (item.endsWith(".ts") || item.endsWith(".tsx")) {
1496
+ let content = await fs.readFile(itemPath, "utf-8");
1497
+ const hasChanges = content.includes("@/contents/themes/starter/");
1498
+ if (hasChanges) {
1499
+ content = content.replace(
1500
+ /@\/contents\/themes\/starter\//g,
1501
+ `@/contents/themes/${config2.projectSlug}/`
1502
+ );
1503
+ await fs.writeFile(itemPath, content, "utf-8");
1504
+ }
1505
+ }
1506
+ }
1507
+ };
1508
+ await processDir(testsDir);
1509
+ }
1455
1510
  function toCamelCase(str) {
1456
1511
  return str.split("-").map((word, index) => {
1457
1512
  if (index === 0) {
@@ -1467,10 +1522,10 @@ import path2 from "path";
1467
1522
  function getTargetThemesDir2() {
1468
1523
  return path2.resolve(process.cwd(), "contents", "themes");
1469
1524
  }
1470
- async function updateAuthConfig(config) {
1525
+ async function updateAuthConfig(config2) {
1471
1526
  const authConfigPath = path2.join(
1472
1527
  getTargetThemesDir2(),
1473
- config.projectSlug,
1528
+ config2.projectSlug,
1474
1529
  "config",
1475
1530
  "auth.config.ts"
1476
1531
  );
@@ -1480,22 +1535,22 @@ async function updateAuthConfig(config) {
1480
1535
  let content = await fs2.readFile(authConfigPath, "utf-8");
1481
1536
  content = content.replace(
1482
1537
  /(emailPassword:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1483
- `$1${config.auth.emailPassword}`
1538
+ `$1${config2.auth.emailPassword}`
1484
1539
  );
1485
1540
  content = content.replace(
1486
1541
  /(google:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1487
- `$1${config.auth.googleOAuth}`
1542
+ `$1${config2.auth.googleOAuth}`
1488
1543
  );
1489
1544
  content = content.replace(
1490
1545
  /(emailVerification:\s*)(?:true|false)/g,
1491
- `$1${config.auth.emailVerification}`
1546
+ `$1${config2.auth.emailVerification}`
1492
1547
  );
1493
1548
  await fs2.writeFile(authConfigPath, content, "utf-8");
1494
1549
  }
1495
- async function updateDashboardUIConfig(config) {
1550
+ async function updateDashboardUIConfig(config2) {
1496
1551
  const dashboardConfigPath = path2.join(
1497
1552
  getTargetThemesDir2(),
1498
- config.projectSlug,
1553
+ config2.projectSlug,
1499
1554
  "config",
1500
1555
  "dashboard.config.ts"
1501
1556
  );
@@ -1505,42 +1560,42 @@ async function updateDashboardUIConfig(config) {
1505
1560
  let content = await fs2.readFile(dashboardConfigPath, "utf-8");
1506
1561
  content = content.replace(
1507
1562
  /(search:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1508
- `$1${config.dashboard.search}`
1563
+ `$1${config2.dashboard.search}`
1509
1564
  );
1510
1565
  content = content.replace(
1511
1566
  /(notifications:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1512
- `$1${config.dashboard.notifications}`
1567
+ `$1${config2.dashboard.notifications}`
1513
1568
  );
1514
1569
  content = content.replace(
1515
1570
  /(themeToggle:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1516
- `$1${config.dashboard.themeToggle}`
1571
+ `$1${config2.dashboard.themeToggle}`
1517
1572
  );
1518
1573
  content = content.replace(
1519
1574
  /(support:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1520
- `$1${config.dashboard.support}`
1575
+ `$1${config2.dashboard.support}`
1521
1576
  );
1522
1577
  content = content.replace(
1523
1578
  /(quickCreate:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1524
- `$1${config.dashboard.quickCreate}`
1579
+ `$1${config2.dashboard.quickCreate}`
1525
1580
  );
1526
1581
  content = content.replace(
1527
1582
  /(adminAccess:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1528
- `$1${config.dashboard.superadminAccess}`
1583
+ `$1${config2.dashboard.superadminAccess}`
1529
1584
  );
1530
1585
  content = content.replace(
1531
1586
  /(devtoolsAccess:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1532
- `$1${config.dashboard.devtoolsAccess}`
1587
+ `$1${config2.dashboard.devtoolsAccess}`
1533
1588
  );
1534
1589
  content = content.replace(
1535
1590
  /(defaultCollapsed:\s*)(?:true|false)/g,
1536
- `$1${config.dashboard.sidebarCollapsed}`
1591
+ `$1${config2.dashboard.sidebarCollapsed}`
1537
1592
  );
1538
1593
  await fs2.writeFile(dashboardConfigPath, content, "utf-8");
1539
1594
  }
1540
- async function updateDevToolsConfig(config) {
1595
+ async function updateDevToolsConfig(config2) {
1541
1596
  const devConfigPath = path2.join(
1542
1597
  getTargetThemesDir2(),
1543
- config.projectSlug,
1598
+ config2.projectSlug,
1544
1599
  "config",
1545
1600
  "dev.config.ts"
1546
1601
  );
@@ -1550,18 +1605,18 @@ async function updateDevToolsConfig(config) {
1550
1605
  let content = await fs2.readFile(devConfigPath, "utf-8");
1551
1606
  content = content.replace(
1552
1607
  /(devKeyring:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1553
- `$1${config.dev.devKeyring}`
1608
+ `$1${config2.dev.devKeyring}`
1554
1609
  );
1555
1610
  content = content.replace(
1556
1611
  /(debugMode:\s*)(?:true|false)/g,
1557
- `$1${config.dev.debugMode}`
1612
+ `$1${config2.dev.debugMode}`
1558
1613
  );
1559
1614
  await fs2.writeFile(devConfigPath, content, "utf-8");
1560
1615
  }
1561
- async function updatePermissionsConfig(config) {
1616
+ async function updatePermissionsConfig(config2) {
1562
1617
  const permissionsConfigPath = path2.join(
1563
1618
  getTargetThemesDir2(),
1564
- config.projectSlug,
1619
+ config2.projectSlug,
1565
1620
  "config",
1566
1621
  "permissions.config.ts"
1567
1622
  );
@@ -1569,7 +1624,7 @@ async function updatePermissionsConfig(config) {
1569
1624
  return;
1570
1625
  }
1571
1626
  let content = await fs2.readFile(permissionsConfigPath, "utf-8");
1572
- const availableRoles = config.teamRoles;
1627
+ const availableRoles = config2.teamRoles;
1573
1628
  const roleArrayPattern = /roles:\s*\[(.*?)\]/g;
1574
1629
  content = content.replace(roleArrayPattern, (match, rolesStr) => {
1575
1630
  const currentRoles = rolesStr.split(",").map((r) => r.trim().replace(/['"]/g, "")).filter((r) => r.length > 0);
@@ -1581,10 +1636,48 @@ async function updatePermissionsConfig(config) {
1581
1636
  });
1582
1637
  await fs2.writeFile(permissionsConfigPath, content, "utf-8");
1583
1638
  }
1584
- async function updateDashboardConfig(config) {
1639
+ async function updateEntityPermissions(config2) {
1640
+ const permissionsConfigPath = path2.join(
1641
+ getTargetThemesDir2(),
1642
+ config2.projectSlug,
1643
+ "config",
1644
+ "permissions.config.ts"
1645
+ );
1646
+ if (!await fs2.pathExists(permissionsConfigPath)) {
1647
+ return;
1648
+ }
1649
+ let content = await fs2.readFile(permissionsConfigPath, "utf-8");
1650
+ if (config2.contentFeatures.pages) {
1651
+ content = uncommentPermissionBlock(content, "PAGES");
1652
+ }
1653
+ if (config2.contentFeatures.blog) {
1654
+ content = uncommentPermissionBlock(content, "POSTS");
1655
+ }
1656
+ await fs2.writeFile(permissionsConfigPath, content, "utf-8");
1657
+ }
1658
+ function uncommentPermissionBlock(content, markerName) {
1659
+ const startMarker = `// __${markerName}_PERMISSIONS_START__`;
1660
+ const endMarker = `// __${markerName}_PERMISSIONS_END__`;
1661
+ const startIndex = content.indexOf(startMarker);
1662
+ const endIndex = content.indexOf(endMarker);
1663
+ if (startIndex === -1 || endIndex === -1) {
1664
+ return content;
1665
+ }
1666
+ const beforeBlock = content.slice(0, startIndex);
1667
+ const block = content.slice(startIndex + startMarker.length, endIndex);
1668
+ const afterBlock = content.slice(endIndex + endMarker.length);
1669
+ const uncommentedBlock = block.split("\n").map((line) => {
1670
+ if (line.match(/^\s*\/\/\s+/)) {
1671
+ return line.replace(/^(\s*)\/\/\s*/, "$1");
1672
+ }
1673
+ return line;
1674
+ }).join("\n");
1675
+ return beforeBlock + uncommentedBlock + afterBlock;
1676
+ }
1677
+ async function updateDashboardConfig(config2) {
1585
1678
  const dashboardConfigPath = path2.join(
1586
1679
  getTargetThemesDir2(),
1587
- config.projectSlug,
1680
+ config2.projectSlug,
1588
1681
  "config",
1589
1682
  "dashboard.config.ts"
1590
1683
  );
@@ -1592,19 +1685,19 @@ async function updateDashboardConfig(config) {
1592
1685
  return;
1593
1686
  }
1594
1687
  let content = await fs2.readFile(dashboardConfigPath, "utf-8");
1595
- if (!config.features.analytics) {
1688
+ if (!config2.features.analytics) {
1596
1689
  content = content.replace(
1597
1690
  /(id:\s*['"]analytics['"].*?enabled:\s*)true/gs,
1598
1691
  "$1false"
1599
1692
  );
1600
1693
  }
1601
- if (!config.features.billing) {
1694
+ if (!config2.features.billing) {
1602
1695
  content = content.replace(
1603
1696
  /(id:\s*['"]billing['"].*?enabled:\s*)true/gs,
1604
1697
  "$1false"
1605
1698
  );
1606
1699
  }
1607
- if (config.teamMode === "single-user") {
1700
+ if (config2.teamMode === "single-user") {
1608
1701
  content = content.replace(
1609
1702
  /(id:\s*['"]team['"].*?enabled:\s*)true/gs,
1610
1703
  "$1false"
@@ -1616,10 +1709,10 @@ async function updateDashboardConfig(config) {
1616
1709
  }
1617
1710
  await fs2.writeFile(dashboardConfigPath, content, "utf-8");
1618
1711
  }
1619
- async function generateEnvExample(config) {
1712
+ async function generateEnvExample(config2) {
1620
1713
  const envExamplePath = path2.resolve(process.cwd(), ".env.example");
1621
1714
  let oauthSection = "";
1622
- if (config.auth.googleOAuth) {
1715
+ if (config2.auth.googleOAuth) {
1623
1716
  oauthSection = `# =============================================================================
1624
1717
  # OAUTH PROVIDERS
1625
1718
  # =============================================================================
@@ -1635,7 +1728,7 @@ GOOGLE_CLIENT_SECRET="your-google-client-secret"
1635
1728
  `;
1636
1729
  }
1637
1730
  const envContent = `# NextSpark Environment Configuration
1638
- # Generated for: ${config.projectName}
1731
+ # Generated for: ${config2.projectName}
1639
1732
 
1640
1733
  # =============================================================================
1641
1734
  # DATABASE
@@ -1651,7 +1744,7 @@ BETTER_AUTH_SECRET="your-secret-key-here"
1651
1744
  # =============================================================================
1652
1745
  # THEME
1653
1746
  # =============================================================================
1654
- NEXT_PUBLIC_ACTIVE_THEME="${config.projectSlug}"
1747
+ NEXT_PUBLIC_ACTIVE_THEME="${config2.projectSlug}"
1655
1748
 
1656
1749
  # =============================================================================
1657
1750
  # APPLICATION
@@ -1659,7 +1752,7 @@ NEXT_PUBLIC_ACTIVE_THEME="${config.projectSlug}"
1659
1752
  NEXT_PUBLIC_APP_URL="http://localhost:3000"
1660
1753
  NODE_ENV="development"
1661
1754
 
1662
- ${config.features.billing ? `# =============================================================================
1755
+ ${config2.features.billing ? `# =============================================================================
1663
1756
  # STRIPE (Billing)
1664
1757
  # =============================================================================
1665
1758
  STRIPE_SECRET_KEY="sk_test_..."
@@ -1677,19 +1770,39 @@ ${oauthSection}`;
1677
1770
  await fs2.writeFile(envExamplePath, envContent, "utf-8");
1678
1771
  }
1679
1772
  }
1680
- async function updateReadme(config) {
1681
- const readmePath = path2.join(getTargetThemesDir2(), config.projectSlug, "README.md");
1773
+ async function updateReadme(config2) {
1774
+ const readmePath = path2.join(getTargetThemesDir2(), config2.projectSlug, "README.md");
1682
1775
  if (!await fs2.pathExists(readmePath)) {
1683
1776
  return;
1684
1777
  }
1685
1778
  let content = await fs2.readFile(readmePath, "utf-8");
1686
- content = content.replace(/# Starter Theme/g, `# ${config.projectName}`);
1779
+ content = content.replace(/# Starter Theme/g, `# ${config2.projectName}`);
1687
1780
  content = content.replace(
1688
1781
  /Minimal starter theme for NextSpark/g,
1689
- config.projectDescription
1782
+ config2.projectDescription
1690
1783
  );
1691
1784
  await fs2.writeFile(readmePath, content, "utf-8");
1692
1785
  }
1786
+ async function copyEnvExampleToEnv() {
1787
+ const projectRoot = process.cwd();
1788
+ const envExamplePath = path2.resolve(projectRoot, ".env.example");
1789
+ const envPath = path2.resolve(projectRoot, ".env");
1790
+ if (await fs2.pathExists(envExamplePath) && !await fs2.pathExists(envPath)) {
1791
+ await fs2.copy(envExamplePath, envPath);
1792
+ }
1793
+ }
1794
+ async function updateGlobalsCss(config2) {
1795
+ const globalsCssPath = path2.resolve(process.cwd(), "app", "globals.css");
1796
+ if (!await fs2.pathExists(globalsCssPath)) {
1797
+ return;
1798
+ }
1799
+ let content = await fs2.readFile(globalsCssPath, "utf-8");
1800
+ content = content.replace(
1801
+ /@import\s+["']\.\.\/contents\/themes\/[^/]+\/styles\/globals\.css["'];?/,
1802
+ `@import "../contents/themes/${config2.projectSlug}/styles/globals.css";`
1803
+ );
1804
+ await fs2.writeFile(globalsCssPath, content, "utf-8");
1805
+ }
1693
1806
 
1694
1807
  // src/wizard/generators/messages-generator.ts
1695
1808
  import fs3 from "fs-extra";
@@ -1697,8 +1810,8 @@ import path3 from "path";
1697
1810
  function getTargetThemesDir3() {
1698
1811
  return path3.resolve(process.cwd(), "contents", "themes");
1699
1812
  }
1700
- async function removeUnusedLanguages(config) {
1701
- const messagesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "messages");
1813
+ async function removeUnusedLanguages(config2) {
1814
+ const messagesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "messages");
1702
1815
  if (!await fs3.pathExists(messagesDir)) {
1703
1816
  return;
1704
1817
  }
@@ -1708,13 +1821,13 @@ async function removeUnusedLanguages(config) {
1708
1821
  return fs3.statSync(folderPath).isDirectory() && Object.keys(AVAILABLE_LOCALES).includes(f);
1709
1822
  });
1710
1823
  for (const folder of languageFolders) {
1711
- if (!config.supportedLocales.includes(folder)) {
1824
+ if (!config2.supportedLocales.includes(folder)) {
1712
1825
  await fs3.remove(path3.join(messagesDir, folder));
1713
1826
  }
1714
1827
  }
1715
1828
  }
1716
- async function removeUnusedEntityMessages(config) {
1717
- const entitiesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "entities");
1829
+ async function removeUnusedEntityMessages(config2) {
1830
+ const entitiesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "entities");
1718
1831
  if (!await fs3.pathExists(entitiesDir)) {
1719
1832
  return;
1720
1833
  }
@@ -1727,45 +1840,45 @@ async function removeUnusedEntityMessages(config) {
1727
1840
  const files = await fs3.readdir(messagesDir);
1728
1841
  for (const file of files) {
1729
1842
  const locale = path3.basename(file, ".json");
1730
- if (Object.keys(AVAILABLE_LOCALES).includes(locale) && !config.supportedLocales.includes(locale)) {
1843
+ if (Object.keys(AVAILABLE_LOCALES).includes(locale) && !config2.supportedLocales.includes(locale)) {
1731
1844
  await fs3.remove(path3.join(messagesDir, file));
1732
1845
  }
1733
1846
  }
1734
1847
  }
1735
1848
  }
1736
- async function ensureLanguageFolders(config) {
1737
- const messagesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "messages");
1849
+ async function ensureLanguageFolders(config2) {
1850
+ const messagesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "messages");
1738
1851
  if (!await fs3.pathExists(messagesDir)) {
1739
1852
  return;
1740
1853
  }
1741
- const defaultLocaleDir = path3.join(messagesDir, config.defaultLocale);
1854
+ const defaultLocaleDir = path3.join(messagesDir, config2.defaultLocale);
1742
1855
  if (!await fs3.pathExists(defaultLocaleDir)) {
1743
1856
  const enDir = path3.join(messagesDir, "en");
1744
1857
  if (await fs3.pathExists(enDir)) {
1745
1858
  await fs3.copy(enDir, defaultLocaleDir);
1746
1859
  }
1747
1860
  }
1748
- for (const locale of config.supportedLocales) {
1861
+ for (const locale of config2.supportedLocales) {
1749
1862
  const localeDir = path3.join(messagesDir, locale);
1750
1863
  if (!await fs3.pathExists(localeDir) && await fs3.pathExists(defaultLocaleDir)) {
1751
1864
  await fs3.copy(defaultLocaleDir, localeDir);
1752
1865
  }
1753
1866
  }
1754
1867
  }
1755
- async function updateMessageFiles(config) {
1756
- const messagesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "messages");
1868
+ async function updateMessageFiles(config2) {
1869
+ const messagesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "messages");
1757
1870
  if (!await fs3.pathExists(messagesDir)) {
1758
1871
  return;
1759
1872
  }
1760
- for (const locale of config.supportedLocales) {
1873
+ for (const locale of config2.supportedLocales) {
1761
1874
  const commonPath = path3.join(messagesDir, locale, "common.json");
1762
1875
  if (await fs3.pathExists(commonPath)) {
1763
1876
  try {
1764
1877
  const content = await fs3.readJson(commonPath);
1765
1878
  if (content.app) {
1766
- content.app.name = config.projectName;
1879
+ content.app.name = config2.projectName;
1767
1880
  if (content.app.description) {
1768
- content.app.description = config.projectDescription;
1881
+ content.app.description = config2.projectDescription;
1769
1882
  }
1770
1883
  }
1771
1884
  await fs3.writeJson(commonPath, content, { spaces: 2 });
@@ -1774,27 +1887,27 @@ async function updateMessageFiles(config) {
1774
1887
  }
1775
1888
  }
1776
1889
  }
1777
- async function processI18n(config) {
1778
- await removeUnusedLanguages(config);
1779
- await removeUnusedEntityMessages(config);
1780
- await ensureLanguageFolders(config);
1781
- await updateMessageFiles(config);
1890
+ async function processI18n(config2) {
1891
+ await removeUnusedLanguages(config2);
1892
+ await removeUnusedEntityMessages(config2);
1893
+ await ensureLanguageFolders(config2);
1894
+ await updateMessageFiles(config2);
1782
1895
  }
1783
1896
 
1784
1897
  // src/wizard/generators/content-features-generator.ts
1785
1898
  import fs4 from "fs-extra";
1786
1899
  import path4 from "path";
1787
- import { fileURLToPath as fileURLToPath3 } from "url";
1788
- var __filename3 = fileURLToPath3(import.meta.url);
1789
- var __dirname3 = path4.dirname(__filename3);
1900
+ import { fileURLToPath as fileURLToPath4 } from "url";
1901
+ var __filename4 = fileURLToPath4(import.meta.url);
1902
+ var __dirname4 = path4.dirname(__filename4);
1790
1903
  function getTemplatesDir2() {
1791
1904
  try {
1792
1905
  const corePkgPath = __require.resolve("@nextsparkjs/core/package.json");
1793
1906
  return path4.join(path4.dirname(corePkgPath), "templates");
1794
1907
  } catch {
1795
1908
  const possiblePaths = [
1796
- path4.resolve(__dirname3, "../../../../../core/templates"),
1797
- path4.resolve(__dirname3, "../../../../core/templates"),
1909
+ path4.resolve(__dirname4, "../../../../../core/templates"),
1910
+ path4.resolve(__dirname4, "../../../../core/templates"),
1798
1911
  path4.resolve(process.cwd(), "node_modules/@nextsparkjs/core/templates")
1799
1912
  ];
1800
1913
  for (const p of possiblePaths) {
@@ -1811,20 +1924,27 @@ function getFeaturesDir() {
1811
1924
  function getTargetThemeDir(projectSlug) {
1812
1925
  return path4.resolve(process.cwd(), "contents", "themes", projectSlug);
1813
1926
  }
1814
- async function copyPagesFeature(config) {
1927
+ async function copyPagesFeature(config2) {
1815
1928
  const featuresDir = getFeaturesDir();
1816
- const targetThemeDir = getTargetThemeDir(config.projectSlug);
1929
+ const targetThemeDir = getTargetThemeDir(config2.projectSlug);
1817
1930
  const sourcePagesEntity = path4.join(featuresDir, "pages", "entities", "pages");
1818
1931
  const targetEntitiesDir = path4.join(targetThemeDir, "entities", "pages");
1819
- if (!await fs4.pathExists(sourcePagesEntity)) {
1932
+ if (await fs4.pathExists(sourcePagesEntity)) {
1933
+ await fs4.copy(sourcePagesEntity, targetEntitiesDir);
1934
+ } else {
1820
1935
  console.warn(`Warning: Pages entity not found at: ${sourcePagesEntity}`);
1821
- return;
1822
1936
  }
1823
- await fs4.copy(sourcePagesEntity, targetEntitiesDir);
1937
+ const sourceHeroBlock = path4.join(featuresDir, "pages", "blocks", "hero");
1938
+ const targetHeroBlock = path4.join(targetThemeDir, "blocks", "hero");
1939
+ if (await fs4.pathExists(sourceHeroBlock)) {
1940
+ await fs4.copy(sourceHeroBlock, targetHeroBlock);
1941
+ } else {
1942
+ console.warn(`Warning: Hero block not found at: ${sourceHeroBlock}`);
1943
+ }
1824
1944
  }
1825
- async function copyBlogFeature(config) {
1945
+ async function copyBlogFeature(config2) {
1826
1946
  const featuresDir = getFeaturesDir();
1827
- const targetThemeDir = getTargetThemeDir(config.projectSlug);
1947
+ const targetThemeDir = getTargetThemeDir(config2.projectSlug);
1828
1948
  const sourcePostsEntity = path4.join(featuresDir, "blog", "entities", "posts");
1829
1949
  const targetPostsEntity = path4.join(targetThemeDir, "entities", "posts");
1830
1950
  if (await fs4.pathExists(sourcePostsEntity)) {
@@ -1840,34 +1960,34 @@ async function copyBlogFeature(config) {
1840
1960
  console.warn(`Warning: Post-content block not found at: ${sourcePostContentBlock}`);
1841
1961
  }
1842
1962
  }
1843
- async function copyContentFeatures(config) {
1844
- if (!config.contentFeatures.pages && !config.contentFeatures.blog) {
1963
+ async function copyContentFeatures(config2) {
1964
+ if (!config2.contentFeatures.pages && !config2.contentFeatures.blog) {
1845
1965
  return;
1846
1966
  }
1847
- if (config.contentFeatures.pages) {
1848
- await copyPagesFeature(config);
1967
+ if (config2.contentFeatures.pages) {
1968
+ await copyPagesFeature(config2);
1849
1969
  }
1850
- if (config.contentFeatures.blog) {
1851
- await copyBlogFeature(config);
1970
+ if (config2.contentFeatures.blog) {
1971
+ await copyBlogFeature(config2);
1852
1972
  }
1853
1973
  }
1854
1974
 
1855
1975
  // src/wizard/generators/theme-plugins-installer.ts
1856
- import { existsSync as existsSync6, cpSync, mkdirSync, readFileSync as readFileSync5, writeFileSync } from "fs";
1857
- import { join as join5, resolve as resolve2 } from "path";
1976
+ import { existsSync as existsSync6, cpSync, mkdirSync, readFileSync as readFileSync6, writeFileSync } from "fs";
1977
+ import { join as join6, resolve as resolve2 } from "path";
1858
1978
  import chalk9 from "chalk";
1859
1979
  import ora6 from "ora";
1860
1980
 
1861
1981
  // src/commands/add-theme.ts
1862
1982
  import chalk8 from "chalk";
1863
1983
  import ora5 from "ora";
1864
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1865
- import { join as join4 } from "path";
1984
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1985
+ import { join as join5 } from "path";
1866
1986
  async function addTheme(packageSpec, options = {}) {
1867
1987
  const spinner = ora5(`Adding theme ${packageSpec}`).start();
1868
1988
  let cleanup = null;
1869
1989
  try {
1870
- const contentsDir = join4(process.cwd(), "contents");
1990
+ const contentsDir = join5(process.cwd(), "contents");
1871
1991
  if (!existsSync5(contentsDir)) {
1872
1992
  spinner.fail('contents/ directory not found. Run "nextspark init" first.');
1873
1993
  return;
@@ -1930,13 +2050,13 @@ async function addTheme(packageSpec, options = {}) {
1930
2050
  }
1931
2051
  function checkPluginExists(pluginName) {
1932
2052
  const name = pluginName.replace(/^@[^/]+\//, "").replace(/^nextspark-plugin-/, "").replace(/^plugin-/, "");
1933
- return existsSync5(join4(process.cwd(), "contents", "plugins", name));
2053
+ return existsSync5(join5(process.cwd(), "contents", "plugins", name));
1934
2054
  }
1935
2055
  function getCoreVersion() {
1936
- const pkgPath = join4(process.cwd(), "node_modules", "@nextsparkjs", "core", "package.json");
2056
+ const pkgPath = join5(process.cwd(), "node_modules", "@nextsparkjs", "core", "package.json");
1937
2057
  if (existsSync5(pkgPath)) {
1938
2058
  try {
1939
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
2059
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1940
2060
  return pkg.version || "0.0.0";
1941
2061
  } catch {
1942
2062
  return "0.0.0";
@@ -1974,20 +2094,20 @@ var THEME_REQUIRED_PLUGINS2 = {
1974
2094
  };
1975
2095
  function isMonorepoMode2() {
1976
2096
  const possiblePaths = [
1977
- join5(process.cwd(), "pnpm-workspace.yaml"),
1978
- join5(process.cwd(), "..", "pnpm-workspace.yaml"),
1979
- join5(process.cwd(), "..", "..", "pnpm-workspace.yaml")
2097
+ join6(process.cwd(), "pnpm-workspace.yaml"),
2098
+ join6(process.cwd(), "..", "pnpm-workspace.yaml"),
2099
+ join6(process.cwd(), "..", "..", "pnpm-workspace.yaml")
1980
2100
  ];
1981
2101
  return possiblePaths.some((p) => existsSync6(p));
1982
2102
  }
1983
2103
  function getMonorepoRoot() {
1984
2104
  const possibleRoots = [
1985
2105
  process.cwd(),
1986
- join5(process.cwd(), ".."),
1987
- join5(process.cwd(), "..", "..")
2106
+ join6(process.cwd(), ".."),
2107
+ join6(process.cwd(), "..", "..")
1988
2108
  ];
1989
2109
  for (const root of possibleRoots) {
1990
- if (existsSync6(join5(root, "pnpm-workspace.yaml"))) {
2110
+ if (existsSync6(join6(root, "pnpm-workspace.yaml"))) {
1991
2111
  return resolve2(root);
1992
2112
  }
1993
2113
  }
@@ -1997,15 +2117,15 @@ function getLocalPackageDir(type, name) {
1997
2117
  const monorepoRoot = getMonorepoRoot();
1998
2118
  if (!monorepoRoot) return null;
1999
2119
  const baseDir = type === "theme" ? "themes" : "plugins";
2000
- const packageDir = join5(monorepoRoot, baseDir, name);
2001
- if (existsSync6(packageDir) && existsSync6(join5(packageDir, "package.json"))) {
2120
+ const packageDir = join6(monorepoRoot, baseDir, name);
2121
+ if (existsSync6(packageDir) && existsSync6(join6(packageDir, "package.json"))) {
2002
2122
  return packageDir;
2003
2123
  }
2004
2124
  return null;
2005
2125
  }
2006
2126
  async function copyLocalTheme(name, sourceDir) {
2007
- const targetDir = join5(process.cwd(), "contents", "themes", name);
2008
- const themesDir = join5(process.cwd(), "contents", "themes");
2127
+ const targetDir = join6(process.cwd(), "contents", "themes", name);
2128
+ const themesDir = join6(process.cwd(), "contents", "themes");
2009
2129
  if (!existsSync6(themesDir)) {
2010
2130
  mkdirSync(themesDir, { recursive: true });
2011
2131
  }
@@ -2018,8 +2138,8 @@ async function copyLocalTheme(name, sourceDir) {
2018
2138
  return true;
2019
2139
  }
2020
2140
  async function copyLocalPlugin(name, sourceDir) {
2021
- const targetDir = join5(process.cwd(), "contents", "plugins", name);
2022
- const pluginsDir = join5(process.cwd(), "contents", "plugins");
2141
+ const targetDir = join6(process.cwd(), "contents", "plugins", name);
2142
+ const pluginsDir = join6(process.cwd(), "contents", "plugins");
2023
2143
  if (!existsSync6(pluginsDir)) {
2024
2144
  mkdirSync(pluginsDir, { recursive: true });
2025
2145
  }
@@ -2032,12 +2152,12 @@ async function copyLocalPlugin(name, sourceDir) {
2032
2152
  return true;
2033
2153
  }
2034
2154
  async function updateTsConfigPaths(name, type) {
2035
- const tsconfigPath = join5(process.cwd(), "tsconfig.json");
2155
+ const tsconfigPath = join6(process.cwd(), "tsconfig.json");
2036
2156
  if (!existsSync6(tsconfigPath)) {
2037
2157
  return;
2038
2158
  }
2039
2159
  try {
2040
- const content = readFileSync5(tsconfigPath, "utf-8");
2160
+ const content = readFileSync6(tsconfigPath, "utf-8");
2041
2161
  const tsconfig = JSON.parse(content);
2042
2162
  if (!tsconfig.compilerOptions) {
2043
2163
  tsconfig.compilerOptions = {};
@@ -2077,7 +2197,7 @@ async function installTheme2(theme) {
2077
2197
  prefixText: " "
2078
2198
  }).start();
2079
2199
  try {
2080
- const targetDir = join5(process.cwd(), "contents", "themes", theme);
2200
+ const targetDir = join6(process.cwd(), "contents", "themes", theme);
2081
2201
  if (existsSync6(targetDir)) {
2082
2202
  spinner.info(chalk9.gray(`Reference theme ${theme} already exists`));
2083
2203
  return true;
@@ -2130,7 +2250,7 @@ async function installPlugins(plugins) {
2130
2250
  prefixText: " "
2131
2251
  }).start();
2132
2252
  try {
2133
- const pluginDir = join5(process.cwd(), "contents", "plugins", plugin);
2253
+ const pluginDir = join6(process.cwd(), "contents", "plugins", plugin);
2134
2254
  if (existsSync6(pluginDir)) {
2135
2255
  spinner.info(chalk9.gray(`Plugin ${plugin} already installed`));
2136
2256
  continue;
@@ -2195,16 +2315,16 @@ import fs5 from "fs-extra";
2195
2315
  import fs6 from "fs-extra";
2196
2316
 
2197
2317
  // src/wizard/generators/index.ts
2198
- var __filename4 = fileURLToPath4(import.meta.url);
2199
- var __dirname4 = path5.dirname(__filename4);
2318
+ var __filename5 = fileURLToPath5(import.meta.url);
2319
+ var __dirname5 = path5.dirname(__filename5);
2200
2320
  function getTemplatesDir3() {
2201
2321
  try {
2202
2322
  const corePkgPath = __require.resolve("@nextsparkjs/core/package.json");
2203
2323
  return path5.join(path5.dirname(corePkgPath), "templates");
2204
2324
  } catch {
2205
2325
  const possiblePaths = [
2206
- path5.resolve(__dirname4, "../../../../../core/templates"),
2207
- path5.resolve(__dirname4, "../../../../core/templates"),
2326
+ path5.resolve(__dirname5, "../../../../../core/templates"),
2327
+ path5.resolve(__dirname5, "../../../../core/templates"),
2208
2328
  path5.resolve(process.cwd(), "node_modules/@nextsparkjs/core/templates")
2209
2329
  ];
2210
2330
  for (const p of possiblePaths) {
@@ -2228,7 +2348,8 @@ async function copyProjectFiles() {
2228
2348
  { src: "npmrc", dest: ".npmrc", force: false },
2229
2349
  { src: "tsconfig.cypress.json", dest: "tsconfig.cypress.json", force: false },
2230
2350
  { src: "cypress.d.ts", dest: "cypress.d.ts", force: false },
2231
- { src: "eslint.config.mjs", dest: "eslint.config.mjs", force: false }
2351
+ { src: "eslint.config.mjs", dest: "eslint.config.mjs", force: false },
2352
+ { src: "scripts/cy-run-prod.cjs", dest: "scripts/cy-run-prod.cjs", force: false }
2232
2353
  ];
2233
2354
  for (const item of itemsToCopy) {
2234
2355
  const srcPath = path5.join(templatesDir, item.src);
@@ -2240,12 +2361,12 @@ async function copyProjectFiles() {
2240
2361
  }
2241
2362
  }
2242
2363
  }
2243
- async function updatePackageJson(config) {
2364
+ async function updatePackageJson(config2) {
2244
2365
  const packageJsonPath = path5.resolve(process.cwd(), "package.json");
2245
2366
  let packageJson;
2246
2367
  if (!await fs7.pathExists(packageJsonPath)) {
2247
2368
  packageJson = {
2248
- name: config.projectSlug,
2369
+ name: config2.projectSlug,
2249
2370
  version: "0.1.0",
2250
2371
  private: true,
2251
2372
  scripts: {},
@@ -2264,11 +2385,13 @@ async function updatePackageJson(config) {
2264
2385
  "build:registries": "nextspark registry:build",
2265
2386
  "db:migrate": "nextspark db:migrate",
2266
2387
  "db:seed": "nextspark db:seed",
2267
- "test:theme": `jest --config contents/themes/${config.projectSlug}/tests/jest/jest.config.ts`,
2268
- "cy:open": `cypress open --config-file contents/themes/${config.projectSlug}/tests/cypress.config.ts`,
2269
- "cy:run": `cypress run --config-file contents/themes/${config.projectSlug}/tests/cypress.config.ts`,
2270
- "allure:generate": `allure generate contents/themes/${config.projectSlug}/tests/cypress/allure-results --clean -o contents/themes/${config.projectSlug}/tests/cypress/allure-report`,
2271
- "allure:open": `allure open contents/themes/${config.projectSlug}/tests/cypress/allure-report`
2388
+ "test": "node node_modules/@nextsparkjs/core/scripts/test/jest-theme.mjs",
2389
+ "cy:open": "node node_modules/@nextsparkjs/core/scripts/test/cy.mjs open",
2390
+ "cy:run": "node node_modules/@nextsparkjs/core/scripts/test/cy.mjs run",
2391
+ "cy:tags": "node node_modules/@nextsparkjs/core/scripts/test/cy.mjs tags",
2392
+ "cy:run:prod": "node scripts/cy-run-prod.cjs",
2393
+ "allure:generate": `allure generate contents/themes/${config2.projectSlug}/tests/cypress/allure-results --clean -o contents/themes/${config2.projectSlug}/tests/cypress/allure-report`,
2394
+ "allure:open": `allure open contents/themes/${config2.projectSlug}/tests/cypress/allure-report`
2272
2395
  };
2273
2396
  for (const [name, command] of Object.entries(scriptsToAdd)) {
2274
2397
  if (!packageJson.scripts[name]) {
@@ -2278,8 +2401,8 @@ async function updatePackageJson(config) {
2278
2401
  packageJson.dependencies = packageJson.dependencies || {};
2279
2402
  const depsToAdd = {
2280
2403
  // NextSpark
2281
- "@nextsparkjs/core": "^0.1.0-beta.4",
2282
- "@nextsparkjs/cli": "^0.1.0-beta.4",
2404
+ "@nextsparkjs/core": "latest",
2405
+ "@nextsparkjs/cli": "latest",
2283
2406
  // Next.js + React
2284
2407
  "next": "^15.1.0",
2285
2408
  "react": "^19.0.0",
@@ -2338,14 +2461,16 @@ async function updatePackageJson(config) {
2338
2461
  "@testing-library/react": "^16.3.0",
2339
2462
  "jest-environment-jsdom": "^29.7.0",
2340
2463
  // Cypress
2341
- "cypress": "^14.0.0",
2464
+ "cypress": "^15.8.2",
2342
2465
  "@testing-library/cypress": "^10.0.2",
2343
2466
  "@cypress/webpack-preprocessor": "^6.0.2",
2344
- "@cypress/grep": "^4.1.0",
2467
+ "@cypress/grep": "^5.0.1",
2345
2468
  "ts-loader": "^9.5.1",
2346
2469
  "webpack": "^5.97.0",
2347
2470
  "allure-cypress": "^3.0.0",
2348
- "allure-commandline": "^2.27.0"
2471
+ "allure-commandline": "^2.27.0",
2472
+ // NextSpark Testing
2473
+ "@nextsparkjs/testing": "latest"
2349
2474
  };
2350
2475
  for (const [name, version] of Object.entries(devDepsToAdd)) {
2351
2476
  if (!packageJson.devDependencies[name]) {
@@ -2354,7 +2479,7 @@ async function updatePackageJson(config) {
2354
2479
  }
2355
2480
  await fs7.writeJson(packageJsonPath, packageJson, { spaces: 2 });
2356
2481
  }
2357
- async function updateGitignore(config) {
2482
+ async function updateGitignore(config2) {
2358
2483
  const gitignorePath = path5.resolve(process.cwd(), ".gitignore");
2359
2484
  const entriesToAdd = `
2360
2485
  # NextSpark
@@ -2382,26 +2507,30 @@ contents/themes/*/tests/jest/coverage
2382
2507
  await fs7.writeFile(gitignorePath, entriesToAdd.trim());
2383
2508
  }
2384
2509
  }
2385
- async function generateProject(config) {
2510
+ async function generateProject(config2) {
2386
2511
  await copyProjectFiles();
2387
- await copyStarterTheme(config);
2388
- await copyContentFeatures(config);
2389
- await updateThemeConfig(config);
2390
- await updateDevConfig(config);
2391
- await updateAppConfig(config);
2392
- await updateBillingConfig(config);
2393
- await updateRolesConfig(config);
2394
- await updateMigrations(config);
2395
- await updatePermissionsConfig(config);
2396
- await updateDashboardConfig(config);
2397
- await updateAuthConfig(config);
2398
- await updateDashboardUIConfig(config);
2399
- await updateDevToolsConfig(config);
2400
- await processI18n(config);
2401
- await updatePackageJson(config);
2402
- await updateGitignore(config);
2403
- await generateEnvExample(config);
2404
- await updateReadme(config);
2512
+ await updateGlobalsCss(config2);
2513
+ await copyStarterTheme(config2);
2514
+ await copyContentFeatures(config2);
2515
+ await updateThemeConfig(config2);
2516
+ await updateDevConfig(config2);
2517
+ await updateAppConfig(config2);
2518
+ await updateBillingConfig(config2);
2519
+ await updateRolesConfig(config2);
2520
+ await updateMigrations(config2);
2521
+ await updateTestFiles(config2);
2522
+ await updatePermissionsConfig(config2);
2523
+ await updateEntityPermissions(config2);
2524
+ await updateDashboardConfig(config2);
2525
+ await updateAuthConfig(config2);
2526
+ await updateDashboardUIConfig(config2);
2527
+ await updateDevToolsConfig(config2);
2528
+ await processI18n(config2);
2529
+ await updatePackageJson(config2);
2530
+ await updateGitignore(config2);
2531
+ await generateEnvExample(config2);
2532
+ await updateReadme(config2);
2533
+ await copyEnvExampleToEnv();
2405
2534
  }
2406
2535
 
2407
2536
  // src/wizard/presets.ts
@@ -2564,9 +2693,9 @@ var BOX = {
2564
2693
  bottomRight: "\u2518"
2565
2694
  // bottom-right corner
2566
2695
  };
2567
- function getFileTree(config) {
2696
+ function getFileTree(config2) {
2568
2697
  const files = [];
2569
- const themeDir = `contents/themes/${config.projectSlug}`;
2698
+ const themeDir = `contents/themes/${config2.projectSlug}`;
2570
2699
  files.push(`${themeDir}/config/app.config.ts`);
2571
2700
  files.push(`${themeDir}/config/billing.config.ts`);
2572
2701
  files.push(`${themeDir}/config/dashboard.config.ts`);
@@ -2582,7 +2711,7 @@ function getFileTree(config) {
2582
2711
  files.push(`${themeDir}/blocks/hero/block.tsx`);
2583
2712
  files.push(`${themeDir}/blocks/hero/schema.ts`);
2584
2713
  files.push(`${themeDir}/blocks/hero/styles.ts`);
2585
- for (const locale of config.supportedLocales) {
2714
+ for (const locale of config2.supportedLocales) {
2586
2715
  files.push(`${themeDir}/messages/${locale}/common.json`);
2587
2716
  files.push(`${themeDir}/messages/${locale}/auth.json`);
2588
2717
  files.push(`${themeDir}/messages/${locale}/dashboard.json`);
@@ -2595,7 +2724,7 @@ function getFileTree(config) {
2595
2724
  files.push(`${themeDir}/styles/theme.css`);
2596
2725
  files.push(`${themeDir}/styles/components.css`);
2597
2726
  files.push(`${themeDir}/tests/cypress.config.ts`);
2598
- files.push(`${themeDir}/tests/jest/jest.config.ts`);
2727
+ files.push(`${themeDir}/tests/jest/jest.config.cjs`);
2599
2728
  files.push(`${themeDir}/tests/cypress/e2e/auth.cy.ts`);
2600
2729
  files.push(`${themeDir}/tests/cypress/e2e/dashboard.cy.ts`);
2601
2730
  files.push(`${themeDir}/tests/jest/components/hero.test.tsx`);
@@ -2633,10 +2762,10 @@ function groupFilesByCategory(files) {
2633
2762
  function formatFilePath(file, themeDir) {
2634
2763
  return file.replace(`${themeDir}/`, "");
2635
2764
  }
2636
- function showConfigPreview(config) {
2637
- const files = getFileTree(config);
2765
+ function showConfigPreview(config2) {
2766
+ const files = getFileTree(config2);
2638
2767
  const groups = groupFilesByCategory(files);
2639
- const themeDir = `contents/themes/${config.projectSlug}`;
2768
+ const themeDir = `contents/themes/${config2.projectSlug}`;
2640
2769
  console.log("");
2641
2770
  console.log(chalk10.cyan.bold(" Theme Preview"));
2642
2771
  console.log(chalk10.gray(" " + "=".repeat(50)));
@@ -2698,8 +2827,8 @@ function showConfigPreview(config) {
2698
2827
  }
2699
2828
  console.log("");
2700
2829
  console.log(chalk10.gray(" Locales configured:"));
2701
- for (const locale of config.supportedLocales) {
2702
- const isDefault = locale === config.defaultLocale;
2830
+ for (const locale of config2.supportedLocales) {
2831
+ const isDefault = locale === config2.defaultLocale;
2703
2832
  const suffix = isDefault ? chalk10.cyan(" (default)") : "";
2704
2833
  console.log(chalk10.gray(` - `) + chalk10.white(locale) + suffix);
2705
2834
  }
@@ -2741,25 +2870,25 @@ async function runWizard(options = { mode: "interactive" }) {
2741
2870
  selectedPlugins = getRequiredPlugins(selectedTheme);
2742
2871
  }
2743
2872
  }
2744
- let config;
2873
+ let config2;
2745
2874
  if (options.preset) {
2746
- config = await runPresetMode(options.preset, options);
2875
+ config2 = await runPresetMode(options.preset, options);
2747
2876
  } else {
2748
2877
  switch (options.mode) {
2749
2878
  case "quick":
2750
- config = await runQuickPrompts();
2879
+ config2 = await runQuickPrompts();
2751
2880
  break;
2752
2881
  case "expert":
2753
- config = await runExpertPrompts();
2882
+ config2 = await runExpertPrompts();
2754
2883
  break;
2755
2884
  case "interactive":
2756
2885
  default:
2757
- config = await runAllPrompts();
2886
+ config2 = await runAllPrompts();
2758
2887
  break;
2759
2888
  }
2760
2889
  }
2761
- showConfigSummary(config);
2762
- showConfigPreview(config);
2890
+ showConfigSummary(config2);
2891
+ showConfigPreview(config2);
2763
2892
  if (!options.yes) {
2764
2893
  console.log("");
2765
2894
  const proceed = await confirm5({
@@ -2785,7 +2914,7 @@ async function runWizard(options = { mode: "interactive" }) {
2785
2914
  prefixText: " "
2786
2915
  }).start();
2787
2916
  try {
2788
- await generateProject(config);
2917
+ await generateProject(config2);
2789
2918
  spinner.succeed("Project generated successfully!");
2790
2919
  } catch (error) {
2791
2920
  spinner.fail("Failed to generate project");
@@ -2794,7 +2923,41 @@ async function runWizard(options = { mode: "interactive" }) {
2794
2923
  if (selectedTheme || selectedPlugins.length > 0) {
2795
2924
  await installThemeAndPlugins(selectedTheme, selectedPlugins);
2796
2925
  }
2797
- showNextSteps(config, selectedTheme);
2926
+ const installSpinner = ora7({
2927
+ text: "Installing dependencies...",
2928
+ prefixText: " "
2929
+ }).start();
2930
+ try {
2931
+ execSync("pnpm install --force", {
2932
+ cwd: process.cwd(),
2933
+ stdio: "pipe"
2934
+ });
2935
+ installSpinner.succeed("Dependencies installed!");
2936
+ } catch (error) {
2937
+ installSpinner.fail("Failed to install dependencies");
2938
+ console.log(chalk11.yellow(' Run "pnpm install" manually to install dependencies'));
2939
+ }
2940
+ const registrySpinner = ora7({
2941
+ text: "Building registries...",
2942
+ prefixText: " "
2943
+ }).start();
2944
+ try {
2945
+ const projectRoot = process.cwd();
2946
+ const registryScript = join7(projectRoot, "node_modules/@nextsparkjs/core/scripts/build/registry.mjs");
2947
+ execSync(`node "${registryScript}" --build`, {
2948
+ cwd: projectRoot,
2949
+ stdio: "pipe",
2950
+ env: {
2951
+ ...process.env,
2952
+ NEXTSPARK_PROJECT_ROOT: projectRoot
2953
+ }
2954
+ });
2955
+ registrySpinner.succeed("Registries built!");
2956
+ } catch (error) {
2957
+ registrySpinner.fail("Failed to build registries");
2958
+ console.log(chalk11.yellow(' Registries will be built automatically when you run "pnpm dev"'));
2959
+ }
2960
+ showNextSteps(config2, selectedTheme);
2798
2961
  } catch (error) {
2799
2962
  if (error instanceof Error) {
2800
2963
  if (error.message.includes("User force closed")) {
@@ -2834,46 +2997,46 @@ async function runPresetMode(presetName, options) {
2834
2997
  console.log("");
2835
2998
  projectInfo = await promptProjectInfo();
2836
2999
  }
2837
- const config = applyPreset(projectInfo, presetName);
2838
- return config;
3000
+ const config2 = applyPreset(projectInfo, presetName);
3001
+ return config2;
2839
3002
  }
2840
- function showConfigSummary(config) {
3003
+ function showConfigSummary(config2) {
2841
3004
  console.log("");
2842
3005
  console.log(chalk11.cyan(" " + "=".repeat(60)));
2843
3006
  console.log(chalk11.bold.white(" Configuration Summary"));
2844
3007
  console.log(chalk11.cyan(" " + "=".repeat(60)));
2845
3008
  console.log("");
2846
3009
  console.log(chalk11.white(" Project:"));
2847
- console.log(chalk11.gray(` Name: ${chalk11.white(config.projectName)}`));
2848
- console.log(chalk11.gray(` Slug: ${chalk11.white(config.projectSlug)}`));
2849
- console.log(chalk11.gray(` Description: ${chalk11.white(config.projectDescription)}`));
3010
+ console.log(chalk11.gray(` Name: ${chalk11.white(config2.projectName)}`));
3011
+ console.log(chalk11.gray(` Slug: ${chalk11.white(config2.projectSlug)}`));
3012
+ console.log(chalk11.gray(` Description: ${chalk11.white(config2.projectDescription)}`));
2850
3013
  console.log("");
2851
3014
  console.log(chalk11.white(" Team Mode:"));
2852
- console.log(chalk11.gray(` Mode: ${chalk11.white(config.teamMode)}`));
2853
- console.log(chalk11.gray(` Roles: ${chalk11.white(config.teamRoles.join(", "))}`));
3015
+ console.log(chalk11.gray(` Mode: ${chalk11.white(config2.teamMode)}`));
3016
+ console.log(chalk11.gray(` Roles: ${chalk11.white(config2.teamRoles.join(", "))}`));
2854
3017
  console.log("");
2855
3018
  console.log(chalk11.white(" Internationalization:"));
2856
- console.log(chalk11.gray(` Default: ${chalk11.white(config.defaultLocale)}`));
2857
- console.log(chalk11.gray(` Languages: ${chalk11.white(config.supportedLocales.join(", "))}`));
3019
+ console.log(chalk11.gray(` Default: ${chalk11.white(config2.defaultLocale)}`));
3020
+ console.log(chalk11.gray(` Languages: ${chalk11.white(config2.supportedLocales.join(", "))}`));
2858
3021
  console.log("");
2859
3022
  console.log(chalk11.white(" Billing:"));
2860
- console.log(chalk11.gray(` Model: ${chalk11.white(config.billingModel)}`));
2861
- console.log(chalk11.gray(` Currency: ${chalk11.white(config.currency.toUpperCase())}`));
3023
+ console.log(chalk11.gray(` Model: ${chalk11.white(config2.billingModel)}`));
3024
+ console.log(chalk11.gray(` Currency: ${chalk11.white(config2.currency.toUpperCase())}`));
2862
3025
  console.log("");
2863
3026
  console.log(chalk11.white(" Features:"));
2864
- const enabledFeatures = Object.entries(config.features).filter(([_, enabled]) => enabled).map(([feature]) => feature);
3027
+ const enabledFeatures = Object.entries(config2.features).filter(([_, enabled]) => enabled).map(([feature]) => feature);
2865
3028
  console.log(chalk11.gray(` Enabled: ${chalk11.white(enabledFeatures.join(", ") || "None")}`));
2866
3029
  console.log("");
2867
3030
  console.log(chalk11.white(" Authentication:"));
2868
- const enabledAuth = Object.entries(config.auth).filter(([_, enabled]) => enabled).map(([method]) => formatAuthMethod(method));
3031
+ const enabledAuth = Object.entries(config2.auth).filter(([_, enabled]) => enabled).map(([method]) => formatAuthMethod(method));
2869
3032
  console.log(chalk11.gray(` Methods: ${chalk11.white(enabledAuth.join(", ") || "None")}`));
2870
3033
  console.log("");
2871
3034
  console.log(chalk11.white(" Dashboard:"));
2872
- const enabledDashboard = Object.entries(config.dashboard).filter(([_, enabled]) => enabled).map(([feature]) => formatDashboardFeature(feature));
3035
+ const enabledDashboard = Object.entries(config2.dashboard).filter(([_, enabled]) => enabled).map(([feature]) => formatDashboardFeature(feature));
2873
3036
  console.log(chalk11.gray(` Features: ${chalk11.white(enabledDashboard.join(", ") || "None")}`));
2874
3037
  console.log("");
2875
3038
  console.log(chalk11.white(" Dev Tools:"));
2876
- const enabledDevTools = Object.entries(config.dev).filter(([_, enabled]) => enabled).map(([tool]) => formatDevTool(tool));
3039
+ const enabledDevTools = Object.entries(config2.dev).filter(([_, enabled]) => enabled).map(([tool]) => formatDevTool(tool));
2877
3040
  console.log(chalk11.gray(` Enabled: ${chalk11.white(enabledDevTools.join(", ") || "None")}`));
2878
3041
  }
2879
3042
  function formatAuthMethod(method) {
@@ -2900,38 +3063,38 @@ function formatDevTool(tool) {
2900
3063
  };
2901
3064
  return mapping[tool] || tool;
2902
3065
  }
2903
- function showNextSteps(config, referenceTheme = null) {
3066
+ function showNextSteps(config2, referenceTheme = null) {
2904
3067
  console.log("");
2905
3068
  console.log(chalk11.cyan(" " + "=".repeat(60)));
2906
- console.log(chalk11.bold.green(" \u2728 NextSpark project created successfully!"));
3069
+ console.log(chalk11.bold.green(" \u2728 NextSpark project ready!"));
2907
3070
  console.log(chalk11.cyan(" " + "=".repeat(60)));
2908
3071
  console.log("");
2909
3072
  console.log(chalk11.bold.white(" Next steps:"));
2910
3073
  console.log("");
2911
- console.log(chalk11.white(" 1. Install dependencies:"));
2912
- console.log(chalk11.cyan(" pnpm install"));
3074
+ console.log(chalk11.white(" 1. Configure your .env file:"));
3075
+ console.log(chalk11.gray(" Edit these values in .env:"));
2913
3076
  console.log("");
2914
- console.log(chalk11.white(" 2. Set up your environment:"));
2915
- console.log(chalk11.gray(" Copy .env.example to .env and configure:"));
2916
- console.log(chalk11.yellow(" - DATABASE_URL"));
2917
- console.log(chalk11.yellow(" - BETTER_AUTH_SECRET"));
2918
- console.log(chalk11.yellow(` - NEXT_PUBLIC_ACTIVE_THEME=${config.projectSlug}`));
3077
+ console.log(chalk11.yellow(" DATABASE_URL"));
3078
+ console.log(chalk11.gray(" PostgreSQL connection string"));
3079
+ console.log(chalk11.dim(" Example: postgresql://user:pass@localhost:5432/mydb"));
2919
3080
  console.log("");
2920
- console.log(chalk11.white(" 3. Generate registries:"));
2921
- console.log(chalk11.cyan(" pnpm build:registries"));
3081
+ console.log(chalk11.yellow(" BETTER_AUTH_SECRET"));
3082
+ console.log(chalk11.gray(" Generate with:"));
3083
+ console.log(chalk11.cyan(" openssl rand -base64 32"));
2922
3084
  console.log("");
2923
- console.log(chalk11.white(" 4. Run database migrations:"));
3085
+ console.log(chalk11.white(" 2. Run database migrations:"));
2924
3086
  console.log(chalk11.cyan(" pnpm db:migrate"));
2925
3087
  console.log("");
2926
- console.log(chalk11.white(" 5. Start the development server:"));
3088
+ console.log(chalk11.white(" 3. Start the development server:"));
2927
3089
  console.log(chalk11.cyan(" pnpm dev"));
2928
3090
  console.log("");
2929
3091
  console.log(chalk11.gray(" " + "-".repeat(60)));
2930
- console.log(chalk11.gray(` Your theme: ${chalk11.white(`contents/themes/${config.projectSlug}/`)}`));
3092
+ console.log(chalk11.gray(` Theme: ${chalk11.white(`contents/themes/${config2.projectSlug}/`)}`));
3093
+ console.log(chalk11.gray(` Active theme: ${chalk11.green(`NEXT_PUBLIC_ACTIVE_THEME=${config2.projectSlug}`)}`));
2931
3094
  if (referenceTheme) {
2932
- console.log(chalk11.gray(` Reference theme: ${chalk11.white(`contents/themes/${referenceTheme}/`)}`));
3095
+ console.log(chalk11.gray(` Reference: ${chalk11.white(`contents/themes/${referenceTheme}/`)}`));
2933
3096
  }
2934
- console.log(chalk11.gray(" Documentation: https://nextspark.dev/docs"));
3097
+ console.log(chalk11.gray(" Docs: https://nextspark.dev/docs"));
2935
3098
  console.log("");
2936
3099
  }
2937
3100
  function findLocalCoreTarball() {
@@ -2942,14 +3105,14 @@ function findLocalCoreTarball() {
2942
3105
  (f) => f.includes("nextsparkjs-core") && f.endsWith(".tgz")
2943
3106
  );
2944
3107
  if (coreTarball) {
2945
- return join6(cwd, coreTarball);
3108
+ return join7(cwd, coreTarball);
2946
3109
  }
2947
3110
  } catch {
2948
3111
  }
2949
3112
  return null;
2950
3113
  }
2951
3114
  function isCoreInstalled() {
2952
- const corePath = join6(process.cwd(), "node_modules", "@nextsparkjs", "core");
3115
+ const corePath = join7(process.cwd(), "node_modules", "@nextsparkjs", "core");
2953
3116
  return existsSync7(corePath);
2954
3117
  }
2955
3118
  async function installCore() {
@@ -2967,8 +3130,8 @@ async function installCore() {
2967
3130
  packageSpec = localTarball;
2968
3131
  spinner.text = "Installing @nextsparkjs/core from local tarball...";
2969
3132
  }
2970
- const useYarn = existsSync7(join6(process.cwd(), "yarn.lock"));
2971
- const usePnpm = existsSync7(join6(process.cwd(), "pnpm-lock.yaml"));
3133
+ const useYarn = existsSync7(join7(process.cwd(), "yarn.lock"));
3134
+ const usePnpm = existsSync7(join7(process.cwd(), "pnpm-lock.yaml"));
2972
3135
  let installCmd;
2973
3136
  if (usePnpm) {
2974
3137
  installCmd = `pnpm add ${packageSpec}`;
@@ -2993,7 +3156,7 @@ async function installCore() {
2993
3156
  }
2994
3157
  }
2995
3158
  async function copyNpmrc() {
2996
- const npmrcPath = join6(process.cwd(), ".npmrc");
3159
+ const npmrcPath = join7(process.cwd(), ".npmrc");
2997
3160
  if (existsSync7(npmrcPath)) {
2998
3161
  return;
2999
3162
  }
@@ -3017,10 +3180,10 @@ function parsePlugins(pluginsStr) {
3017
3180
  }
3018
3181
  function hasExistingProject() {
3019
3182
  const projectRoot = process.cwd();
3020
- return existsSync8(join7(projectRoot, "contents")) || existsSync8(join7(projectRoot, ".nextspark"));
3183
+ return existsSync8(join8(projectRoot, "contents")) || existsSync8(join8(projectRoot, ".nextspark"));
3021
3184
  }
3022
3185
  function generateInitialRegistries(registriesDir) {
3023
- writeFileSync2(join7(registriesDir, "block-registry.ts"), `// Auto-generated by nextspark init
3186
+ writeFileSync2(join8(registriesDir, "block-registry.ts"), `// Auto-generated by nextspark init
3024
3187
  import type { ComponentType } from 'react'
3025
3188
 
3026
3189
  export const BLOCK_REGISTRY: Record<string, {
@@ -3033,26 +3196,26 @@ export const BLOCK_REGISTRY: Record<string, {
3033
3196
 
3034
3197
  export const BLOCK_COMPONENTS: Record<string, React.LazyExoticComponent<ComponentType<any>>> = {}
3035
3198
  `);
3036
- writeFileSync2(join7(registriesDir, "theme-registry.ts"), `// Auto-generated by nextspark init
3199
+ writeFileSync2(join8(registriesDir, "theme-registry.ts"), `// Auto-generated by nextspark init
3037
3200
  export const THEME_REGISTRY: Record<string, unknown> = {}
3038
3201
  `);
3039
- writeFileSync2(join7(registriesDir, "entity-registry.ts"), `// Auto-generated by nextspark init
3202
+ writeFileSync2(join8(registriesDir, "entity-registry.ts"), `// Auto-generated by nextspark init
3040
3203
  export const ENTITY_REGISTRY: Record<string, unknown> = {}
3041
3204
  `);
3042
- writeFileSync2(join7(registriesDir, "entity-registry.client.ts"), `// Auto-generated by nextspark init
3205
+ writeFileSync2(join8(registriesDir, "entity-registry.client.ts"), `// Auto-generated by nextspark init
3043
3206
  export const CLIENT_ENTITY_REGISTRY: Record<string, unknown> = {}
3044
3207
  export function parseChildEntity(path: string) { return null }
3045
3208
  export function getEntityApiPath(entity: string) { return \`/api/\${entity}\` }
3046
3209
  export function clientMetaSystemAdapter() { return {} }
3047
3210
  export type ClientEntityConfig = Record<string, unknown>
3048
3211
  `);
3049
- writeFileSync2(join7(registriesDir, "billing-registry.ts"), `// Auto-generated by nextspark init
3212
+ writeFileSync2(join8(registriesDir, "billing-registry.ts"), `// Auto-generated by nextspark init
3050
3213
  export const BILLING_REGISTRY = { plans: [], features: [] }
3051
3214
  `);
3052
- writeFileSync2(join7(registriesDir, "plugin-registry.ts"), `// Auto-generated by nextspark init
3215
+ writeFileSync2(join8(registriesDir, "plugin-registry.ts"), `// Auto-generated by nextspark init
3053
3216
  export const PLUGIN_REGISTRY: Record<string, unknown> = {}
3054
3217
  `);
3055
- writeFileSync2(join7(registriesDir, "testing-registry.ts"), `// Auto-generated by nextspark init
3218
+ writeFileSync2(join8(registriesDir, "testing-registry.ts"), `// Auto-generated by nextspark init
3056
3219
  export const FLOW_REGISTRY: Record<string, unknown> = {}
3057
3220
  export const FEATURE_REGISTRY: Record<string, unknown> = {}
3058
3221
  export const TAGS_REGISTRY: Record<string, unknown> = {}
@@ -3060,11 +3223,11 @@ export const COVERAGE_SUMMARY = { total: 0, covered: 0 }
3060
3223
  export type FlowEntry = unknown
3061
3224
  export type FeatureEntry = unknown
3062
3225
  `);
3063
- writeFileSync2(join7(registriesDir, "docs-registry.ts"), `// Auto-generated by nextspark init
3226
+ writeFileSync2(join8(registriesDir, "docs-registry.ts"), `// Auto-generated by nextspark init
3064
3227
  export const DOCS_REGISTRY = { sections: [], pages: [] }
3065
3228
  export type DocSectionMeta = { title: string; slug: string }
3066
3229
  `);
3067
- writeFileSync2(join7(registriesDir, "index.ts"), `// Auto-generated by nextspark init
3230
+ writeFileSync2(join8(registriesDir, "index.ts"), `// Auto-generated by nextspark init
3068
3231
  export * from './block-registry'
3069
3232
  export * from './theme-registry'
3070
3233
  export * from './entity-registry'
@@ -3079,18 +3242,18 @@ async function simpleInit(options) {
3079
3242
  const spinner = ora8("Initializing NextSpark project...").start();
3080
3243
  const projectRoot = process.cwd();
3081
3244
  try {
3082
- const nextspark = join7(projectRoot, ".nextspark");
3083
- const registriesDir = join7(nextspark, "registries");
3245
+ const nextspark = join8(projectRoot, ".nextspark");
3246
+ const registriesDir = join8(nextspark, "registries");
3084
3247
  if (!existsSync8(registriesDir) || options.force) {
3085
3248
  mkdirSync2(registriesDir, { recursive: true });
3086
3249
  spinner.text = "Creating .nextspark/registries/";
3087
3250
  generateInitialRegistries(registriesDir);
3088
3251
  spinner.text = "Generated initial registries";
3089
3252
  }
3090
- const tsconfigPath = join7(projectRoot, "tsconfig.json");
3253
+ const tsconfigPath = join8(projectRoot, "tsconfig.json");
3091
3254
  if (existsSync8(tsconfigPath)) {
3092
3255
  spinner.text = "Updating tsconfig.json paths...";
3093
- const tsconfig = JSON.parse(readFileSync6(tsconfigPath, "utf-8"));
3256
+ const tsconfig = JSON.parse(readFileSync7(tsconfigPath, "utf-8"));
3094
3257
  tsconfig.compilerOptions = tsconfig.compilerOptions || {};
3095
3258
  tsconfig.compilerOptions.paths = {
3096
3259
  ...tsconfig.compilerOptions.paths,
@@ -3099,7 +3262,7 @@ async function simpleInit(options) {
3099
3262
  };
3100
3263
  writeFileSync2(tsconfigPath, JSON.stringify(tsconfig, null, 2));
3101
3264
  }
3102
- const envExample = join7(projectRoot, ".env.example");
3265
+ const envExample = join8(projectRoot, ".env.example");
3103
3266
  if (!existsSync8(envExample)) {
3104
3267
  const envContent = `# NextSpark Configuration
3105
3268
  DATABASE_URL="postgresql://user:password@localhost:5432/db"
@@ -3608,10 +3771,101 @@ async function doctorCommand() {
3608
3771
  }
3609
3772
  }
3610
3773
 
3774
+ // src/commands/db.ts
3775
+ import { spawn as spawn5 } from "child_process";
3776
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
3777
+ import { join as join9 } from "path";
3778
+ import chalk15 from "chalk";
3779
+ import ora9 from "ora";
3780
+ function loadProjectEnv4(projectRoot) {
3781
+ const envPath = join9(projectRoot, ".env");
3782
+ const envVars = {};
3783
+ if (existsSync9(envPath)) {
3784
+ const content = readFileSync8(envPath, "utf-8");
3785
+ for (const line of content.split("\n")) {
3786
+ const trimmed = line.trim();
3787
+ if (trimmed && !trimmed.startsWith("#")) {
3788
+ const [key, ...valueParts] = trimmed.split("=");
3789
+ if (key && valueParts.length > 0) {
3790
+ let value = valueParts.join("=");
3791
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
3792
+ value = value.slice(1, -1);
3793
+ }
3794
+ envVars[key] = value;
3795
+ }
3796
+ }
3797
+ }
3798
+ }
3799
+ return envVars;
3800
+ }
3801
+ async function dbMigrateCommand() {
3802
+ const spinner = ora9("Preparing to run migrations...").start();
3803
+ try {
3804
+ const coreDir = getCoreDir();
3805
+ const projectRoot = getProjectRoot();
3806
+ const migrationsScript = join9(coreDir, "scripts", "db", "run-migrations.mjs");
3807
+ if (!existsSync9(migrationsScript)) {
3808
+ spinner.fail("Migrations script not found");
3809
+ console.error(chalk15.red(`Expected script at: ${migrationsScript}`));
3810
+ process.exit(1);
3811
+ }
3812
+ spinner.succeed("Core package found");
3813
+ const projectEnv = loadProjectEnv4(projectRoot);
3814
+ if (!projectEnv.DATABASE_URL) {
3815
+ spinner.fail("DATABASE_URL not found in .env file");
3816
+ console.error(chalk15.red("Please configure DATABASE_URL in your .env file"));
3817
+ process.exit(1);
3818
+ }
3819
+ if (!projectEnv.NEXT_PUBLIC_ACTIVE_THEME) {
3820
+ spinner.fail("NEXT_PUBLIC_ACTIVE_THEME not found in .env file");
3821
+ console.error(chalk15.red("Please configure NEXT_PUBLIC_ACTIVE_THEME in your .env file"));
3822
+ process.exit(1);
3823
+ }
3824
+ spinner.start("Running database migrations...");
3825
+ const migrateProcess = spawn5("node", [migrationsScript], {
3826
+ cwd: projectRoot,
3827
+ stdio: "inherit",
3828
+ env: {
3829
+ ...projectEnv,
3830
+ ...process.env,
3831
+ NEXTSPARK_PROJECT_ROOT: projectRoot,
3832
+ NEXTSPARK_CORE_DIR: coreDir
3833
+ }
3834
+ });
3835
+ migrateProcess.on("error", (err) => {
3836
+ spinner.fail("Migration failed");
3837
+ console.error(chalk15.red(err.message));
3838
+ process.exit(1);
3839
+ });
3840
+ migrateProcess.on("close", (code) => {
3841
+ if (code === 0) {
3842
+ console.log(chalk15.green("\n\u2705 Migrations completed successfully!"));
3843
+ process.exit(0);
3844
+ } else {
3845
+ console.error(chalk15.red(`
3846
+ \u274C Migrations failed with exit code ${code}`));
3847
+ process.exit(code ?? 1);
3848
+ }
3849
+ });
3850
+ } catch (error) {
3851
+ spinner.fail("Migration preparation failed");
3852
+ if (error instanceof Error) {
3853
+ console.error(chalk15.red(error.message));
3854
+ }
3855
+ process.exit(1);
3856
+ }
3857
+ }
3858
+ async function dbSeedCommand() {
3859
+ console.log(chalk15.cyan("\u2139\uFE0F Sample data is included as part of the migration process."));
3860
+ console.log(chalk15.cyan(" Running db:migrate to apply all migrations including sample data...\n"));
3861
+ await dbMigrateCommand();
3862
+ }
3863
+
3611
3864
  // src/cli.ts
3865
+ config();
3612
3866
  var program = new Command();
3613
3867
  program.name("nextspark").description("NextSpark CLI - Professional SaaS Boilerplate").version("0.1.0-beta.4");
3614
- program.command("dev").description("Start development server with registry watcher").option("-p, --port <port>", "Port to run the dev server on", "3000").option("--no-registry", "Disable registry watcher").action(devCommand);
3868
+ program.command("dev").description("Start development server with registry watcher").option("-p, --port <port>", "Port to run the dev server on", process.env.PORT || "3000").option("--no-registry", "Disable registry watcher").action(devCommand);
3615
3869
  program.command("build").description("Build for production").option("--no-registry", "Skip registry generation before build").action(buildCommand);
3616
3870
  program.command("generate").description("Generate all registries").option("-w, --watch", "Watch for changes").action(generateCommand);
3617
3871
  var registry = program.command("registry").description("Registry management commands");
@@ -3623,8 +3877,13 @@ program.command("init").description("Initialize NextSpark project").option("-f,
3623
3877
  program.command("add:plugin <package>").description("Add a plugin to your project").option("-v, --version <version>", "Specific version to install").option("-f, --force", "Overwrite if already exists").option("--skip-postinstall", "Skip postinstall hooks").option("--no-deps", "Skip installing dependencies").option("--dry-run", "Show what would be done without making changes").action(addPluginCommand);
3624
3878
  program.command("add:theme <package>").description("Add a theme to your project").option("-v, --version <version>", "Specific version to install").option("-f, --force", "Overwrite if already exists").option("--skip-postinstall", "Skip postinstall hooks").option("--no-deps", "Skip installing dependencies").option("--dry-run", "Show what would be done without making changes").action(addThemeCommand);
3625
3879
  program.command("doctor").description("Run health check on NextSpark project").action(doctorCommand);
3880
+ var db = program.command("db").description("Database management commands");
3881
+ db.command("migrate").description("Run database migrations").action(dbMigrateCommand);
3882
+ db.command("seed").description("Seed database with sample data").action(dbSeedCommand);
3883
+ program.command("db:migrate").description("Run database migrations (alias)").action(dbMigrateCommand);
3884
+ program.command("db:seed").description("Seed database with sample data (alias)").action(dbSeedCommand);
3626
3885
  program.showHelpAfterError();
3627
3886
  program.configureOutput({
3628
- writeErr: (str) => process.stderr.write(chalk15.red(str))
3887
+ writeErr: (str) => process.stderr.write(chalk16.red(str))
3629
3888
  });
3630
3889
  program.parse();