@onexapis/cli 1.1.36 → 1.1.38

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.mjs CHANGED
@@ -2,18 +2,18 @@
2
2
  import chalk4 from 'chalk';
3
3
  import ora from 'ora';
4
4
  import * as esbuild from 'esbuild';
5
- import path8 from 'path';
6
- import fs7 from 'fs/promises';
7
- import crypto2 from 'crypto';
5
+ import path10 from 'path';
6
+ import fs9 from 'fs/promises';
7
+ import crypto from 'crypto';
8
8
  import { glob } from 'glob';
9
9
  import { createRequire } from 'module';
10
- import os3 from 'os';
10
+ import os from 'os';
11
11
  import dotenv from 'dotenv';
12
12
  import fs from 'fs-extra';
13
13
  import ejs from 'ejs';
14
- import { execSync, spawn } from 'child_process';
14
+ import spawn2 from 'cross-spawn';
15
15
  import { Command } from 'commander';
16
- import fs2 from 'fs';
16
+ import fs4 from 'fs';
17
17
  import inquirer from 'inquirer';
18
18
  import archiver from 'archiver';
19
19
  import FormData from 'form-data';
@@ -23,6 +23,7 @@ import AdmZip from 'adm-zip';
23
23
  import chokidar from 'chokidar';
24
24
  import http from 'http';
25
25
  import { WebSocketServer, WebSocket } from 'ws';
26
+ import semver from 'semver';
26
27
 
27
28
  var __defProp = Object.defineProperty;
