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

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