28
29
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -104,8 +105,8 @@ async function generateThemeCSS(themePath, outDir) {
104
105
  const tailwindcss = (await import('tailwindcss')).default;
105
106
  const tailwindConfig = {
106
107
  content: [
107
- path8.join(themePath, "sections/**/*.{ts,tsx}"),
108
- path8.join(themePath, "components/**/*.{ts,tsx}")
108
+ path10.join(themePath, "sections/**/*.{ts,tsx}"),
109
+ path10.join(themePath, "components/**/*.{ts,tsx}")
109
110
  ],
110
111
  theme: { extend: {} },
111
112
  plugins: []
@@ -115,7 +116,7 @@ async function generateThemeCSS(themePath, outDir) {
115
116
  inputCSS,
116
117
  { from: void 0 }
117
118
  );
118
- await fs7.writeFile(path8.join(outDir, "bundle.css"), result.css);
119
+ await fs9.writeFile(path10.join(outDir, "bundle.css"), result.css);
119
120
  logger.info("Generated bundle.css");
120
121
  } catch (err) {
121
122
  logger.warning(
@@ -126,12 +127,12 @@ async function generateThemeCSS(themePath, outDir) {
126
127
  async function resolveNodeModulesFile(startDir, relativePath) {
127
128
  let dir = startDir;
128
129
  while (true) {
129
- const candidate = path8.join(dir, "node_modules", relativePath);
130
+ const candidate = path10.join(dir, "node_modules", relativePath);
130
131
  try {
131
- await fs7.access(candidate);
132
+ await fs9.access(candidate);
132
133
  return candidate;
133
134
  } catch {
134
- const parent = path8.dirname(dir);
135
+ const parent = path10.dirname(dir);
135
136
  if (parent === dir) break;
136
137
  dir = parent;
137
138
  }
@@ -155,7 +156,7 @@ async function scanImportsFromPackage(sourceDir, packageName) {
155
156
  });
156
157
  for (const file of sourceFiles) {
157
158
  try {
158
- const content = await fs7.readFile(path8.join(sourceDir, file), "utf-8");
159
+ const content = await fs9.readFile(path10.join(sourceDir, file), "utf-8");
159
160
  for (const match of content.matchAll(namespaceImportRegex)) {
160
161
  const subpath = match[1] ? match[1].slice(1) : "";
161
162
  if (!result[subpath]) result[subpath] = /* @__PURE__ */ new Set();
@@ -209,17 +210,17 @@ function createCoreGlobalPlugin(themePath) {
209
210
  const distFileName = subpath ? `${subpath}.mjs` : "index.mjs";
210
211
  let distPath = await resolveNodeModulesFile(
211
212
  themePath,
212
- path8.join("@onexapis", "core", "dist", distFileName)
213
+ path10.join("@onexapis", "core", "dist", distFileName)
213
214
  );
214
215
  if (!distPath) {
215
216
  distPath = await resolveNodeModulesFile(
216
217
  __dirname,
217
- path8.join("@onexapis", "core", "dist", distFileName)
218
+ path10.join("@onexapis", "core", "dist", distFileName)
218
219
  );
219
220
  }
220
221
  try {
221
222
  if (!distPath) throw new Error("not found");
222
- const distContent = await fs7.readFile(distPath, "utf-8");
223
+ const distContent = await fs9.readFile(distPath, "utf-8");
223
224
  const exportMatches = distContent.matchAll(/export\s*\{([^}]+)\}/g);
224
225
  for (const m of exportMatches) {
225
226
  const names = m[1].split(",").map((n) => {
@@ -448,7 +449,7 @@ async function generateThemeData(themePath, outputDir, themeId) {
448
449
  const pages = {};
449
450
  for (const ext of [".ts", ".js"]) {
450
451
  try {
451
- const mod = await jiti.import(path8.join(themePath, `theme.config${ext}`));
452
+ const mod = await jiti.import(path10.join(themePath, `theme.config${ext}`));
452
453
  themeConfig = mod.default || mod;
453
454
  break;
454
455
  } catch {
@@ -456,20 +457,20 @@ async function generateThemeData(themePath, outputDir, themeId) {
456
457
  }
457
458
  for (const ext of [".ts", ".js"]) {
458
459
  try {
459
- const mod = await jiti.import(path8.join(themePath, `theme.layout${ext}`));
460
+ const mod = await jiti.import(path10.join(themePath, `theme.layout${ext}`));
460
461
  layoutConfig = mod.default || mod;
461
462
  break;
462
463
  } catch {
463
464
  }
464
465
  }
465
466
  const schemas = {};
466
- const sectionsDir = path8.join(themePath, "sections");
467
+ const sectionsDir = path10.join(themePath, "sections");
467
468
  try {
468
- const sectionDirs = await fs7.readdir(sectionsDir);
469
+ const sectionDirs = await fs9.readdir(sectionsDir);
469
470
  for (const dir of sectionDirs) {
470
- const schemaFile = path8.join(sectionsDir, dir, `${dir}.schema.ts`);
471
+ const schemaFile = path10.join(sectionsDir, dir, `${dir}.schema.ts`);
471
472
  try {
472
- await fs7.access(schemaFile);
473
+ await fs9.access(schemaFile);
473
474
  const mod = await jiti.import(schemaFile);
474
475
  for (const [key, value] of Object.entries(mod)) {
475
476
  if (key.endsWith("Schema") && value && typeof value === "object" && value.type) {
@@ -481,14 +482,14 @@ async function generateThemeData(themePath, outputDir, themeId) {
481
482
  }
482
483
  } catch {
483
484
  }
484
- const pagesDir = path8.join(themePath, "pages");
485
+ const pagesDir = path10.join(themePath, "pages");
485
486
  try {
486
- const files = await fs7.readdir(pagesDir);
487
+ const files = await fs9.readdir(pagesDir);
487
488
  for (const file of files) {
488
489
  if (!file.match(/\.(ts|js)$/)) continue;
489
490
  const name = file.replace(/\.(ts|js)$/, "");
490
491
  try {
491
- const mod = await jiti.import(path8.join(pagesDir, file));
492
+ const mod = await jiti.import(path10.join(pagesDir, file));
492
493
  const config = mod.default || mod;
493
494
  const sections = (config.sections || []).map((section) => {
494
495
  const schema = schemas[section.type];
@@ -516,8 +517,8 @@ async function generateThemeData(themePath, outputDir, themeId) {
516
517
  }
517
518
  } catch {
518
519
  }
519
- await fs7.writeFile(
520
- path8.join(outputDir, "theme-data.json"),
520
+ await fs9.writeFile(
521
+ path10.join(outputDir, "theme-data.json"),
521
522
  JSON.stringify(
522
523
  {
523
524
  themeId,
@@ -540,53 +541,53 @@ async function generateThemeData(themePath, outputDir, themeId) {
540
541
  logger.info(`Generated theme-data.json (${Object.keys(pages).length} pages)`);
541
542
  }
542
543
  async function contentHashEntry(outputDir) {
543
- const entryPath = path8.join(outputDir, "bundle-entry.js");
544
- const mapPath = path8.join(outputDir, "bundle-entry.js.map");
544
+ const entryPath = path10.join(outputDir, "bundle-entry.js");
545
+ const mapPath = path10.join(outputDir, "bundle-entry.js.map");
545
546
  const oldFiles = await glob("bundle-entry-*.js*", { cwd: outputDir });
546
547
  for (const f of oldFiles) {
547
- await fs7.unlink(path8.join(outputDir, f));
548
+ await fs9.unlink(path10.join(outputDir, f));
548
549
  }
549
550
  let entryContent;
550
551
  try {
551
- entryContent = await fs7.readFile(entryPath, "utf-8");
552
+ entryContent = await fs9.readFile(entryPath, "utf-8");
552
553
  } catch {
553
- const indexPath = path8.join(outputDir, "index.js");
554
+ const indexPath = path10.join(outputDir, "index.js");
554
555
  try {
555
- entryContent = await fs7.readFile(indexPath, "utf-8");
556
+ entryContent = await fs9.readFile(indexPath, "utf-8");
556
557
  } catch {
557
558
  logger.warning("No entry file found in output, skipping content hash");
558
559
  return;
559
560
  }
560
- const hash2 = crypto2.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
561
+ const hash2 = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
561
562
  const hashedName2 = `bundle-entry-${hash2}.js`;
562
- const indexMapPath = path8.join(outputDir, "index.js.map");
563
+ const indexMapPath = path10.join(outputDir, "index.js.map");
563
564
  const hashedMapName2 = `bundle-entry-${hash2}.js.map`;
564
565
  entryContent = entryContent.replace(
565
566
  /\/\/# sourceMappingURL=index\.js\.map/,
566
567
  `//# sourceMappingURL=${hashedMapName2}`
567
568
  );
568
- await fs7.writeFile(path8.join(outputDir, hashedName2), entryContent);
569
- await fs7.unlink(indexPath);
569
+ await fs9.writeFile(path10.join(outputDir, hashedName2), entryContent);
570
+ await fs9.unlink(indexPath);
570
571
  try {
571
- await fs7.access(indexMapPath);
572
- await fs7.rename(indexMapPath, path8.join(outputDir, hashedMapName2));
572
+ await fs9.access(indexMapPath);
573
+ await fs9.rename(indexMapPath, path10.join(outputDir, hashedMapName2));
573
574
  } catch {
574
575
  }
575
576
  logger.info(`Entry hashed: ${hashedName2}`);
576
577
  return;
577
578
  }
578
- const hash = crypto2.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
579
+ const hash = crypto.createHash("sha256").update(entryContent).digest("hex").slice(0, 8);
579
580
  const hashedName = `bundle-entry-${hash}.js`;
580
581
  const hashedMapName = `bundle-entry-${hash}.js.map`;
581
582
  entryContent = entryContent.replace(
582
583
  /\/\/# sourceMappingURL=bundle-entry\.js\.map/,
583
584
  `//# sourceMappingURL=${hashedMapName}`
584
585
  );
585
- await fs7.writeFile(path8.join(outputDir, hashedName), entryContent);
586
- await fs7.unlink(entryPath);
586
+ await fs9.writeFile(path10.join(outputDir, hashedName), entryContent);
587
+ await fs9.unlink(entryPath);
587
588
  try {
588
- await fs7.access(mapPath);
589
- await fs7.rename(mapPath, path8.join(outputDir, hashedMapName));
589
+ await fs9.access(mapPath);
590
+ await fs9.rename(mapPath, path10.join(outputDir, hashedMapName));
590
591
  } catch {
591
592
  }
592
593
  logger.info(`Entry hashed: ${hashedName}`);
@@ -598,7 +599,7 @@ async function extractDataRequirements(themePath) {
598
599
  const requirements = {};
599
600
  for (const file of schemaFiles) {
600
601
  try {
601
- const mod = await jiti.import(path8.join(themePath, file));
602
+ const mod = await jiti.import(path10.join(themePath, file));
602
603
  const exports$1 = mod;
603
604
  for (const value of Object.values(exports$1)) {
604
605
  if (value && typeof value === "object" && typeof value.type === "string" && value.dataRequirements && typeof value.dataRequirements === "object") {
@@ -617,8 +618,8 @@ async function generateManifest(themeName, themePath, outputDir) {
617
618
  let version2 = "1.0.0";
618
619
  let themeId = themeName;
619
620
  try {
620
- const pkgContent = await fs7.readFile(
621
- path8.join(themePath, "package.json"),
621
+ const pkgContent = await fs9.readFile(
622
+ path10.join(themePath, "package.json"),
622
623
  "utf-8"
623
624
  );
624
625
  const pkg = JSON.parse(pkgContent);
@@ -636,7 +637,7 @@ async function generateManifest(themeName, themePath, outputDir) {
636
637
  const dataRequirements = await extractDataRequirements(themePath);
637
638
  let hasThemeConfig = false;
638
639
  try {
639
- await fs7.access(path8.join(themePath, "theme.config.ts"));
640
+ await fs9.access(path10.join(themePath, "theme.config.ts"));
640
641
  hasThemeConfig = true;
641
642
  } catch {
642
643
  }
@@ -677,24 +678,24 @@ async function generateManifest(themeName, themePath, outputDir) {
677
678
  // Section data requirements for server-side prefetching (keyed by section type)
678
679
  dataRequirements
679
680
  };
680
- await fs7.writeFile(
681
- path8.join(outputDir, "manifest.json"),
681
+ await fs9.writeFile(
682
+ path10.join(outputDir, "manifest.json"),
682
683
  JSON.stringify(manifest, null, 2)
683
684
  );
684
685
  }
685
686
  async function compileStandaloneTheme(themePath, themeName) {
686
- const outputDir = path8.join(themePath, "dist");
687
- const bundleEntry = path8.join(themePath, "bundle-entry.ts");
688
- const indexEntry = path8.join(themePath, "index.ts");
687
+ const outputDir = path10.join(themePath, "dist");
688
+ const bundleEntry = path10.join(themePath, "bundle-entry.ts");
689
+ const indexEntry = path10.join(themePath, "index.ts");
689
690
  let entryPoint = indexEntry;
690
691
  try {
691
- await fs7.access(bundleEntry);
692
+ await fs9.access(bundleEntry);
692
693
  entryPoint = bundleEntry;
693
694
  } catch {
694
695
  }
695
- const shimPath = path8.join(outputDir, ".process-shim.js");
696
- await fs7.mkdir(outputDir, { recursive: true });
697
- await fs7.writeFile(shimPath, PROCESS_SHIM);
696
+ const shimPath = path10.join(outputDir, ".process-shim.js");
697
+ await fs9.mkdir(outputDir, { recursive: true });
698
+ await fs9.writeFile(shimPath, PROCESS_SHIM);
698
699
  const buildOptions = {
699
700
  entryPoints: [entryPoint],
700
701
  bundle: true,
@@ -744,7 +745,7 @@ async function compileStandaloneTheme(themePath, themeName) {
744
745
  try {
745
746
  const result = await esbuild.build(buildOptions);
746
747
  try {
747
- await fs7.unlink(shimPath);
748
+ await fs9.unlink(shimPath);
748
749
  } catch {
749
750
  }
750
751
  await contentHashEntry(outputDir);
@@ -763,7 +764,7 @@ async function compileStandaloneTheme(themePath, themeName) {
763
764
  return true;
764
765
  } catch (error) {
765
766
  try {
766
- await fs7.unlink(shimPath);
767
+ await fs9.unlink(shimPath);
767
768
  } catch {
768
769
  }
769
770
  logger.error(`esbuild compilation failed: ${error}`);
@@ -771,18 +772,18 @@ async function compileStandaloneTheme(themePath, themeName) {
771
772
  }
772
773
  }
773
774
  async function compileStandaloneThemeDev(themePath, themeName) {
774
- const outputDir = path8.join(themePath, "dist");
775
- const bundleEntry = path8.join(themePath, "bundle-entry.ts");
776
- const indexEntry = path8.join(themePath, "index.ts");
775
+ const outputDir = path10.join(themePath, "dist");
776
+ const bundleEntry = path10.join(themePath, "bundle-entry.ts");
777
+ const indexEntry = path10.join(themePath, "index.ts");
777
778
  let entryPoint = indexEntry;
778
779
  try {
779
- await fs7.access(bundleEntry);
780
+ await fs9.access(bundleEntry);
780
781
  entryPoint = bundleEntry;
781
782
  } catch {
782
783
  }
783
- const shimPath = path8.join(outputDir, ".process-shim.js");
784
- await fs7.mkdir(outputDir, { recursive: true });
785
- await fs7.writeFile(shimPath, PROCESS_SHIM);
784
+ const shimPath = path10.join(outputDir, ".process-shim.js");
785
+ await fs9.mkdir(outputDir, { recursive: true });
786
+ await fs9.writeFile(shimPath, PROCESS_SHIM);
786
787
  const buildOptions = {
787
788
  entryPoints: [entryPoint],
788
789
  bundle: true,
@@ -835,18 +836,18 @@ async function compileStandaloneThemeDev(themePath, themeName) {
835
836
  return { context: context2, outputDir };
836
837
  }
837
838
  async function compilePreviewRuntime(themePath) {
838
- const outputDir = path8.join(themePath, "dist");
839
- await fs7.mkdir(outputDir, { recursive: true });
840
- const outputPath = path8.join(outputDir, "preview-runtime.js");
839
+ const outputDir = path10.join(themePath, "dist");
840
+ await fs9.mkdir(outputDir, { recursive: true });
841
+ const outputPath = path10.join(outputDir, "preview-runtime.js");
841
842
  const locations = [
842
- path8.join(__dirname, "..", "preview", "preview-app.tsx"),
843
- path8.join(__dirname, "preview", "preview-app.tsx"),
844
- path8.join(__dirname, "..", "..", "src", "preview", "preview-app.tsx")
843
+ path10.join(__dirname, "..", "preview", "preview-app.tsx"),
844
+ path10.join(__dirname, "preview", "preview-app.tsx"),
845
+ path10.join(__dirname, "..", "..", "src", "preview", "preview-app.tsx")
845
846
  ];
846
847
  let previewEntryPath = null;
847
848
  for (const loc of locations) {
848
849
  try {
849
- await fs7.access(loc);
850
+ await fs9.access(loc);
850
851
  previewEntryPath = loc;
851
852
  break;
852
853
  } catch {
@@ -929,10 +930,10 @@ ${locations.join("\n")}`
929
930
  if (!lucideScanned) {
930
931
  lucideScanned = true;
931
932
  const coreSrcCandidates = [
932
- path8.join(themePath, "node_modules", "@onexapis", "core", "src"),
933
- path8.join(themePath, "..", "..", "packages", "core", "src"),
933
+ path10.join(themePath, "node_modules", "@onexapis", "core", "src"),
934
+ path10.join(themePath, "..", "..", "packages", "core", "src"),
934
935
  // monorepo sibling
935
- path8.join(
936
+ path10.join(
936
937
  __dirname,
937
938
  "..",
938
939
  "..",
@@ -947,7 +948,7 @@ ${locations.join("\n")}`
947
948
  let coreSourceDir = null;
948
949
  for (const candidate of coreSrcCandidates) {
949
950
  try {
950
- await fs7.access(candidate);
951
+ await fs9.access(candidate);
951
952
  coreSourceDir = candidate;
952
953
  break;
953
954
  } catch {
@@ -966,21 +967,21 @@ ${locations.join("\n")}`
966
967
  }
967
968
  } else {
968
969
  const coreDistCandidates = [
969
- path8.join(themePath, "node_modules", "@onexapis", "core", "dist")
970
+ path10.join(themePath, "node_modules", "@onexapis", "core", "dist")
970
971
  ];
971
972
  const resolvedDist = await resolveNodeModulesFile(
972
973
  __dirname,
973
- path8.join("@onexapis", "core", "dist")
974
+ path10.join("@onexapis", "core", "dist")
974
975
  );
975
976
  if (resolvedDist) coreDistCandidates.push(resolvedDist);
976
977
  for (const candidate of coreDistCandidates) {
977
978
  try {
978
- await fs7.access(candidate);
979
+ await fs9.access(candidate);
979
980
  const mjsFiles = await glob("*.mjs", { cwd: candidate });
980
981
  const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
981
982
  for (const file of mjsFiles) {
982
- const content = await fs7.readFile(
983
- path8.join(candidate, file),
983
+ const content = await fs9.readFile(
984
+ path10.join(candidate, file),
984
985
  "utf-8"
985
986
  );
986
987
  for (const match of content.matchAll(importRegex)) {
@@ -1035,7 +1036,7 @@ export default new Proxy({}, { get: (_, name) => name === '__esModule' ? true :
1035
1036
  const req = createRequire(import.meta.url || __filename);
1036
1037
  const cjsPath = req.resolve("framer-motion");
1037
1038
  const pkgDir = cjsPath.replace(/[/\\]dist[/\\].*$/, "");
1038
- const esmEntry = path8.join(pkgDir, "dist", "es", "index.mjs");
1039
+ const esmEntry = path10.join(pkgDir, "dist", "es", "index.mjs");
1039
1040
  const { existsSync } = await import('fs');
1040
1041
  if (existsSync(esmEntry)) {
1041
1042
  return { path: esmEntry, namespace: "file" };
@@ -1134,8 +1135,8 @@ export function headers() { return new Headers(); }
1134
1135
  });
1135
1136
  }
1136
1137
  };
1137
- const shimPath = path8.join(outputDir, ".process-shim-preview.js");
1138
- await fs7.writeFile(shimPath, PROCESS_SHIM);
1138
+ const shimPath = path10.join(outputDir, ".process-shim-preview.js");
1139
+ await fs9.writeFile(shimPath, PROCESS_SHIM);
1139
1140
  await esbuild.build({
1140
1141
  entryPoints: [previewEntryPath],
1141
1142
  bundle: true,
@@ -1170,7 +1171,7 @@ export function headers() { return new Headers(); }
1170
1171
  }
1171
1172
  });
1172
1173
  try {
1173
- await fs7.unlink(shimPath);
1174
+ await fs9.unlink(shimPath);
1174
1175
  } catch {
1175
1176
  }
1176
1177
  return outputPath;
@@ -1303,18 +1304,18 @@ async function renderTemplate(templatePath, data) {
1303
1304
  return ejs.render(template, data);
1304
1305
  }
1305
1306
  async function writeFile(filePath, content) {
1306
- await fs.ensureDir(path8.dirname(filePath));
1307
+ await fs.ensureDir(path10.dirname(filePath));
1307
1308
  await fs.writeFile(filePath, content, "utf-8");
1308
1309
  }
1309
1310
  function getTemplatesDir() {
1310
1311
  const locations = [
1311
- path8.join(__dirname, "../../templates"),
1312
+ path10.join(__dirname, "../../templates"),
1312
1313
  // Development
1313
- path8.join(__dirname, "../templates"),
1314
+ path10.join(__dirname, "../templates"),
1314
1315
  // Production (dist/)
1315
- path8.join(process.cwd(), "templates"),
1316
+ path10.join(process.cwd(), "templates"),
1316
1317
  // Fallback
1317
- path8.join(process.cwd(), "packages/cli/templates")
1318
+ path10.join(process.cwd(), "packages/cli/templates")
1318
1319
  // Monorepo
1319
1320
  ];
1320
1321
  for (const location of locations) {
@@ -1326,7 +1327,7 @@ function getTemplatesDir() {
1326
1327
  }
1327
1328
  async function copyTemplate(templateName, targetDir, data) {
1328
1329
  const templatesDir = getTemplatesDir();
1329
- const templateDir = path8.join(templatesDir, templateName);
1330
+ const templateDir = path10.join(templatesDir, templateName);
1330
1331
  if (!fs.existsSync(templateDir)) {
1331
1332
  throw new Error(
1332
1333
  `Template "${templateName}" not found at ${templateDir}. Available templates: ${fs.readdirSync(templatesDir).join(", ")}`
@@ -1335,8 +1336,8 @@ async function copyTemplate(templateName, targetDir, data) {
1335
1336
  await fs.ensureDir(targetDir);
1336
1337
  const files = await fs.readdir(templateDir);
1337
1338
  for (const file of files) {
1338
- const templatePath = path8.join(templateDir, file);
1339
- const targetPath = path8.join(targetDir, file);
1339
+ const templatePath = path10.join(templateDir, file);
1340
+ const targetPath = path10.join(targetDir, file);
1340
1341
  const stat = await fs.stat(templatePath);
1341
1342
  if (stat.isDirectory()) {
1342
1343
  await copyTemplateDir(templatePath, targetPath, data);
@@ -1353,8 +1354,8 @@ async function copyTemplateDir(templateDir, targetDir, data) {
1353
1354
  await fs.ensureDir(targetDir);
1354
1355
  const files = await fs.readdir(templateDir);
1355
1356
  for (const file of files) {
1356
- const templatePath = path8.join(templateDir, file);
1357
- const targetPath = path8.join(targetDir, file);
1357
+ const templatePath = path10.join(templateDir, file);
1358
+ const targetPath = path10.join(targetDir, file);
1358
1359
  const stat = await fs.stat(templatePath);
1359
1360
  if (stat.isDirectory()) {
1360
1361
  await copyTemplateDir(templatePath, targetPath, data);
@@ -1369,32 +1370,32 @@ async function copyTemplateDir(templateDir, targetDir, data) {
1369
1370
  }
1370
1371
  function getProjectRoot() {
1371
1372
  let currentDir = process.cwd();
1372
- while (currentDir !== path8.parse(currentDir).root) {
1373
- const packageJsonPath = path8.join(currentDir, "package.json");
1373
+ while (currentDir !== path10.parse(currentDir).root) {
1374
+ const packageJsonPath = path10.join(currentDir, "package.json");
1374
1375
  if (fs.existsSync(packageJsonPath)) {
1375
1376
  const packageJson = fs.readJsonSync(packageJsonPath);
1376
- if (packageJson.workspaces || fs.existsSync(path8.join(currentDir, "src/themes")) || fs.existsSync(path8.join(currentDir, "themes"))) {
1377
+ if (packageJson.workspaces || fs.existsSync(path10.join(currentDir, "src/themes")) || fs.existsSync(path10.join(currentDir, "themes"))) {
1377
1378
  return currentDir;
1378
1379
  }
1379
1380
  }
1380
- currentDir = path8.dirname(currentDir);
1381
+ currentDir = path10.dirname(currentDir);
1381
1382
  }
1382
1383
  return process.cwd();
1383
1384
  }
1384
1385
  function getThemesDir() {
1385
1386
  const root = getProjectRoot();
1386
- if (fs.existsSync(path8.join(root, "themes")))
1387
- return path8.join(root, "themes");
1388
- if (fs.existsSync(path8.join(root, "src/themes")))
1389
- return path8.join(root, "src/themes");
1390
- return path8.dirname(root);
1387
+ if (fs.existsSync(path10.join(root, "themes")))
1388
+ return path10.join(root, "themes");
1389
+ if (fs.existsSync(path10.join(root, "src/themes")))
1390
+ return path10.join(root, "src/themes");
1391
+ return path10.dirname(root);
1391
1392
  }
1392
1393
  function getFeaturesDir() {
1393
- return path8.join(getProjectRoot(), "src/features");
1394
+ return path10.join(getProjectRoot(), "src/features");
1394
1395
  }
1395
1396
  function isOneXProject() {
1396
1397
  const root = getProjectRoot();
1397
- return fs.existsSync(path8.join(root, "themes")) || fs.existsSync(path8.join(root, "src/themes")) || fs.existsSync(path8.join(root, "theme.config.ts")) || fs.existsSync(path8.join(root, "bundle-entry.ts"));
1398
+ return fs.existsSync(path10.join(root, "themes")) || fs.existsSync(path10.join(root, "src/themes")) || fs.existsSync(path10.join(root, "theme.config.ts")) || fs.existsSync(path10.join(root, "bundle-entry.ts"));
1398
1399
  }
1399
1400
  function ensureOneXProject() {
1400
1401
  if (!isOneXProject()) {
@@ -1410,13 +1411,13 @@ function listThemes() {
1410
1411
  return [];
1411
1412
  }
1412
1413
  return fs.readdirSync(themesDir).filter((name) => {
1413
- const themePath = path8.join(themesDir, name);
1414
- return fs.statSync(themePath).isDirectory() && (fs.existsSync(path8.join(themePath, "theme.config.ts")) || fs.existsSync(path8.join(themePath, "bundle-entry.ts")) || fs.existsSync(path8.join(themePath, "manifest.ts")));
1414
+ const themePath = path10.join(themesDir, name);
1415
+ return fs.statSync(themePath).isDirectory() && (fs.existsSync(path10.join(themePath, "theme.config.ts")) || fs.existsSync(path10.join(themePath, "bundle-entry.ts")) || fs.existsSync(path10.join(themePath, "manifest.ts")));
1415
1416
  });
1416
1417
  }
1417
1418
  function themeExists(themeName) {
1418
- const themePath = path8.join(getThemesDir(), themeName);
1419
- return fs.existsSync(themePath) && (fs.existsSync(path8.join(themePath, "theme.config.ts")) || fs.existsSync(path8.join(themePath, "bundle-entry.ts")) || fs.existsSync(path8.join(themePath, "manifest.ts")));
1419
+ const themePath = path10.join(getThemesDir(), themeName);
1420
+ return fs.existsSync(themePath) && (fs.existsSync(path10.join(themePath, "theme.config.ts")) || fs.existsSync(path10.join(themePath, "bundle-entry.ts")) || fs.existsSync(path10.join(themePath, "manifest.ts")));
1420
1421
  }
1421
1422
  function detectPackageManager() {
1422
1423
  const userAgent = process.env.npm_config_user_agent || "";
@@ -1424,24 +1425,22 @@ function detectPackageManager() {
1424
1425
  if (userAgent.includes("yarn")) return "yarn";
1425
1426
  if (userAgent.includes("bun")) return "bun";
1426
1427
  const cwd = process.cwd();
1427
- if (fs.existsSync(path8.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
1428
- if (fs.existsSync(path8.join(cwd, "yarn.lock"))) return "yarn";
1429
- if (fs.existsSync(path8.join(cwd, "bun.lockb"))) return "bun";
1428
+ if (fs.existsSync(path10.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
1429
+ if (fs.existsSync(path10.join(cwd, "yarn.lock"))) return "yarn";
1430
+ if (fs.existsSync(path10.join(cwd, "bun.lockb"))) return "bun";
1430
1431
  return "npm";
1431
1432
  }
1432
1433
  async function installDependencies(projectPath, packageManager = "npm") {
1433
- return new Promise((resolve, reject) => {
1434
- try {
1435
- const installCmd = packageManager === "yarn" ? "yarn" : `${packageManager} install`;
1436
- execSync(installCmd, {
1437
- cwd: projectPath,
1438
- stdio: "inherit"
1439
- });
1440
- resolve();
1441
- } catch (error) {
1442
- reject(error);
1443
- }
1434
+ const args = packageManager === "yarn" ? [] : ["install"];
1435
+ const result = spawn2.sync(packageManager, args, {
1436
+ cwd: projectPath,
1437
+ stdio: "inherit"
1444
1438
  });
1439
+ if (result.status !== 0) {
1440
+ throw new Error(
1441
+ `${packageManager} install failed with exit code ${result.status}`
1442
+ );
1443
+ }
1445
1444
  }
1446
1445
 
1447
1446
  // src/commands/init.ts
@@ -1475,6 +1474,184 @@ function getValidCategories() {
1475
1474
  "contact"
1476
1475
  ];
1477
1476
  }
1477
+ var AUTH_DIR = path10.join(os.homedir(), ".onexthm");
1478
+ var AUTH_FILE = path10.join(AUTH_DIR, "auth.json");
1479
+ function getApiUrl() {
1480
+ return process.env.ONEXTHM_API_URL || process.env.NEXT_PUBLIC_API_URL || "https://platform-dev.onexeos.com";
1481
+ }
1482
+ async function saveAuthTokens(tokens) {
1483
+ await fs.ensureDir(AUTH_DIR);
1484
+ const key = getMachineKey();
1485
+ const data = JSON.stringify(tokens);
1486
+ const encrypted = encrypt(data, key);
1487
+ await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
1488
+ }
1489
+ function loadAuthTokens() {
1490
+ try {
1491
+ if (!fs.existsSync(AUTH_FILE)) return null;
1492
+ const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
1493
+ const key = getMachineKey();
1494
+ const data = decrypt(encrypted, key);
1495
+ return JSON.parse(data);
1496
+ } catch {
1497
+ return null;
1498
+ }
1499
+ }
1500
+ async function clearAuthTokens() {
1501
+ try {
1502
+ await fs.remove(AUTH_FILE);
1503
+ } catch {
1504
+ }
1505
+ }
1506
+ function isTokenExpired(tokens) {
1507
+ return Date.now() / 1e3 > tokens.expiresAt - 60;
1508
+ }
1509
+ async function getValidTokens() {
1510
+ const tokens = loadAuthTokens();
1511
+ if (!tokens) return null;
1512
+ if (!isTokenExpired(tokens)) return tokens;
1513
+ try {
1514
+ const apiUrl = getApiUrl();
1515
+ const response = await fetch(`${apiUrl}/auth/refresh`, {
1516
+ method: "POST",
1517
+ headers: { "Content-Type": "application/json" },
1518
+ body: JSON.stringify({ refresh_token: tokens.refreshToken })
1519
+ });
1520
+ if (!response.ok) {
1521
+ await clearAuthTokens();
1522
+ return null;
1523
+ }
1524
+ const data = await response.json();
1525
+ const body = data.statusCode ? data.body : data;
1526
+ const refreshed = {
1527
+ ...tokens,
1528
+ accessToken: body.AccessToken || tokens.accessToken,
1529
+ idToken: body.IdToken || tokens.idToken,
1530
+ expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
1531
+ };
1532
+ await saveAuthTokens(refreshed);
1533
+ return refreshed;
1534
+ } catch {
1535
+ await clearAuthTokens();
1536
+ return null;
1537
+ }
1538
+ }
1539
+ async function authenticatedFetch(url, init) {
1540
+ const tokens = await getValidTokens();
1541
+ if (!tokens) {
1542
+ throw new Error("Not logged in. Run: onexthm login");
1543
+ }
1544
+ const headers = new Headers(init?.headers);
1545
+ headers.set("Authorization", `Bearer ${tokens.idToken}`);
1546
+ headers.set("Content-Type", "application/json");
1547
+ return fetch(url, { ...init, headers });
1548
+ }
1549
+ function getMachineKey() {
1550
+ let seed;
1551
+ if (process.platform === "darwin") {
1552
+ seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
1553
+ } else if (process.platform === "linux") {
1554
+ try {
1555
+ seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
1556
+ } catch {
1557
+ seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
1558
+ }
1559
+ } else {
1560
+ seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
1561
+ }
1562
+ return crypto.createHash("sha256").update(seed).digest();
1563
+ }
1564
+ function encrypt(text, key) {
1565
+ const iv = crypto.randomBytes(16);
1566
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
1567
+ let encrypted = cipher.update(text, "utf-8", "hex");
1568
+ encrypted += cipher.final("hex");
1569
+ const tag = cipher.getAuthTag();
1570
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
1571
+ }
1572
+ function decrypt(text, key) {
1573
+ const [ivHex, tagHex, encrypted] = text.split(":");
1574
+ const iv = Buffer.from(ivHex, "hex");
1575
+ const tag = Buffer.from(tagHex, "hex");
1576
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
1577
+ decipher.setAuthTag(tag);
1578
+ let decrypted = decipher.update(encrypted, "hex", "utf-8");
1579
+ decrypted += decipher.final("utf-8");
1580
+ return decrypted;
1581
+ }
1582
+ function parseJwtClaims(idToken) {
1583
+ try {
1584
+ const payload = idToken.split(".")[1];
1585
+ const decoded = Buffer.from(payload, "base64url").toString("utf-8");
1586
+ return JSON.parse(decoded);
1587
+ } catch {
1588
+ return {};
1589
+ }
1590
+ }
1591
+ function getNpxCommand() {
1592
+ return process.platform === "win32" ? "npx.cmd" : "npx";
1593
+ }
1594
+ function getDefaultMcpConfig() {
1595
+ return {
1596
+ mcpServers: {
1597
+ onexthm: {
1598
+ command: getNpxCommand(),
1599
+ args: ["-y", "@onexapis/theme-mcp"]
1600
+ }
1601
+ }
1602
+ };
1603
+ }
1604
+ function ensureMcpJson(projectPath, figmaApiKey) {
1605
+ const mcpJsonPath = path10.join(projectPath, ".mcp.json");
1606
+ const npx = getNpxCommand();
1607
+ let mcpConfig;
1608
+ if (fs4.existsSync(mcpJsonPath)) {
1609
+ try {
1610
+ mcpConfig = JSON.parse(fs4.readFileSync(mcpJsonPath, "utf-8"));
1611
+ if (!mcpConfig.mcpServers) {
1612
+ mcpConfig.mcpServers = {};
1613
+ }
1614
+ } catch {
1615
+ mcpConfig = getDefaultMcpConfig();
1616
+ }
1617
+ } else {
1618
+ mcpConfig = getDefaultMcpConfig();
1619
+ }
1620
+ mcpConfig.mcpServers.onexthm = {
1621
+ command: npx,
1622
+ args: ["-y", "@onexapis/theme-mcp"]
1623
+ };
1624
+ if (figmaApiKey) {
1625
+ mcpConfig.mcpServers.figma = {
1626
+ command: npx,
1627
+ args: [
1628
+ "-y",
1629
+ "figma-developer-mcp",
1630
+ `--figma-api-key=${figmaApiKey}`,
1631
+ "--stdio"
1632
+ ]
1633
+ };
1634
+ } else {
1635
+ delete mcpConfig.mcpServers.figma;
1636
+ }
1637
+ fs4.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + "\n");
1638
+ ensureThemeMcpDependency(projectPath);
1639
+ }
1640
+ function ensureThemeMcpDependency(projectPath) {
1641
+ const pkgJsonPath = path10.join(projectPath, "package.json");
1642
+ if (!fs4.existsSync(pkgJsonPath)) return;
1643
+ try {
1644
+ const pkg = JSON.parse(fs4.readFileSync(pkgJsonPath, "utf-8"));
1645
+ if (!pkg.devDependencies) {
1646
+ pkg.devDependencies = {};
1647
+ }
1648
+ if (!pkg.devDependencies["@onexapis/theme-mcp"]) {
1649
+ pkg.devDependencies["@onexapis/theme-mcp"] = "^0.1.0";
1650
+ fs4.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
1651
+ }
1652
+ } catch {
1653
+ }
1654
+ }
1478
1655
 
1479
1656
  // src/commands/init.ts
1480
1657
  async function initCommand(projectName, options = {}) {
@@ -1492,7 +1669,7 @@ async function initCommand(projectName, options = {}) {
1492
1669
  if (!validateThemeName(kebabName)) {
1493
1670
  return "Invalid project name. Use lowercase letters, numbers, and hyphens only.";
1494
1671
  }
1495
- if (fs2.existsSync(path8.join(process.cwd(), kebabName))) {
1672
+ if (fs4.existsSync(path10.join(process.cwd(), kebabName))) {
1496
1673
  return `Directory "${kebabName}" already exists`;
1497
1674
  }
1498
1675
  return true;
@@ -1503,11 +1680,46 @@ async function initCommand(projectName, options = {}) {
1503
1680
  } else {
1504
1681
  name = toKebabCase(projectName);
1505
1682
  }
1506
- const projectPath = path8.join(process.cwd(), name);
1507
- if (fs2.existsSync(projectPath)) {
1683
+ const projectPath = path10.join(process.cwd(), name);
1684
+ if (fs4.existsSync(projectPath)) {
1508
1685
  logger.error(`Directory "${name}" already exists.`);
1509
1686
  process.exit(1);
1510
1687
  }
1688
+ if (!options.yes) {
1689
+ try {
1690
+ const apiUrl = getApiUrl();
1691
+ const controller = new AbortController();
1692
+ const timeout = setTimeout(() => controller.abort(), 3e3);
1693
+ const response = await fetch(
1694
+ `${apiUrl}/website-api/themes/${encodeURIComponent(name)}/exists`,
1695
+ { signal: controller.signal }
1696
+ );
1697
+ clearTimeout(timeout);
1698
+ if (response.ok) {
1699
+ const data2 = await response.json();
1700
+ const body = data2.statusCode ? data2.body : data2;
1701
+ if (body.exists && body.owner === "other") {
1702
+ logger.warning(
1703
+ `Theme ID "${name}" is already registered by another developer.
1704
+ You can still create this locally, but publishing will fail.
1705
+ Consider using a different name.`
1706
+ );
1707
+ const { proceed } = await inquirer.prompt([
1708
+ {
1709
+ type: "confirm",
1710
+ name: "proceed",
1711
+ message: "Continue anyway?",
1712
+ default: false
1713
+ }
1714
+ ]);
1715
+ if (!proceed) {
1716
+ process.exit(0);
1717
+ }
1718
+ }
1719
+ }
1720
+ } catch {
1721
+ }
1722
+ }
1511
1723
  let displayName;
1512
1724
  let description;
1513
1725
  let author;
@@ -1575,7 +1787,7 @@ async function initCommand(projectName, options = {}) {
1575
1787
  }
1576
1788
  logger.startSpinner("Creating project structure...");
1577
1789
  try {
1578
- fs2.mkdirSync(projectPath, { recursive: true });
1790
+ fs4.mkdirSync(projectPath, { recursive: true });
1579
1791
  await copyTemplate(template, projectPath, data);
1580
1792
  await renameThemeInFiles(
1581
1793
  projectPath,
@@ -1584,28 +1796,14 @@ async function initCommand(projectName, options = {}) {
1584
1796
  description,
1585
1797
  author
1586
1798
  );
1587
- const mcpJsonPath = path8.join(projectPath, ".mcp.json");
1588
- if (fs2.existsSync(mcpJsonPath)) {
1589
- let mcpContent = fs2.readFileSync(mcpJsonPath, "utf-8");
1590
- if (figmaApiKey) {
1591
- mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
1592
- } else {
1593
- try {
1594
- const mcpJson = JSON.parse(mcpContent);
1595
- delete mcpJson.mcpServers.figma;
1596
- mcpContent = JSON.stringify(mcpJson, null, 2) + "\n";
1597
- } catch {
1598
- }
1599
- }
1600
- fs2.writeFileSync(mcpJsonPath, mcpContent, "utf-8");
1601
- }
1799
+ ensureMcpJson(projectPath, figmaApiKey || void 0);
1602
1800
  logger.stopSpinner(true, "Project structure created!");
1603
1801
  if (options.git) {
1604
1802
  logger.startSpinner("Initializing git repository...");
1605
1803
  try {
1606
- execSync("git init", { cwd: projectPath, stdio: "ignore" });
1607
- execSync("git add .", { cwd: projectPath, stdio: "ignore" });
1608
- execSync('git commit -m "Initial commit from onexthm init"', {
1804
+ spawn2.sync("git", ["init"], { cwd: projectPath, stdio: "ignore" });
1805
+ spawn2.sync("git", ["add", "."], { cwd: projectPath, stdio: "ignore" });
1806
+ spawn2.sync("git", ["commit", "-m", "Initial commit from onexthm init"], {
1609
1807
  cwd: projectPath,
1610
1808
  stdio: "ignore"
1611
1809
  });
@@ -1657,16 +1855,16 @@ async function initCommand(projectName, options = {}) {
1657
1855
  logger.error(
1658
1856
  error instanceof Error ? error.message : "Unknown error occurred"
1659
1857
  );
1660
- if (fs2.existsSync(projectPath)) {
1661
- fs2.rmSync(projectPath, { recursive: true, force: true });
1858
+ if (fs4.existsSync(projectPath)) {
1859
+ fs4.rmSync(projectPath, { recursive: true, force: true });
1662
1860
  }
1663
1861
  process.exit(1);
1664
1862
  }
1665
1863
  }
1666
1864
  async function renameThemeInFiles(projectPath, themeName, displayName, description, author) {
1667
- const configPath = path8.join(projectPath, "theme.config.ts");
1668
- if (fs2.existsSync(configPath)) {
1669
- let content = fs2.readFileSync(configPath, "utf-8");
1865
+ const configPath = path10.join(projectPath, "theme.config.ts");
1866
+ if (fs4.existsSync(configPath)) {
1867
+ let content = fs4.readFileSync(configPath, "utf-8");
1670
1868
  content = content.replace(
1671
1869
  /name: "My Simple Theme"/,
1672
1870
  `name: "${displayName}"`
@@ -1675,11 +1873,11 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
1675
1873
  /description: ".*?"/,
1676
1874
  `description: "${description}"`
1677
1875
  );
1678
- fs2.writeFileSync(configPath, content, "utf-8");
1876
+ fs4.writeFileSync(configPath, content, "utf-8");
1679
1877
  }
1680
- const pkgPath = path8.join(projectPath, "package.json");
1681
- if (fs2.existsSync(pkgPath)) {
1682
- let content = fs2.readFileSync(pkgPath, "utf-8");
1878
+ const pkgPath = path10.join(projectPath, "package.json");
1879
+ if (fs4.existsSync(pkgPath)) {
1880
+ let content = fs4.readFileSync(pkgPath, "utf-8");
1683
1881
  content = content.replace(
1684
1882
  /@onex-themes\/my-simple/g,
1685
1883
  `@onex-themes/${themeName}`
@@ -1688,7 +1886,7 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
1688
1886
  /"description": ".*?"/,
1689
1887
  `"description": "${description}"`
1690
1888
  );
1691
- fs2.writeFileSync(pkgPath, content, "utf-8");
1889
+ fs4.writeFileSync(pkgPath, content, "utf-8");
1692
1890
  }
1693
1891
  }
1694
1892
 
@@ -1699,10 +1897,10 @@ async function createSectionCommand(name, options) {
1699
1897
  ensureOneXProject();
1700
1898
  if (!options.theme) {
1701
1899
  const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
1702
- (f) => fs.existsSync(path8.join(process.cwd(), f))
1900
+ (f) => fs.existsSync(path10.join(process.cwd(), f))
1703
1901
  );
1704
1902
  if (isStandaloneTheme) {
1705
- options.theme = path8.basename(process.cwd());
1903
+ options.theme = path10.basename(process.cwd());
1706
1904
  }
1707
1905
  }
1708
1906
  const sectionName = toKebabCase(name);
@@ -1765,35 +1963,35 @@ async function createSectionCommand(name, options) {
1765
1963
  };
1766
1964
  logger.startSpinner("Creating section files...");
1767
1965
  try {
1768
- const themePath = path8.join(getThemesDir(), themeName);
1769
- const sectionPath = path8.join(themePath, "sections", sectionName);
1966
+ const themePath = path10.join(getThemesDir(), themeName);
1967
+ const sectionPath = path10.join(themePath, "sections", sectionName);
1770
1968
  const schemaContent = generateSectionSchema(data);
1771
1969
  await writeFile(
1772
- path8.join(sectionPath, `${sectionName}.schema.ts`),
1970
+ path10.join(sectionPath, `${sectionName}.schema.ts`),
1773
1971
  schemaContent
1774
1972
  );
1775
1973
  if (createTemplate) {
1776
1974
  const templateContent = generateSectionTemplate(data);
1777
1975
  await writeFile(
1778
- path8.join(sectionPath, `${sectionName}-default.tsx`),
1976
+ path10.join(sectionPath, `${sectionName}-default.tsx`),
1779
1977
  templateContent
1780
1978
  );
1781
1979
  }
1782
1980
  const indexContent = generateSectionIndex(data, createTemplate);
1783
- await writeFile(path8.join(sectionPath, "index.ts"), indexContent);
1981
+ await writeFile(path10.join(sectionPath, "index.ts"), indexContent);
1784
1982
  logger.stopSpinner(true, "Section files created successfully!");
1785
1983
  logger.newLine();
1786
1984
  logger.section("Next steps:");
1787
1985
  logger.log(
1788
- ` 1. Edit schema: ${path8.relative(process.cwd(), path8.join(sectionPath, `${sectionName}.schema.ts`))}`
1986
+ ` 1. Edit schema: ${path10.relative(process.cwd(), path10.join(sectionPath, `${sectionName}.schema.ts`))}`
1789
1987
  );
1790
1988
  if (createTemplate) {
1791
1989
  logger.log(
1792
- ` 2. Edit template: ${path8.relative(process.cwd(), path8.join(sectionPath, `${sectionName}-default.tsx`))}`
1990
+ ` 2. Edit template: ${path10.relative(process.cwd(), path10.join(sectionPath, `${sectionName}-default.tsx`))}`
1793
1991
  );
1794
1992
  }
1795
1993
  logger.log(
1796
- ` 3. Add to theme manifest: ${path8.relative(process.cwd(), path8.join(themePath, "manifest.ts"))}`
1994
+ ` 3. Add to theme manifest: ${path10.relative(process.cwd(), path10.join(themePath, "manifest.ts"))}`
1797
1995
  );
1798
1996
  logger.newLine();
1799
1997
  logger.success("Section created successfully!");
@@ -1941,10 +2139,10 @@ async function createBlockCommand(name, options) {
1941
2139
  ensureOneXProject();
1942
2140
  if (!options.theme) {
1943
2141
  const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
1944
- (f) => fs.existsSync(path8.join(process.cwd(), f))
2142
+ (f) => fs.existsSync(path10.join(process.cwd(), f))
1945
2143
  );
1946
2144
  if (isStandaloneTheme) {
1947
- options.theme = path8.basename(process.cwd());
2145
+ options.theme = path10.basename(process.cwd());
1948
2146
  }
1949
2147
  }
1950
2148
  const blockName = toKebabCase(name);
@@ -2019,24 +2217,24 @@ async function createBlockCommand(name, options) {
2019
2217
  };
2020
2218
  logger.startSpinner("Creating block files...");
2021
2219
  try {
2022
- const blockPath = scope === "shared" ? path8.join(getFeaturesDir(), "blocks", blockName) : path8.join(getThemesDir(), themeName, "blocks", blockName);
2220
+ const blockPath = scope === "shared" ? path10.join(getFeaturesDir(), "blocks", blockName) : path10.join(getThemesDir(), themeName, "blocks", blockName);
2023
2221
  const schemaContent = generateBlockSchema(data);
2024
2222
  await writeFile(
2025
- path8.join(blockPath, `${blockName}.schema.ts`),
2223
+ path10.join(blockPath, `${blockName}.schema.ts`),
2026
2224
  schemaContent
2027
2225
  );
2028
2226
  const componentContent = generateBlockComponent(data);
2029
- await writeFile(path8.join(blockPath, `${blockName}.tsx`), componentContent);
2227
+ await writeFile(path10.join(blockPath, `${blockName}.tsx`), componentContent);
2030
2228
  const indexContent = generateBlockIndex(data);
2031
- await writeFile(path8.join(blockPath, "index.ts"), indexContent);
2229
+ await writeFile(path10.join(blockPath, "index.ts"), indexContent);
2032
2230
  logger.stopSpinner(true, "Block files created successfully!");
2033
2231
  logger.newLine();
2034
2232
  logger.section("Next steps:");
2035
2233
  logger.log(
2036
- ` 1. Edit schema: ${path8.relative(process.cwd(), path8.join(blockPath, `${blockName}.schema.ts`))}`
2234
+ ` 1. Edit schema: ${path10.relative(process.cwd(), path10.join(blockPath, `${blockName}.schema.ts`))}`
2037
2235
  );
2038
2236
  logger.log(
2039
- ` 2. Edit component: ${path8.relative(process.cwd(), path8.join(blockPath, `${blockName}.tsx`))}`
2237
+ ` 2. Edit component: ${path10.relative(process.cwd(), path10.join(blockPath, `${blockName}.tsx`))}`
2040
2238
  );
2041
2239
  logger.log(
2042
2240
  ` 3. Register in block registry: src/lib/registry/block-registry.ts`
@@ -2214,31 +2412,31 @@ async function createComponentCommand(name, options) {
2214
2412
  };
2215
2413
  logger.startSpinner("Creating component files...");
2216
2414
  try {
2217
- const componentPath = path8.join(
2415
+ const componentPath = path10.join(
2218
2416
  getFeaturesDir(),
2219
2417
  "components",
2220
2418
  componentName
2221
2419
  );
2222
2420
  const schemaContent = generateComponentSchema(data);
2223
2421
  await writeFile(
2224
- path8.join(componentPath, `${componentName}.schema.ts`),
2422
+ path10.join(componentPath, `${componentName}.schema.ts`),
2225
2423
  schemaContent
2226
2424
  );
2227
2425
  const componentContent = generateComponent(data);
2228
2426
  await writeFile(
2229
- path8.join(componentPath, `${componentName}.tsx`),
2427
+ path10.join(componentPath, `${componentName}.tsx`),
2230
2428
  componentContent
2231
2429
  );
2232
2430
  const indexContent = generateComponentIndex(data);
2233
- await writeFile(path8.join(componentPath, "index.ts"), indexContent);
2431
+ await writeFile(path10.join(componentPath, "index.ts"), indexContent);
2234
2432
  logger.stopSpinner(true, "Component files created successfully!");
2235
2433
  logger.newLine();
2236
2434
  logger.section("Next steps:");
2237
2435
  logger.log(
2238
- ` 1. Edit schema: ${path8.relative(process.cwd(), path8.join(componentPath, `${componentName}.schema.ts`))}`
2436
+ ` 1. Edit schema: ${path10.relative(process.cwd(), path10.join(componentPath, `${componentName}.schema.ts`))}`
2239
2437
  );
2240
2438
  logger.log(
2241
- ` 2. Edit component: ${path8.relative(process.cwd(), path8.join(componentPath, `${componentName}.tsx`))}`
2439
+ ` 2. Edit component: ${path10.relative(process.cwd(), path10.join(componentPath, `${componentName}.tsx`))}`
2242
2440
  );
2243
2441
  logger.log(
2244
2442
  ` 3. Register in component registry: src/lib/registry/component-registry.ts`
@@ -2395,13 +2593,13 @@ async function listSections(themeFilter) {
2395
2593
  return;
2396
2594
  }
2397
2595
  for (const theme of themes) {
2398
- const sectionsDir = path8.join(getThemesDir(), theme, "sections");
2596
+ const sectionsDir = path10.join(getThemesDir(), theme, "sections");
2399
2597
  if (!fs.existsSync(sectionsDir)) {
2400
2598
  continue;
2401
2599
  }
2402
2600
  const sections = fs.readdirSync(sectionsDir).filter((name) => {
2403
- const sectionPath = path8.join(sectionsDir, name);
2404
- return fs.statSync(sectionPath).isDirectory() && fs.existsSync(path8.join(sectionPath, "index.ts"));
2601
+ const sectionPath = path10.join(sectionsDir, name);
2602
+ return fs.statSync(sectionPath).isDirectory() && fs.existsSync(path10.join(sectionPath, "index.ts"));
2405
2603
  });
2406
2604
  if (sections.length > 0) {
2407
2605
  logger.log(chalk4.cyan(`
@@ -2415,11 +2613,11 @@ async function listSections(themeFilter) {
2415
2613
  }
2416
2614
  async function listBlocks(themeFilter) {
2417
2615
  logger.section("\u{1F9F1} Blocks");
2418
- const sharedBlocksDir = path8.join(getFeaturesDir(), "blocks");
2616
+ const sharedBlocksDir = path10.join(getFeaturesDir(), "blocks");
2419
2617
  if (fs.existsSync(sharedBlocksDir)) {
2420
2618
  const sharedBlocks = fs.readdirSync(sharedBlocksDir).filter((name) => {
2421
- const blockPath = path8.join(sharedBlocksDir, name);
2422
- return fs.statSync(blockPath).isDirectory() && fs.existsSync(path8.join(blockPath, "index.ts"));
2619
+ const blockPath = path10.join(sharedBlocksDir, name);
2620
+ return fs.statSync(blockPath).isDirectory() && fs.existsSync(path10.join(blockPath, "index.ts"));
2423
2621
  });
2424
2622
  if (sharedBlocks.length > 0) {
2425
2623
  logger.log(chalk4.cyan("\n Shared:"));
@@ -2430,13 +2628,13 @@ async function listBlocks(themeFilter) {
2430
2628
  }
2431
2629
  const themes = themeFilter ? [themeFilter] : listThemes();
2432
2630
  for (const theme of themes) {
2433
- const blocksDir = path8.join(getThemesDir(), theme, "blocks");
2631
+ const blocksDir = path10.join(getThemesDir(), theme, "blocks");
2434
2632
  if (!fs.existsSync(blocksDir)) {
2435
2633
  continue;
2436
2634
  }
2437
2635
  const blocks = fs.readdirSync(blocksDir).filter((name) => {
2438
- const blockPath = path8.join(blocksDir, name);
2439
- return fs.statSync(blockPath).isDirectory() && fs.existsSync(path8.join(blockPath, "index.ts"));
2636
+ const blockPath = path10.join(blocksDir, name);
2637
+ return fs.statSync(blockPath).isDirectory() && fs.existsSync(path10.join(blockPath, "index.ts"));
2440
2638
  });
2441
2639
  if (blocks.length > 0) {
2442
2640
  logger.log(chalk4.cyan(`
@@ -2450,14 +2648,14 @@ async function listBlocks(themeFilter) {
2450
2648
  }
2451
2649
  async function listComponents() {
2452
2650
  logger.section("\u2699\uFE0F Components");
2453
- const componentsDir = path8.join(getFeaturesDir(), "components");
2651
+ const componentsDir = path10.join(getFeaturesDir(), "components");
2454
2652
  if (!fs.existsSync(componentsDir)) {
2455
2653
  logger.warning("No components directory found");
2456
2654
  return;
2457
2655
  }
2458
2656
  const components = fs.readdirSync(componentsDir).filter((name) => {
2459
- const componentPath = path8.join(componentsDir, name);
2460
- return fs.statSync(componentPath).isDirectory() && fs.existsSync(path8.join(componentPath, "index.ts"));
2657
+ const componentPath = path10.join(componentsDir, name);
2658
+ return fs.statSync(componentPath).isDirectory() && fs.existsSync(path10.join(componentPath, "index.ts"));
2461
2659
  });
2462
2660
  if (components.length === 0) {
2463
2661
  logger.warning("No components found");
@@ -2478,11 +2676,11 @@ async function listThemesInfo() {
2478
2676
  }
2479
2677
  logger.log("");
2480
2678
  for (const theme of themes) {
2481
- const themeDir = path8.join(getThemesDir(), theme);
2679
+ const themeDir = path10.join(getThemesDir(), theme);
2482
2680
  const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
2483
2681
  let manifestContent = "";
2484
2682
  for (const candidate of candidates) {
2485
- const candidatePath = path8.join(themeDir, candidate);
2683
+ const candidatePath = path10.join(themeDir, candidate);
2486
2684
  if (fs.existsSync(candidatePath)) {
2487
2685
  manifestContent = fs.readFileSync(candidatePath, "utf-8");
2488
2686
  break;
@@ -2520,9 +2718,9 @@ async function validateCommand(options) {
2520
2718
  "theme.config.ts",
2521
2719
  "bundle-entry.ts",
2522
2720
  "manifest.ts"
2523
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
2721
+ ].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
2524
2722
  if (isThemeDir) {
2525
- themeToValidate = path8.basename(process.cwd());
2723
+ themeToValidate = path10.basename(process.cwd());
2526
2724
  logger.info(`Validating current theme: ${themeToValidate}`);
2527
2725
  } else {
2528
2726
  logger.error(
@@ -2531,11 +2729,11 @@ async function validateCommand(options) {
2531
2729
  process.exit(1);
2532
2730
  }
2533
2731
  }
2534
- const themePath = path8.join(getThemesDir(), themeToValidate);
2732
+ const themePath = path10.join(getThemesDir(), themeToValidate);
2535
2733
  logger.startSpinner("Running validation checks...");
2536
2734
  const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
2537
2735
  const foundEntry = entryFiles.find(
2538
- (f) => fs.existsSync(path8.join(themePath, f))
2736
+ (f) => fs.existsSync(path10.join(themePath, f))
2539
2737
  );
2540
2738
  if (!foundEntry) {
2541
2739
  issues.push({
@@ -2545,7 +2743,7 @@ async function validateCommand(options) {
2545
2743
  });
2546
2744
  } else if (foundEntry === "manifest.ts") {
2547
2745
  const manifestContent = fs.readFileSync(
2548
- path8.join(themePath, foundEntry),
2746
+ path10.join(themePath, foundEntry),
2549
2747
  "utf-8"
2550
2748
  );
2551
2749
  if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
@@ -2556,7 +2754,7 @@ async function validateCommand(options) {
2556
2754
  });
2557
2755
  }
2558
2756
  }
2559
- const configPath = path8.join(themePath, "theme.config.ts");
2757
+ const configPath = path10.join(themePath, "theme.config.ts");
2560
2758
  if (!fs.existsSync(configPath)) {
2561
2759
  issues.push({
2562
2760
  type: "warning",
@@ -2564,7 +2762,7 @@ async function validateCommand(options) {
2564
2762
  message: "Theme config file not found (recommended)"
2565
2763
  });
2566
2764
  }
2567
- const indexPath = path8.join(themePath, "index.ts");
2765
+ const indexPath = path10.join(themePath, "index.ts");
2568
2766
  if (!fs.existsSync(indexPath)) {
2569
2767
  issues.push({
2570
2768
  type: "warning",
@@ -2572,7 +2770,7 @@ async function validateCommand(options) {
2572
2770
  message: "Index file not found (recommended)"
2573
2771
  });
2574
2772
  }
2575
- const sectionsDir = path8.join(themePath, "sections");
2773
+ const sectionsDir = path10.join(themePath, "sections");
2576
2774
  if (!fs.existsSync(sectionsDir)) {
2577
2775
  issues.push({
2578
2776
  type: "warning",
@@ -2581,16 +2779,16 @@ async function validateCommand(options) {
2581
2779
  });
2582
2780
  } else {
2583
2781
  const sections = fs.readdirSync(sectionsDir).filter(
2584
- (name) => fs.statSync(path8.join(sectionsDir, name)).isDirectory()
2782
+ (name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory()
2585
2783
  );
2586
2784
  for (const sectionName of sections) {
2587
- const sectionPath = path8.join(sectionsDir, sectionName);
2588
- const schemaFile = path8.join(sectionPath, `${sectionName}.schema.ts`);
2589
- const defaultTemplate = path8.join(
2785
+ const sectionPath = path10.join(sectionsDir, sectionName);
2786
+ const schemaFile = path10.join(sectionPath, `${sectionName}.schema.ts`);
2787
+ const defaultTemplate = path10.join(
2590
2788
  sectionPath,
2591
2789
  `${sectionName}-default.tsx`
2592
2790
  );
2593
- const indexFile = path8.join(sectionPath, "index.ts");
2791
+ const indexFile = path10.join(sectionPath, "index.ts");
2594
2792
  if (!fs.existsSync(schemaFile)) {
2595
2793
  issues.push({
2596
2794
  type: "error",
@@ -2614,14 +2812,14 @@ async function validateCommand(options) {
2614
2812
  }
2615
2813
  }
2616
2814
  }
2617
- const blocksDir = path8.join(themePath, "blocks");
2815
+ const blocksDir = path10.join(themePath, "blocks");
2618
2816
  if (fs.existsSync(blocksDir)) {
2619
- const blocks = fs.readdirSync(blocksDir).filter((name) => fs.statSync(path8.join(blocksDir, name)).isDirectory());
2817
+ const blocks = fs.readdirSync(blocksDir).filter((name) => fs.statSync(path10.join(blocksDir, name)).isDirectory());
2620
2818
  for (const blockName of blocks) {
2621
- const blockPath = path8.join(blocksDir, blockName);
2622
- const schemaFile = path8.join(blockPath, `${blockName}.schema.ts`);
2623
- const componentFile = path8.join(blockPath, `${blockName}.tsx`);
2624
- const indexFile = path8.join(blockPath, "index.ts");
2819
+ const blockPath = path10.join(blocksDir, blockName);
2820
+ const schemaFile = path10.join(blockPath, `${blockName}.schema.ts`);
2821
+ const componentFile = path10.join(blockPath, `${blockName}.tsx`);
2822
+ const indexFile = path10.join(blockPath, "index.ts");
2625
2823
  if (!fs.existsSync(schemaFile)) {
2626
2824
  issues.push({
2627
2825
  type: "error",
@@ -2647,13 +2845,13 @@ async function validateCommand(options) {
2647
2845
  }
2648
2846
  if (fs.existsSync(sectionsDir)) {
2649
2847
  const sections = fs.readdirSync(sectionsDir).filter(
2650
- (name) => fs.statSync(path8.join(sectionsDir, name)).isDirectory()
2848
+ (name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory()
2651
2849
  );
2652
2850
  for (const sectionName of sections) {
2653
- const sectionPath = path8.join(sectionsDir, sectionName);
2851
+ const sectionPath = path10.join(sectionsDir, sectionName);
2654
2852
  const tsxFiles = fs.readdirSync(sectionPath).filter((f) => f.endsWith(".tsx") && !f.endsWith(".schema.ts"));
2655
2853
  for (const tsxFile of tsxFiles) {
2656
- const filePath = path8.join(sectionPath, tsxFile);
2854
+ const filePath = path10.join(sectionPath, tsxFile);
2657
2855
  const content = fs.readFileSync(filePath, "utf-8");
2658
2856
  const relPath = `sections/${sectionName}/${tsxFile}`;
2659
2857
  if (!content.includes('"use client"') && !content.includes("'use client'")) {
@@ -2701,23 +2899,46 @@ async function validateCommand(options) {
2701
2899
  }
2702
2900
  }
2703
2901
  }
2704
- const registryPath = path8.join(themePath, "sections-registry.ts");
2705
- const bundleEntryPath = path8.join(themePath, "bundle-entry.ts");
2902
+ const registryPath = path10.join(themePath, "sections-registry.ts");
2903
+ const bundleEntryPath = path10.join(themePath, "bundle-entry.ts");
2706
2904
  const registryContent = fs.existsSync(registryPath) ? fs.readFileSync(registryPath, "utf-8") : fs.existsSync(bundleEntryPath) ? fs.readFileSync(bundleEntryPath, "utf-8") : "";
2707
2905
  if (fs.existsSync(sectionsDir) && registryContent) {
2708
2906
  const sections = fs.readdirSync(sectionsDir).filter(
2709
- (name) => fs.statSync(path8.join(sectionsDir, name)).isDirectory()
2907
+ (name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory()
2710
2908
  );
2711
2909
  for (const sectionName of sections) {
2712
2910
  if (!registryContent.includes(`sections/${sectionName}`) && !registryContent.includes(`"${sectionName}"`)) {
2713
2911
  issues.push({
2714
- type: "warning",
2912
+ type: "error",
2715
2913
  file: `sections/${sectionName}/`,
2716
- message: "Section not found in sections-registry.ts or bundle-entry.ts \u2014 may not be included in build"
2914
+ message: "Section not exported in sections-registry.ts or bundle-entry.ts \u2014 will not be included in build"
2717
2915
  });
2718
2916
  }
2719
2917
  }
2720
2918
  }
2919
+ if (fs.existsSync(sectionsDir)) {
2920
+ const schemaTypes = await loadSchemaTypes(themePath, sectionsDir);
2921
+ for (const { folderName, schemaType } of schemaTypes) {
2922
+ if (schemaType && schemaType !== folderName) {
2923
+ issues.push({
2924
+ type: "error",
2925
+ file: `sections/${folderName}/${folderName}.schema.ts`,
2926
+ message: `Schema type "${schemaType}" doesn't match folder name "${folderName}". Rename folder to "${schemaType}/" or change schema type to "${folderName}".`
2927
+ });
2928
+ }
2929
+ }
2930
+ const pagesDir = path10.join(themePath, "pages");
2931
+ if (fs.existsSync(pagesDir)) {
2932
+ const allSchemaTypeSet = new Set(
2933
+ schemaTypes.map((s) => s.schemaType || s.folderName)
2934
+ );
2935
+ const pageIssues = await validatePageSectionTypes(
2936
+ pagesDir,
2937
+ allSchemaTypeSet
2938
+ );
2939
+ issues.push(...pageIssues);
2940
+ }
2941
+ }
2721
2942
  logger.stopSpinner(true, "Validation complete");
2722
2943
  const errors = issues.filter((i) => i.type === "error");
2723
2944
  const warnings = issues.filter((i) => i.type === "warning");
@@ -2756,6 +2977,83 @@ async function validateCommand(options) {
2756
2977
  }
2757
2978
  }
2758
2979
  }
2980
+ async function loadSchemaTypes(themePath, sectionsDir) {
2981
+ const results = [];
2982
+ const sections = fs.readdirSync(sectionsDir).filter((name) => fs.statSync(path10.join(sectionsDir, name)).isDirectory());
2983
+ for (const sectionName of sections) {
2984
+ const schemaFile = path10.join(
2985
+ sectionsDir,
2986
+ sectionName,
2987
+ `${sectionName}.schema.ts`
2988
+ );
2989
+ if (!fs.existsSync(schemaFile)) {
2990
+ results.push({ folderName: sectionName, schemaType: null });
2991
+ continue;
2992
+ }
2993
+ const content = fs.readFileSync(schemaFile, "utf-8");
2994
+ const typeMatch = content.match(/\btype:\s*["']([^"']+)["']/);
2995
+ results.push({
2996
+ folderName: sectionName,
2997
+ schemaType: typeMatch ? typeMatch[1] : null
2998
+ });
2999
+ }
3000
+ return results;
3001
+ }
3002
+ async function validatePageSectionTypes(pagesDir, validTypes) {
3003
+ const issues = [];
3004
+ const files = fs.readdirSync(pagesDir).filter((f) => f.match(/\.(ts|js)$/));
3005
+ for (const file of files) {
3006
+ const content = fs.readFileSync(path10.join(pagesDir, file), "utf-8");
3007
+ const pageName = file.replace(/\.(ts|js)$/, "");
3008
+ const typeMatches = content.matchAll(/\btype:\s*["']([^"']+)["']/g);
3009
+ for (const match of typeMatches) {
3010
+ const sectionType = match[1];
3011
+ if (isFieldType(sectionType)) continue;
3012
+ if (!validTypes.has(sectionType)) {
3013
+ issues.push({
3014
+ type: "error",
3015
+ file: `pages/${file}`,
3016
+ message: `Page "${pageName}" uses section type "${sectionType}" which doesn't exist in sections/`
3017
+ });
3018
+ }
3019
+ }
3020
+ }
3021
+ return issues;
3022
+ }
3023
+ var FIELD_TYPES = /* @__PURE__ */ new Set([
3024
+ "text",
3025
+ "textarea",
3026
+ "richtext",
3027
+ "number",
3028
+ "range",
3029
+ "toggle",
3030
+ "switch",
3031
+ "select",
3032
+ "radio",
3033
+ "checkbox",
3034
+ "color",
3035
+ "image",
3036
+ "video",
3037
+ "url",
3038
+ "link",
3039
+ "icon",
3040
+ "date",
3041
+ "datetime",
3042
+ "collection",
3043
+ "product",
3044
+ "blog",
3045
+ "page",
3046
+ "html",
3047
+ "code",
3048
+ "json",
3049
+ "array",
3050
+ "object",
3051
+ "group",
3052
+ "section"
3053
+ ]);
3054
+ function isFieldType(type) {
3055
+ return FIELD_TYPES.has(type);
3056
+ }
2759
3057
 
2760
3058
  // src/commands/build.ts
2761
3059
  init_logger();
@@ -2766,14 +3064,14 @@ async function buildCommand(options) {
2766
3064
  if (options.theme) {
2767
3065
  themeName = options.theme;
2768
3066
  try {
2769
- const workspaceThemePath = path8.join(getThemesDir(), themeName);
3067
+ const workspaceThemePath = path10.join(getThemesDir(), themeName);
2770
3068
  if (fs.existsSync(workspaceThemePath)) {
2771
3069
  themePath = workspaceThemePath;
2772
3070
  } else {
2773
- themePath = path8.join(process.cwd(), themeName);
3071
+ themePath = path10.join(process.cwd(), themeName);
2774
3072
  }
2775
3073
  } catch {
2776
- themePath = path8.join(process.cwd(), themeName);
3074
+ themePath = path10.join(process.cwd(), themeName);
2777
3075
  }
2778
3076
  if (!fs.existsSync(themePath)) {
2779
3077
  logger.error(`Theme "${themeName}" not found.`);
@@ -2784,10 +3082,10 @@ async function buildCommand(options) {
2784
3082
  "theme.config.ts",
2785
3083
  "bundle-entry.ts",
2786
3084
  "manifest.ts"
2787
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
3085
+ ].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
2788
3086
  if (isThemeDir) {
2789
3087
  themePath = process.cwd();
2790
- themeName = path8.basename(themePath);
3088
+ themeName = path10.basename(themePath);
2791
3089
  logger.info(`Building current theme: ${themeName}`);
2792
3090
  } else {
2793
3091
  logger.error(
@@ -2796,7 +3094,7 @@ async function buildCommand(options) {
2796
3094
  process.exit(1);
2797
3095
  }
2798
3096
  }
2799
- const packageJsonPath = path8.join(themePath, "package.json");
3097
+ const packageJsonPath = path10.join(themePath, "package.json");
2800
3098
  const hasPkgJson = fs.existsSync(packageJsonPath);
2801
3099
  if (!hasPkgJson) {
2802
3100
  logger.warning(
@@ -2852,9 +3150,9 @@ async function buildCommand(options) {
2852
3150
  logger.success("\u2713 Theme built successfully!");
2853
3151
  logger.newLine();
2854
3152
  logger.info(`Theme: ${themeName}`);
2855
- const distPath = path8.join(themePath, "dist");
3153
+ const distPath = path10.join(themePath, "dist");
2856
3154
  if (fs.existsSync(distPath)) {
2857
- logger.log(`Output: ${path8.relative(process.cwd(), distPath)}`);
3155
+ logger.log(`Output: ${path10.relative(process.cwd(), distPath)}`);
2858
3156
  const files = fs.readdirSync(distPath);
2859
3157
  logger.log(`Files: ${files.length}`);
2860
3158
  }
@@ -2862,17 +3160,17 @@ async function buildCommand(options) {
2862
3160
  }
2863
3161
  function runCommand(command, args, cwd) {
2864
3162
  return new Promise((resolve) => {
2865
- const proc = spawn(command, args, {
3163
+ const proc = spawn2(command, args, {
2866
3164
  cwd,
2867
3165
  stdio: ["pipe", "pipe", "pipe"],
2868
3166
  shell: true
2869
3167
  });
2870
3168
  let stdout = "";
2871
3169
  let stderr = "";
2872
- proc.stdout.on("data", (data) => {
3170
+ proc.stdout?.on("data", (data) => {
2873
3171
  stdout += data.toString();
2874
3172
  });
2875
- proc.stderr.on("data", (data) => {
3173
+ proc.stderr?.on("data", (data) => {
2876
3174
  stderr += data.toString();
2877
3175
  });
2878
3176
  proc.on("close", (code) => {
@@ -2910,7 +3208,7 @@ async function packageCommand(options) {
2910
3208
  let themeName;
2911
3209
  if (options.theme) {
2912
3210
  themeName = options.theme;
2913
- themePath = path8.join(getThemesDir(), themeName);
3211
+ themePath = path10.join(getThemesDir(), themeName);
2914
3212
  if (!fs.existsSync(themePath)) {
2915
3213
  logger.error(`Theme "${themeName}" not found.`);
2916
3214
  process.exit(1);
@@ -2920,10 +3218,10 @@ async function packageCommand(options) {
2920
3218
  "theme.config.ts",
2921
3219
  "bundle-entry.ts",
2922
3220
  "manifest.ts"
2923
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
3221
+ ].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
2924
3222
  if (isThemeDir) {
2925
3223
  themePath = process.cwd();
2926
- themeName = path8.basename(themePath);
3224
+ themeName = path10.basename(themePath);
2927
3225
  logger.info(`Packaging current theme: ${themeName}`);
2928
3226
  } else {
2929
3227
  logger.error(
@@ -2932,7 +3230,7 @@ async function packageCommand(options) {
2932
3230
  process.exit(1);
2933
3231
  }
2934
3232
  }
2935
- const packageJsonPath = path8.join(themePath, "package.json");
3233
+ const packageJsonPath = path10.join(themePath, "package.json");
2936
3234
  let version2 = "1.0.0";
2937
3235
  if (fs.existsSync(packageJsonPath)) {
2938
3236
  const packageJson = await fs.readJson(packageJsonPath);
@@ -2942,7 +3240,7 @@ async function packageCommand(options) {
2942
3240
  logger.info(`Theme: ${themeName}`);
2943
3241
  logger.info(`Version: ${version2}`);
2944
3242
  logger.newLine();
2945
- const compiledThemePath = path8.join(
3243
+ const compiledThemePath = path10.join(
2946
3244
  process.cwd(),
2947
3245
  "themes",
2948
3246
  themeName,
@@ -2976,8 +3274,8 @@ async function packageCommand(options) {
2976
3274
  logger.newLine();
2977
3275
  logger.section("Step 2: Create Package");
2978
3276
  const packageName = options.name || `${themeName}-${version2}`;
2979
- const outputDir = options.output || path8.join(process.cwd(), "dist");
2980
- const outputPath = path8.join(outputDir, `${packageName}.zip`);
3277
+ const outputDir = options.output || path10.join(process.cwd(), "dist");
3278
+ const outputPath = path10.join(outputDir, `${packageName}.zip`);
2981
3279
  await fs.ensureDir(outputDir);
2982
3280
  logger.startSpinner("Creating zip archive...");
2983
3281
  try {
@@ -2990,11 +3288,11 @@ async function packageCommand(options) {
2990
3288
  logger.newLine();
2991
3289
  logger.info(`Package: ${packageName}.zip`);
2992
3290
  logger.log(`Size: ${sizeMB} MB`);
2993
- logger.log(`Location: ${path8.relative(process.cwd(), outputPath)}`);
3291
+ logger.log(`Location: ${path10.relative(process.cwd(), outputPath)}`);
2994
3292
  logger.newLine();
2995
3293
  logger.section("Next steps:");
2996
3294
  logger.log(
2997
- ` onexthm deploy --package ${path8.relative(process.cwd(), outputPath)}`
3295
+ ` onexthm deploy --package ${path10.relative(process.cwd(), outputPath)}`
2998
3296
  );
2999
3297
  } catch (error) {
3000
3298
  logger.stopSpinner(false, "Failed to create package");
@@ -3006,13 +3304,13 @@ async function packageCommand(options) {
3006
3304
  }
3007
3305
  function runCommand2(command, args) {
3008
3306
  return new Promise((resolve) => {
3009
- const proc = spawn(command, args, {
3307
+ const proc = spawn2(command, args, {
3010
3308
  cwd: process.cwd(),
3011
3309
  stdio: "pipe",
3012
3310
  shell: true
3013
3311
  });
3014
3312
  let hasError = false;
3015
- proc.stderr.on("data", (data) => {
3313
+ proc.stderr?.on("data", (data) => {
3016
3314
  const message = data.toString();
3017
3315
  if (message.includes("error") || message.includes("Error") || message.includes("ERROR")) {
3018
3316
  hasError = true;
@@ -3052,9 +3350,9 @@ async function deployCommand(options) {
3052
3350
  ensureOneXProject();
3053
3351
  let packagePath;
3054
3352
  if (options.package) {
3055
- packagePath = path8.resolve(options.package);
3353
+ packagePath = path10.resolve(options.package);
3056
3354
  } else if (options.theme) {
3057
- const distDir = path8.join(process.cwd(), "dist");
3355
+ const distDir = path10.join(process.cwd(), "dist");
3058
3356
  if (!fs.existsSync(distDir)) {
3059
3357
  logger.error("No dist/ directory found. Run 'onexthm package' first.");
3060
3358
  process.exit(1);
@@ -3069,7 +3367,7 @@ async function deployCommand(options) {
3069
3367
  process.exit(1);
3070
3368
  }
3071
3369
  packageFiles.sort().reverse();
3072
- packagePath = path8.join(distDir, packageFiles[0]);
3370
+ packagePath = path10.join(distDir, packageFiles[0]);
3073
3371
  } else {
3074
3372
  logger.error("Either --package or --theme must be specified.");
3075
3373
  logger.info("Examples:");
@@ -3083,11 +3381,11 @@ async function deployCommand(options) {
3083
3381
  }
3084
3382
  const stats = await fs.stat(packagePath);
3085
3383
  const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
3086
- const fileName = path8.basename(packagePath);
3384
+ const fileName = path10.basename(packagePath);
3087
3385
  logger.newLine();
3088
3386
  logger.info(`Package: ${fileName}`);
3089
3387
  logger.log(`Size: ${sizeMB} MB`);
3090
- logger.log(`Path: ${path8.relative(process.cwd(), packagePath)}`);
3388
+ logger.log(`Path: ${path10.relative(process.cwd(), packagePath)}`);
3091
3389
  logger.newLine();
3092
3390
  const apiUrl = options.apiUrl || process.env.ONEX_API_URL || "http://localhost:3001";
3093
3391
  const uploadEndpoint = `${apiUrl}/website-api/themes/upload`;
@@ -3191,11 +3489,11 @@ function getBucketName(env) {
3191
3489
  return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3192
3490
  }
3193
3491
  async function findCompiledThemeDir(themeId, version2) {
3194
- const searchPaths = [path8.resolve(process.cwd(), "dist")];
3492
+ const searchPaths = [path10.resolve(process.cwd(), "dist")];
3195
3493
  for (const dir of searchPaths) {
3196
3494
  if (await fs.pathExists(dir)) {
3197
- const hasManifest = await fs.pathExists(path8.join(dir, "manifest.json"));
3198
- const hasThemeEntry = await fs.pathExists(path8.join(dir, "bundle-entry.js")) || await fs.pathExists(path8.join(dir, "theme.config.js")) || await fs.pathExists(path8.join(dir, "index.js"));
3495
+ const hasManifest = await fs.pathExists(path10.join(dir, "manifest.json"));
3496
+ const hasThemeEntry = await fs.pathExists(path10.join(dir, "bundle-entry.js")) || await fs.pathExists(path10.join(dir, "theme.config.js")) || await fs.pathExists(path10.join(dir, "index.js"));
3199
3497
  if (hasManifest || hasThemeEntry) {
3200
3498
  return dir;
3201
3499
  }
@@ -3204,7 +3502,7 @@ async function findCompiledThemeDir(themeId, version2) {
3204
3502
  return null;
3205
3503
  }
3206
3504
  async function readManifest() {
3207
- const manifestTsPath = path8.resolve(process.cwd(), "manifest.ts");
3505
+ const manifestTsPath = path10.resolve(process.cwd(), "manifest.ts");
3208
3506
  if (await fs.pathExists(manifestTsPath)) {
3209
3507
  try {
3210
3508
  const module = await import(manifestTsPath);
@@ -3213,7 +3511,7 @@ async function readManifest() {
3213
3511
  logger.warning("Failed to import manifest.ts, trying package.json");
3214
3512
  }
3215
3513
  }
3216
- const packageJsonPath = path8.resolve(process.cwd(), "package.json");
3514
+ const packageJsonPath = path10.resolve(process.cwd(), "package.json");
3217
3515
  if (await fs.pathExists(packageJsonPath)) {
3218
3516
  const pkg = await fs.readJson(packageJsonPath);
3219
3517
  return {
@@ -3247,13 +3545,13 @@ async function findSourceDir(themeId, explicitDir) {
3247
3545
  }
3248
3546
  const searchPaths = [
3249
3547
  process.cwd(),
3250
- path8.resolve(process.cwd(), `../../themes/${themeId}`),
3251
- path8.resolve(process.cwd(), `../themes/${themeId}`)
3548
+ path10.resolve(process.cwd(), `../../themes/${themeId}`),
3549
+ path10.resolve(process.cwd(), `../themes/${themeId}`)
3252
3550
  ];
3253
3551
  const markers = ["theme.config.ts", "bundle-entry.ts"];
3254
3552
  for (const dir of searchPaths) {
3255
3553
  for (const marker of markers) {
3256
- if (await fs.pathExists(path8.join(dir, marker))) {
3554
+ if (await fs.pathExists(path10.join(dir, marker))) {
3257
3555
  return dir;
3258
3556
  }
3259
3557
  }
@@ -3304,8 +3602,8 @@ async function uploadCommand(options) {
3304
3602
  }
3305
3603
  spinner.succeed(`Found compiled theme at: ${compiledDir}`);
3306
3604
  spinner.start("Creating bundle.zip...");
3307
- const tmpDir = os3.tmpdir();
3308
- const bundleZipPath = path8.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
3605
+ const tmpDir = os.tmpdir();
3606
+ const bundleZipPath = path10.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
3309
3607
  await createZipFromDir(compiledDir, bundleZipPath);
3310
3608
  const bundleZipBuffer = await fs.readFile(bundleZipPath);
3311
3609
  const bundleSizeMB = (bundleZipBuffer.length / 1024 / 1024).toFixed(2);
@@ -3359,7 +3657,7 @@ async function uploadCommand(options) {
3359
3657
  if (sourceDir) {
3360
3658
  spinner.succeed(`Found source at: ${sourceDir}`);
3361
3659
  spinner.start("Creating source.zip...");
3362
- const sourceZipPath = path8.join(
3660
+ const sourceZipPath = path10.join(
3363
3661
  tmpDir,
3364
3662
  `${themeId}-${version2}-source.zip`
3365
3663
  );
@@ -3493,8 +3791,8 @@ async function resolveLatestVersion(s3Client, bucket, themeId) {
3493
3791
  async function createCompatibilityFiles(outputDir, manifest) {
3494
3792
  const entryFile = manifest.output?.entry || "bundle-entry.js";
3495
3793
  if (entryFile !== "bundle-entry.js" && entryFile.startsWith("bundle-entry-")) {
3496
- const hashedPath = path8.join(outputDir, entryFile);
3497
- const stablePath = path8.join(outputDir, "bundle-entry.js");
3794
+ const hashedPath = path10.join(outputDir, entryFile);
3795
+ const stablePath = path10.join(outputDir, "bundle-entry.js");
3498
3796
  if (await fs.pathExists(hashedPath)) {
3499
3797
  await fs.copy(hashedPath, stablePath);
3500
3798
  const mapPath = hashedPath + ".map";
@@ -3503,13 +3801,13 @@ async function createCompatibilityFiles(outputDir, manifest) {
3503
3801
  }
3504
3802
  }
3505
3803
  }
3506
- const sectionsRegistryPath = path8.join(outputDir, "sections-registry.js");
3804
+ const sectionsRegistryPath = path10.join(outputDir, "sections-registry.js");
3507
3805
  const content = `// Re-export all sections from bundle-entry
3508
3806
  // This file exists to maintain compatibility with the import path
3509
3807
  export * from './bundle-entry.js';
3510
3808
  `;
3511
3809
  await fs.writeFile(sectionsRegistryPath, content, "utf-8");
3512
- const pkgJsonPath = path8.join(outputDir, "package.json");
3810
+ const pkgJsonPath = path10.join(outputDir, "package.json");
3513
3811
  await fs.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
3514
3812
  }
3515
3813
  function showDownloadFailureHelp(themeId, bucket) {
@@ -3571,6 +3869,18 @@ async function downloadCommand(options) {
3571
3869
  spinner.succeed(
3572
3870
  `Resolved latest version: ${chalk4.cyan(resolvedVersion)}`
3573
3871
  );
3872
+ const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.VERCEL);
3873
+ if (isCI) {
3874
+ console.log(
3875
+ chalk4.yellow(
3876
+ `
3877
+ Warning: Resolved "latest" to ${resolvedVersion} in CI environment.
3878
+ For production builds, pin to a specific version:
3879
+ THEME_VERSION=${resolvedVersion}
3880
+ `
3881
+ )
3882
+ );
3883
+ }
3574
3884
  }
3575
3885
  spinner.start(
3576
3886
  `Downloading bundle.zip for ${themeId}@${resolvedVersion}...`
@@ -3592,7 +3902,7 @@ async function downloadCommand(options) {
3592
3902
  zip.extractAllTo(outputDir, true);
3593
3903
  const entries = zip.getEntries().filter((e) => !e.isDirectory);
3594
3904
  spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
3595
- const manifestPath = path8.join(outputDir, "manifest.json");
3905
+ const manifestPath = path10.join(outputDir, "manifest.json");
3596
3906
  const manifest = await fs.readJson(manifestPath);
3597
3907
  await createCompatibilityFiles(outputDir, manifest);
3598
3908
  console.log();
@@ -3692,10 +4002,9 @@ async function resolveLatestVersion2(s3Client, bucket, themeId) {
3692
4002
  }
3693
4003
  function runInstall(cwd) {
3694
4004
  return new Promise((resolve) => {
3695
- const proc = spawn("pnpm", ["install"], {
4005
+ const proc = spawn2("pnpm", ["install"], {
3696
4006
  cwd,
3697
- stdio: "inherit",
3698
- shell: true
4007
+ stdio: "inherit"
3699
4008
  });
3700
4009
  proc.on("close", (code) => resolve(code === 0));
3701
4010
  proc.on("error", () => resolve(false));
@@ -3726,7 +4035,7 @@ async function renameTheme(themeDir, oldName, newName) {
3726
4035
  const oldPrefix = `${oldName}-`;
3727
4036
  const newPrefix = `${newName}-`;
3728
4037
  const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3729
- const pkgPath = path8.join(themeDir, "package.json");
4038
+ const pkgPath = path10.join(themeDir, "package.json");
3730
4039
  if (await fs.pathExists(pkgPath)) {
3731
4040
  const pkg = await fs.readJson(pkgPath);
3732
4041
  pkg.name = `@onex-themes/${newName}`;
@@ -3742,7 +4051,7 @@ async function renameTheme(themeDir, oldName, newName) {
3742
4051
  }
3743
4052
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3744
4053
  }
3745
- const configPath = path8.join(themeDir, "theme.config.ts");
4054
+ const configPath = path10.join(themeDir, "theme.config.ts");
3746
4055
  if (await fs.pathExists(configPath)) {
3747
4056
  let content = await fs.readFile(configPath, "utf-8");
3748
4057
  content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
@@ -3752,7 +4061,7 @@ async function renameTheme(themeDir, oldName, newName) {
3752
4061
  );
3753
4062
  await fs.writeFile(configPath, content);
3754
4063
  }
3755
- const layoutPath = path8.join(themeDir, "theme.layout.ts");
4064
+ const layoutPath = path10.join(themeDir, "theme.layout.ts");
3756
4065
  if (await fs.pathExists(layoutPath)) {
3757
4066
  let content = await fs.readFile(layoutPath, "utf-8");
3758
4067
  content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
@@ -3765,7 +4074,7 @@ async function renameTheme(themeDir, oldName, newName) {
3765
4074
  const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3766
4075
  const tsFiles = await glob("**/*.ts", { cwd: themeDir, nodir: true });
3767
4076
  for (const file of tsFiles) {
3768
- const filePath = path8.join(themeDir, file);
4077
+ const filePath = path10.join(themeDir, file);
3769
4078
  let content = await fs.readFile(filePath, "utf-8");
3770
4079
  const original = content;
3771
4080
  content = content.replace(
@@ -3794,7 +4103,7 @@ async function cloneCommand(themeName, options) {
3794
4103
  const spinner = ora("Initializing clone...").start();
3795
4104
  try {
3796
4105
  const bucket = options.bucket || getBucketName3(options.environment);
3797
- const outputDir = options.output || path8.resolve(process.cwd(), newName);
4106
+ const outputDir = options.output || path10.resolve(process.cwd(), newName);
3798
4107
  const s3Client = getS3Client3();
3799
4108
  if (await fs.pathExists(outputDir)) {
3800
4109
  spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
@@ -3849,7 +4158,7 @@ async function cloneCommand(themeName, options) {
3849
4158
  spinner.succeed(
3850
4159
  `Renamed theme: ${chalk4.gray(themeName)} \u2192 ${chalk4.cyan(newName)}`
3851
4160
  );
3852
- const envExamplePath = path8.join(outputDir, ".env.example");
4161
+ const envExamplePath = path10.join(outputDir, ".env.example");
3853
4162
  if (!await fs.pathExists(envExamplePath)) {
3854
4163
  await fs.writeFile(
3855
4164
  envExamplePath,
@@ -3862,32 +4171,18 @@ async function cloneCommand(themeName, options) {
3862
4171
  ].join("\n")
3863
4172
  );
3864
4173
  }
3865
- const mcpJsonPath = path8.join(outputDir, ".mcp.json");
3866
- if (await fs.pathExists(mcpJsonPath)) {
3867
- const { default: inquirerMod } = await import('inquirer');
3868
- const { figmaApiKey } = await inquirerMod.prompt([
3869
- {
3870
- type: "password",
3871
- name: "figmaApiKey",
3872
- message: "Figma API Key (optional, for Figma-to-code MCP \u2014 press Enter to skip):"
3873
- }
3874
- ]);
3875
- let mcpContent = await fs.readFile(mcpJsonPath, "utf-8");
3876
- if (figmaApiKey) {
3877
- mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
3878
- } else {
3879
- try {
3880
- const mcpJson = JSON.parse(mcpContent);
3881
- delete mcpJson.mcpServers?.figma;
3882
- mcpContent = JSON.stringify(mcpJson, null, 2) + "\n";
3883
- } catch {
3884
- }
4174
+ const { default: inquirerMod } = await import('inquirer');
4175
+ const { figmaApiKey } = await inquirerMod.prompt([
4176
+ {
4177
+ type: "password",
4178
+ name: "figmaApiKey",
4179
+ message: "Figma API Key (optional, for Figma-to-code MCP \u2014 press Enter to skip):"
3885
4180
  }
3886
- await fs.writeFile(mcpJsonPath, mcpContent, "utf-8");
3887
- }
4181
+ ]);
4182
+ ensureMcpJson(outputDir, figmaApiKey || void 0);
3888
4183
  if (options.install !== false) {
3889
4184
  const hasPkgJson = await fs.pathExists(
3890
- path8.join(outputDir, "package.json")
4185
+ path10.join(outputDir, "package.json")
3891
4186
  );
3892
4187
  if (hasPkgJson) {
3893
4188
  spinner.start("Installing dependencies...");
@@ -3914,9 +4209,10 @@ async function cloneCommand(themeName, options) {
3914
4209
  console.log(chalk4.cyan(" Files: ") + chalk4.white(entries.length));
3915
4210
  console.log();
3916
4211
  console.log(chalk4.cyan("Next steps:"));
3917
- console.log(chalk4.gray(` cd ${path8.relative(process.cwd(), outputDir)}`));
4212
+ console.log(chalk4.gray(` cd ${path10.relative(process.cwd(), outputDir)}`));
4213
+ const copyCmd = process.platform === "win32" ? "copy .env.example .env" : "cp .env.example .env";
3918
4214
  console.log(
3919
- chalk4.gray(" cp .env.example .env # then add your Company ID")
4215
+ chalk4.gray(` ${copyCmd} # then add your Company ID`)
3920
4216
  );
3921
4217
  if (options.install === false) {
3922
4218
  console.log(chalk4.gray(" pnpm install"));
@@ -3949,7 +4245,7 @@ var MIME_TYPES = {
3949
4245
  };
3950
4246
  function createDevServer(options) {
3951
4247
  const clients = /* @__PURE__ */ new Set();
3952
- const themeDataPath = path8.join(options.distDir, "theme-data.json");
4248
+ const themeDataPath = path10.join(options.distDir, "theme-data.json");
3953
4249
  const server = http.createServer((req, res) => {
3954
4250
  res.setHeader("Access-Control-Allow-Origin", "*");
3955
4251
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
@@ -3975,8 +4271,8 @@ function createDevServer(options) {
3975
4271
  if (pathname.startsWith("/_assets/")) {
3976
4272
  const parts = pathname.replace(/^\/_assets\//, "").split("/");
3977
4273
  const assetSubpath = parts.slice(1).join("/");
3978
- const assetPath = path8.join(options.themePath, "assets", assetSubpath);
3979
- if (!assetPath.startsWith(path8.join(options.themePath, "assets"))) {
4274
+ const assetPath = path10.join(options.themePath, "assets", assetSubpath);
4275
+ if (!assetPath.startsWith(path10.join(options.themePath, "assets"))) {
3980
4276
  res.writeHead(403);
3981
4277
  res.end("Forbidden");
3982
4278
  return;
@@ -3987,8 +4283,8 @@ function createDevServer(options) {
3987
4283
  if (pathname.startsWith("/themes/")) {
3988
4284
  const match = pathname.match(/^\/themes\/[^/]+\/assets\/(.+)/);
3989
4285
  if (match) {
3990
- const assetPath = path8.join(options.themePath, "assets", match[1]);
3991
- if (!assetPath.startsWith(path8.join(options.themePath, "assets"))) {
4286
+ const assetPath = path10.join(options.themePath, "assets", match[1]);
4287
+ if (!assetPath.startsWith(path10.join(options.themePath, "assets"))) {
3992
4288
  res.writeHead(403);
3993
4289
  res.end("Forbidden");
3994
4290
  return;
@@ -4002,26 +4298,26 @@ function createDevServer(options) {
4002
4298
  const segments = subpath.split("/");
4003
4299
  let assetPath;
4004
4300
  if (segments[0] === options.themeName || segments[0] === options.themeName.replace(/^my-/, "")) {
4005
- assetPath = path8.join(
4301
+ assetPath = path10.join(
4006
4302
  options.themePath,
4007
4303
  "assets",
4008
4304
  segments.slice(1).join("/")
4009
4305
  );
4010
4306
  } else {
4011
- assetPath = path8.join(options.themePath, "assets", subpath);
4307
+ assetPath = path10.join(options.themePath, "assets", subpath);
4012
4308
  }
4013
- if (assetPath.startsWith(path8.join(options.themePath, "assets")) && fs2.existsSync(assetPath)) {
4309
+ if (assetPath.startsWith(path10.join(options.themePath, "assets")) && fs4.existsSync(assetPath)) {
4014
4310
  serveFile(res, assetPath);
4015
4311
  return;
4016
4312
  }
4017
4313
  }
4018
- const filePath = path8.join(options.distDir, pathname);
4314
+ const filePath = path10.join(options.distDir, pathname);
4019
4315
  if (!filePath.startsWith(options.distDir)) {
4020
4316
  res.writeHead(403);
4021
4317
  res.end("Forbidden");
4022
4318
  return;
4023
4319
  }
4024
- if (fs2.existsSync(filePath) && fs2.statSync(filePath).isFile()) {
4320
+ if (fs4.existsSync(filePath) && fs4.statSync(filePath).isFile()) {
4025
4321
  serveFile(res, filePath);
4026
4322
  } else {
4027
4323
  res.writeHead(200, { "Content-Type": "text/html" });
@@ -4053,14 +4349,14 @@ function createDevServer(options) {
4053
4349
  }
4054
4350
  function serveFile(res, filePath) {
4055
4351
  try {
4056
- if (!fs2.existsSync(filePath)) {
4352
+ if (!fs4.existsSync(filePath)) {
4057
4353
  res.writeHead(404);
4058
4354
  res.end("Not Found");
4059
4355
  return;
4060
4356
  }
4061
- const ext = path8.extname(filePath);
4357
+ const ext = path10.extname(filePath);
4062
4358
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
4063
- const content = fs2.readFileSync(filePath);
4359
+ const content = fs4.readFileSync(filePath);
4064
4360
  res.writeHead(200, { "Content-Type": contentType });
4065
4361
  res.end(content);
4066
4362
  } catch {
@@ -4073,7 +4369,7 @@ function generatePreviewHTML(themeName, port, themeDataPath) {
4073
4369
  let fontVarsCSS = "";
4074
4370
  if (themeDataPath) {
4075
4371
  try {
4076
- const themeData = JSON.parse(fs2.readFileSync(themeDataPath, "utf-8"));
4372
+ const themeData = JSON.parse(fs4.readFileSync(themeDataPath, "utf-8"));
4077
4373
  const typography = (themeData?.themeConfig || themeData?.theme?.config)?.typography?.fontFamily;
4078
4374
  if (typography) {
4079
4375
  const fontFamilies = /* @__PURE__ */ new Set();
@@ -4161,14 +4457,14 @@ async function devCommand(options) {
4161
4457
  if (options.theme) {
4162
4458
  themeName = options.theme;
4163
4459
  try {
4164
- const workspaceThemePath = path8.join(getThemesDir(), themeName);
4460
+ const workspaceThemePath = path10.join(getThemesDir(), themeName);
4165
4461
  if (fs.existsSync(workspaceThemePath)) {
4166
4462
  themePath = workspaceThemePath;
4167
4463
  } else {
4168
- themePath = path8.join(process.cwd(), themeName);
4464
+ themePath = path10.join(process.cwd(), themeName);
4169
4465
  }
4170
4466
  } catch {
4171
- themePath = path8.join(process.cwd(), themeName);
4467
+ themePath = path10.join(process.cwd(), themeName);
4172
4468
  }
4173
4469
  if (!fs.existsSync(themePath)) {
4174
4470
  logger.error(`Theme "${themeName}" not found.`);
@@ -4179,10 +4475,10 @@ async function devCommand(options) {
4179
4475
  "theme.config.ts",
4180
4476
  "bundle-entry.ts",
4181
4477
  "manifest.ts"
4182
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
4478
+ ].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
4183
4479
  if (isThemeDir) {
4184
4480
  themePath = process.cwd();
4185
- themeName = path8.basename(themePath);
4481
+ themeName = path10.basename(themePath);
4186
4482
  } else {
4187
4483
  logger.error(
4188
4484
  "Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
@@ -4251,9 +4547,9 @@ async function devCommand(options) {
4251
4547
  watcher.close();
4252
4548
  await context2.dispose();
4253
4549
  server.close();
4254
- const shimPath = path8.join(outputDir, ".process-shim.js");
4550
+ const shimPath = path10.join(outputDir, ".process-shim.js");
4255
4551
  try {
4256
- await fs7.unlink(shimPath);
4552
+ await fs9.unlink(shimPath);
4257
4553
  } catch {
4258
4554
  }
4259
4555
  process.exit(0);
@@ -4262,8 +4558,8 @@ async function devCommand(options) {
4262
4558
 
4263
4559
  // src/commands/config.ts
4264
4560
  init_logger();
4265
- var CONFIG_DIR = path8.join(os3.homedir(), ".onexthm");
4266
- var CONFIG_FILE = path8.join(CONFIG_DIR, ".env");
4561
+ var CONFIG_DIR = path10.join(os.homedir(), ".onexthm");
4562
+ var CONFIG_FILE = path10.join(CONFIG_DIR, ".env");
4267
4563
  var CONFIG_ENTRIES = [
4268
4564
  {
4269
4565
  key: "AWS_ACCESS_KEY_ID",
@@ -4405,122 +4701,6 @@ async function configCommand() {
4405
4701
 
4406
4702
  // src/commands/login.ts
4407
4703
  init_logger();
4408
- var AUTH_DIR = path8.join(os3.homedir(), ".onexthm");
4409
- var AUTH_FILE = path8.join(AUTH_DIR, "auth.json");
4410
- function getApiUrl() {
4411
- return process.env.ONEXTHM_API_URL || process.env.NEXT_PUBLIC_API_URL || "https://platform-dev.onexeos.com";
4412
- }
4413
- async function saveAuthTokens(tokens) {
4414
- await fs.ensureDir(AUTH_DIR);
4415
- const key = getMachineKey();
4416
- const data = JSON.stringify(tokens);
4417
- const encrypted = encrypt(data, key);
4418
- await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
4419
- }
4420
- function loadAuthTokens() {
4421
- try {
4422
- if (!fs.existsSync(AUTH_FILE)) return null;
4423
- const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
4424
- const key = getMachineKey();
4425
- const data = decrypt(encrypted, key);
4426
- return JSON.parse(data);
4427
- } catch {
4428
- return null;
4429
- }
4430
- }
4431
- async function clearAuthTokens() {
4432
- try {
4433
- await fs.remove(AUTH_FILE);
4434
- } catch {
4435
- }
4436
- }
4437
- function isTokenExpired(tokens) {
4438
- return Date.now() / 1e3 > tokens.expiresAt - 60;
4439
- }
4440
- async function getValidTokens() {
4441
- const tokens = loadAuthTokens();
4442
- if (!tokens) return null;
4443
- if (!isTokenExpired(tokens)) return tokens;
4444
- try {
4445
- const apiUrl = getApiUrl();
4446
- const response = await fetch(`${apiUrl}/auth/refresh`, {
4447
- method: "POST",
4448
- headers: { "Content-Type": "application/json" },
4449
- body: JSON.stringify({ refresh_token: tokens.refreshToken })
4450
- });
4451
- if (!response.ok) {
4452
- await clearAuthTokens();
4453
- return null;
4454
- }
4455
- const data = await response.json();
4456
- const body = data.statusCode ? data.body : data;
4457
- const refreshed = {
4458
- ...tokens,
4459
- accessToken: body.AccessToken || tokens.accessToken,
4460
- idToken: body.IdToken || tokens.idToken,
4461
- expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
4462
- };
4463
- await saveAuthTokens(refreshed);
4464
- return refreshed;
4465
- } catch {
4466
- await clearAuthTokens();
4467
- return null;
4468
- }
4469
- }
4470
- async function authenticatedFetch(url, init) {
4471
- const tokens = await getValidTokens();
4472
- if (!tokens) {
4473
- throw new Error("Not logged in. Run: onexthm login");
4474
- }
4475
- const headers = new Headers(init?.headers);
4476
- headers.set("Authorization", `Bearer ${tokens.idToken}`);
4477
- headers.set("Content-Type", "application/json");
4478
- return fetch(url, { ...init, headers });
4479
- }
4480
- function getMachineKey() {
4481
- let seed;
4482
- if (process.platform === "darwin") {
4483
- seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
4484
- } else if (process.platform === "linux") {
4485
- try {
4486
- seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
4487
- } catch {
4488
- seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
4489
- }
4490
- } else {
4491
- seed = `onexthm:${os3.hostname()}:${os3.userInfo().username}`;
4492
- }
4493
- return crypto2.createHash("sha256").update(seed).digest();
4494
- }
4495
- function encrypt(text, key) {
4496
- const iv = crypto2.randomBytes(16);
4497
- const cipher = crypto2.createCipheriv("aes-256-gcm", key, iv);
4498
- let encrypted = cipher.update(text, "utf-8", "hex");
4499
- encrypted += cipher.final("hex");
4500
- const tag = cipher.getAuthTag();
4501
- return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
4502
- }
4503
- function decrypt(text, key) {
4504
- const [ivHex, tagHex, encrypted] = text.split(":");
4505
- const iv = Buffer.from(ivHex, "hex");
4506
- const tag = Buffer.from(tagHex, "hex");
4507
- const decipher = crypto2.createDecipheriv("aes-256-gcm", key, iv);
4508
- decipher.setAuthTag(tag);
4509
- let decrypted = decipher.update(encrypted, "hex", "utf-8");
4510
- decrypted += decipher.final("utf-8");
4511
- return decrypted;
4512
- }
4513
- function parseJwtClaims(idToken) {
4514
- try {
4515
- const payload = idToken.split(".")[1];
4516
- const decoded = Buffer.from(payload, "base64url").toString("utf-8");
4517
- return JSON.parse(decoded);
4518
- } catch {
4519
- return {};
4520
- }
4521
- }
4522
-
4523
- // src/commands/login.ts
4524
4704
  async function loginCommand() {
4525
4705
  logger.header("OneX Theme Developer Login");
4526
4706
  const existing = loadAuthTokens();
@@ -4646,13 +4826,13 @@ async function publishCommand(options) {
4646
4826
  logger.info(`Logged in as: ${tokens.user.email}`);
4647
4827
  let themePath;
4648
4828
  if (options.theme) {
4649
- themePath = path8.resolve(options.theme);
4829
+ themePath = path10.resolve(options.theme);
4650
4830
  } else {
4651
4831
  const isThemeDir = [
4652
4832
  "theme.config.ts",
4653
4833
  "bundle-entry.ts",
4654
4834
  "manifest.ts"
4655
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
4835
+ ].some((f) => fs.existsSync(path10.join(process.cwd(), f)));
4656
4836
  if (isThemeDir) {
4657
4837
  themePath = process.cwd();
4658
4838
  } else {
@@ -4662,14 +4842,31 @@ async function publishCommand(options) {
4662
4842
  process.exit(1);
4663
4843
  }
4664
4844
  }
4665
- const pkgPath = path8.join(themePath, "package.json");
4845
+ const pkgPath = path10.join(themePath, "package.json");
4666
4846
  if (!fs.existsSync(pkgPath)) {
4667
4847
  logger.error("No package.json found in theme directory");
4668
4848
  process.exit(1);
4669
4849
  }
4670
4850
  const pkg = fs.readJsonSync(pkgPath);
4671
- const themeId = pkg.name?.replace("@onex-themes/", "") || path8.basename(themePath);
4851
+ const themeId = pkg.name?.replace("@onex-themes/", "") || path10.basename(themePath);
4852
+ if (options.bump) {
4853
+ const currentVersion = pkg.version || "1.0.0";
4854
+ const newVersion = semver.inc(currentVersion, options.bump);
4855
+ if (!newVersion) {
4856
+ logger.error(`Failed to bump version from ${currentVersion}`);
4857
+ process.exit(1);
4858
+ }
4859
+ pkg.version = newVersion;
4860
+ fs.writeJsonSync(pkgPath, pkg, { spaces: 2 });
4861
+ logger.info(`Bumped version: ${currentVersion} -> ${newVersion}`);
4862
+ }
4672
4863
  const version2 = pkg.version || "1.0.0";
4864
+ if (!semver.valid(version2)) {
4865
+ logger.error(
4866
+ `Invalid version "${version2}" in package.json. Must be valid semver (e.g., 1.0.0)`
4867
+ );
4868
+ process.exit(1);
4869
+ }
4673
4870
  logger.newLine();
4674
4871
  logger.info(`Theme: ${themeId}`);
4675
4872
  logger.info(`Version: ${version2}`);
@@ -4685,7 +4882,11 @@ async function publishCommand(options) {
4685
4882
  themeId,
4686
4883
  name: pkg.displayName || themeId,
4687
4884
  description: pkg.description || "",
4688
- email: tokens.user.email
4885
+ email: tokens.user.email,
4886
+ author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name || tokens.user.name || "",
4887
+ category: pkg.onex?.category || "MINIMAL",
4888
+ tags: pkg.keywords || [],
4889
+ thumbnail_url: pkg.onex?.thumbnail || ""
4689
4890
  })
4690
4891
  }
4691
4892
  );
@@ -4702,6 +4903,38 @@ async function publishCommand(options) {
4702
4903
  logger.error(error instanceof Error ? error.message : "Connection failed");
4703
4904
  process.exit(1);
4704
4905
  }
4906
+ logger.startSpinner("Checking version availability...");
4907
+ try {
4908
+ const checkResponse = await authenticatedFetch(
4909
+ `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/exists`,
4910
+ { method: "GET" }
4911
+ );
4912
+ const checkData = await checkResponse.json();
4913
+ const checkBody = checkData.statusCode ? checkData.body : checkData;
4914
+ if (checkBody.exists) {
4915
+ logger.stopSpinner(false, "Version already published");
4916
+ const patchVer = semver.inc(version2, "patch") || "?";
4917
+ const minorVer = semver.inc(version2, "minor") || "?";
4918
+ const majorVer = semver.inc(version2, "major") || "?";
4919
+ logger.error(
4920
+ `
4921
+ Version ${version2} of "${themeId}" is already published and cannot be overwritten.
4922
+
4923
+ To publish a new version:
4924
+ 1. Bump version in package.json (e.g., ${version2} -> ${patchVer})
4925
+ 2. Run: onexthm publish
4926
+
4927
+ Or use the --bump flag:
4928
+ onexthm publish --bump patch (${version2} -> ${patchVer})
4929
+ onexthm publish --bump minor (${version2} -> ${minorVer})
4930
+ onexthm publish --bump major (${version2} -> ${majorVer})`
4931
+ );
4932
+ process.exit(1);
4933
+ }
4934
+ logger.stopSpinner(true, `Version ${version2} is available`);
4935
+ } catch (error) {
4936
+ logger.stopSpinner(true, "Version check skipped (endpoint not available)");
4937
+ }
4705
4938
  logger.startSpinner("Building theme...");
4706
4939
  try {
4707
4940
  const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
@@ -4747,13 +4980,13 @@ async function publishCommand(options) {
4747
4980
  try {
4748
4981
  const archiver3 = await import('archiver');
4749
4982
  const { createWriteStream } = await import('fs');
4750
- const distDir = path8.join(themePath, "dist");
4983
+ const distDir = path10.join(themePath, "dist");
4751
4984
  if (!fs.existsSync(distDir)) {
4752
4985
  logger.stopSpinner(false, "No dist/ directory");
4753
4986
  logger.error("Build the theme first: onexthm build");
4754
4987
  process.exit(1);
4755
4988
  }
4756
- const bundleZipPath = path8.join(themePath, "dist", "bundle.zip");
4989
+ const bundleZipPath = path10.join(themePath, "dist", "bundle.zip");
4757
4990
  await createZip(distDir, bundleZipPath, [
4758
4991
  "bundle.zip",
4759
4992
  "source.zip",
@@ -4778,7 +5011,7 @@ async function publishCommand(options) {
4778
5011
  }
4779
5012
  logger.startSpinner("Uploading source...");
4780
5013
  try {
4781
- const sourceZipPath = path8.join(themePath, "dist", "source.zip");
5014
+ const sourceZipPath = path10.join(themePath, "dist", "source.zip");
4782
5015
  await createZip(themePath, sourceZipPath, [
4783
5016
  "node_modules",
4784
5017
  "dist",
@@ -4866,14 +5099,14 @@ async function createZip(sourceDir, outputPath, exclude) {
4866
5099
  try {
4867
5100
  const projectRoot = getProjectRoot();
4868
5101
  dotenv.config({
4869
- path: path8.join(projectRoot, ".env.local"),
5102
+ path: path10.join(projectRoot, ".env.local"),
4870
5103
  quiet: true
4871
5104
  });
4872
- dotenv.config({ path: path8.join(projectRoot, ".env"), quiet: true });
5105
+ dotenv.config({ path: path10.join(projectRoot, ".env"), quiet: true });
4873
5106
  } catch {
4874
5107
  }
4875
5108
  dotenv.config({
4876
- path: path8.join(os3.homedir(), ".onexthm", ".env"),
5109
+ path: path10.join(os.homedir(), ".onexthm", ".env"),
4877
5110
  quiet: true
4878
5111
  });
4879
5112
  var require2 = createRequire(import.meta.url);
@@ -4933,7 +5166,10 @@ program.command("config").description("Configure OneX CLI credentials (AWS, API
4933
5166
  program.command("login").description("Login to OneX platform").action(loginCommand);
4934
5167
  program.command("logout").description("Logout from OneX platform").action(logoutCommand);
4935
5168
  program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
4936
- program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").action(publishCommand);
5169
+ program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").option(
5170
+ "--bump <type>",
5171
+ "Auto-bump version before publish (patch|minor|major)"
5172
+ ).action(publishCommand);
4937
5173
  program.configureOutput({
4938
5174
  writeErr: (str) => process.stderr.write(chalk4.red(str))
4939
5175
  });