@onexapis/cli 1.1.37 → 1.1.39

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.
Files changed (34) hide show
  1. package/dist/cli.js +639 -401
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +633 -396
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/index.js +255 -203
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +252 -200
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +6 -4
  10. package/templates/default/CLAUDE.md +334 -1
  11. package/templates/default/esbuild.config.js +20 -0
  12. package/templates/default/pages/about.ts +2 -2
  13. package/templates/default/pages/forgot-password.ts +1 -3
  14. package/templates/default/pages/home.ts +4 -4
  15. package/templates/default/pages/login.ts +1 -3
  16. package/templates/default/pages/profile.ts +2 -2
  17. package/templates/default/pages/register.ts +1 -3
  18. package/templates/default/pages/showcase.ts +7 -7
  19. package/templates/default/pages/verify-code.ts +1 -3
  20. package/templates/default/sections/about/about.schema.ts +1 -1
  21. package/templates/default/sections/auth-forgot-password/auth-forgot-password.schema.ts +1 -1
  22. package/templates/default/sections/auth-login/auth-login.schema.ts +1 -1
  23. package/templates/default/sections/auth-register/auth-register.schema.ts +1 -1
  24. package/templates/default/sections/auth-verify-code/auth-verify-code.schema.ts +1 -1
  25. package/templates/default/sections/cta/cta.schema.ts +1 -1
  26. package/templates/default/sections/features/features.schema.ts +1 -1
  27. package/templates/default/sections/footer/footer.schema.ts +1 -1
  28. package/templates/default/sections/gallery/gallery.schema.ts +1 -1
  29. package/templates/default/sections/header/header.schema.ts +1 -1
  30. package/templates/default/sections/hero/hero.schema.ts +1 -1
  31. package/templates/default/sections/profile/profile.schema.ts +1 -1
  32. package/templates/default/sections/stats/stats.schema.ts +1 -1
  33. package/templates/default/sections/testimonials/testimonials.schema.ts +1 -1
  34. package/templates/default/theme.layout.ts +18 -0
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 path9 from 'path';
6
+ import fs8 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
14
  import { execSync, spawn } from 'child_process';
15
15
  import { Command } from 'commander';
16
- import fs2 from 'fs';
16
+ import fs3 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
+ path9.join(themePath, "sections/**/*.{ts,tsx}"),
109
+ path9.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 fs8.writeFile(path9.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 = path9.join(dir, "node_modules", relativePath);
130
131
  try {
131
- await fs7.access(candidate);
132
+ await fs8.access(candidate);
132
133
  return candidate;
133
134
  } catch {
134
- const parent = path8.dirname(dir);
135
+ const parent = path9.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 fs8.readFile(path9.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
+ path9.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
+ path9.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 fs8.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(path9.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(path9.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 = path9.join(themePath, "sections");
467
468
  try {
468
- const sectionDirs = await fs7.readdir(sectionsDir);
469
+ const sectionDirs = await fs8.readdir(sectionsDir);
469
470
  for (const dir of sectionDirs) {
470
- const schemaFile = path8.join(sectionsDir, dir, `${dir}.schema.ts`);
471
+ const schemaFile = path9.join(sectionsDir, dir, `${dir}.schema.ts`);
471
472
  try {
472
- await fs7.access(schemaFile);
473
+ await fs8.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 = path9.join(themePath, "pages");
485
486
  try {
486
- const files = await fs7.readdir(pagesDir);
487
+ const files = await fs8.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(path9.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 fs8.writeFile(
521
+ path9.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 = path9.join(outputDir, "bundle-entry.js");
545
+ const mapPath = path9.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 fs8.unlink(path9.join(outputDir, f));
548
549
  }
549
550
  let entryContent;
550
551
  try {
551
- entryContent = await fs7.readFile(entryPath, "utf-8");
552
+ entryContent = await fs8.readFile(entryPath, "utf-8");
552
553
  } catch {
553
- const indexPath = path8.join(outputDir, "index.js");
554
+ const indexPath = path9.join(outputDir, "index.js");
554
555
  try {
555
- entryContent = await fs7.readFile(indexPath, "utf-8");
556
+ entryContent = await fs8.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 = path9.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 fs8.writeFile(path9.join(outputDir, hashedName2), entryContent);
570
+ await fs8.unlink(indexPath);
570
571
  try {
571
- await fs7.access(indexMapPath);
572
- await fs7.rename(indexMapPath, path8.join(outputDir, hashedMapName2));
572
+ await fs8.access(indexMapPath);
573
+ await fs8.rename(indexMapPath, path9.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 fs8.writeFile(path9.join(outputDir, hashedName), entryContent);
587
+ await fs8.unlink(entryPath);
587
588
  try {
588
- await fs7.access(mapPath);
589
- await fs7.rename(mapPath, path8.join(outputDir, hashedMapName));
589
+ await fs8.access(mapPath);
590
+ await fs8.rename(mapPath, path9.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(path9.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 fs8.readFile(
622
+ path9.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 fs8.access(path9.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 fs8.writeFile(
682
+ path9.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 = path9.join(themePath, "dist");
688
+ const bundleEntry = path9.join(themePath, "bundle-entry.ts");
689
+ const indexEntry = path9.join(themePath, "index.ts");
689
690
  let entryPoint = indexEntry;
690
691
  try {
691
- await fs7.access(bundleEntry);
692
+ await fs8.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 = path9.join(outputDir, ".process-shim.js");
697
+ await fs8.mkdir(outputDir, { recursive: true });
698
+ await fs8.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 fs8.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 fs8.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 = path9.join(themePath, "dist");
776
+ const bundleEntry = path9.join(themePath, "bundle-entry.ts");
777
+ const indexEntry = path9.join(themePath, "index.ts");
777
778
  let entryPoint = indexEntry;
778
779
  try {
779
- await fs7.access(bundleEntry);
780
+ await fs8.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 = path9.join(outputDir, ".process-shim.js");
785
+ await fs8.mkdir(outputDir, { recursive: true });
786
+ await fs8.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 = path9.join(themePath, "dist");
840
+ await fs8.mkdir(outputDir, { recursive: true });
841
+ const outputPath = path9.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
+ path9.join(__dirname, "..", "preview", "preview-app.tsx"),
844
+ path9.join(__dirname, "preview", "preview-app.tsx"),
845
+ path9.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 fs8.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
+ path9.join(themePath, "node_modules", "@onexapis", "core", "src"),
934
+ path9.join(themePath, "..", "..", "packages", "core", "src"),
934
935
  // monorepo sibling
935
- path8.join(
936
+ path9.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 fs8.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
+ path9.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
+ path9.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 fs8.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 fs8.readFile(
984
+ path9.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 = path9.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 = path9.join(outputDir, ".process-shim-preview.js");
1139
+ await fs8.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 fs8.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(path9.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
+ path9.join(__dirname, "../../templates"),
1312
1313
  // Development
1313
- path8.join(__dirname, "../templates"),
1314
+ path9.join(__dirname, "../templates"),
1314
1315
  // Production (dist/)
1315
- path8.join(process.cwd(), "templates"),
1316
+ path9.join(process.cwd(), "templates"),
1316
1317
  // Fallback
1317
- path8.join(process.cwd(), "packages/cli/templates")
1318
+ path9.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 = path9.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 = path9.join(templateDir, file);
1340
+ const targetPath = path9.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 = path9.join(templateDir, file);
1358
+ const targetPath = path9.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 !== path9.parse(currentDir).root) {
1374
+ const packageJsonPath = path9.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(path9.join(currentDir, "src/themes")) || fs.existsSync(path9.join(currentDir, "themes"))) {
1377
1378
  return currentDir;
1378
1379
  }
1379
1380
  }
1380
- currentDir = path8.dirname(currentDir);
1381
+ currentDir = path9.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(path9.join(root, "themes")))
1388
+ return path9.join(root, "themes");
1389
+ if (fs.existsSync(path9.join(root, "src/themes")))
1390
+ return path9.join(root, "src/themes");
1391
+ return path9.dirname(root);
1391
1392
  }
1392
1393
  function getFeaturesDir() {
1393
- return path8.join(getProjectRoot(), "src/features");
1394
+ return path9.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(path9.join(root, "themes")) || fs.existsSync(path9.join(root, "src/themes")) || fs.existsSync(path9.join(root, "theme.config.ts")) || fs.existsSync(path9.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 = path9.join(themesDir, name);
1415
+ return fs.statSync(themePath).isDirectory() && (fs.existsSync(path9.join(themePath, "theme.config.ts")) || fs.existsSync(path9.join(themePath, "bundle-entry.ts")) || fs.existsSync(path9.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 = path9.join(getThemesDir(), themeName);
1420
+ return fs.existsSync(themePath) && (fs.existsSync(path9.join(themePath, "theme.config.ts")) || fs.existsSync(path9.join(themePath, "bundle-entry.ts")) || fs.existsSync(path9.join(themePath, "manifest.ts")));
1420
1421
  }
1421
1422
  function detectPackageManager() {
1422
1423
  const userAgent = process.env.npm_config_user_agent || "";
@@ -1424,9 +1425,9 @@ 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(path9.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
1429
+ if (fs.existsSync(path9.join(cwd, "yarn.lock"))) return "yarn";
1430
+ if (fs.existsSync(path9.join(cwd, "bun.lockb"))) return "bun";
1430
1431
  return "npm";
1431
1432
  }
1432
1433
  async function installDependencies(projectPath, packageManager = "npm") {
@@ -1475,6 +1476,120 @@ function getValidCategories() {
1475
1476
  "contact"
1476
1477
  ];
1477
1478
  }
1479
+ var AUTH_DIR = path9.join(os.homedir(), ".onexthm");
1480
+ var AUTH_FILE = path9.join(AUTH_DIR, "auth.json");
1481
+ function getApiUrl() {
1482
+ return process.env.ONEXTHM_API_URL || process.env.NEXT_PUBLIC_API_URL || "https://platform-dev.onexeos.com";
1483
+ }
1484
+ async function saveAuthTokens(tokens) {
1485
+ await fs.ensureDir(AUTH_DIR);
1486
+ const key = getMachineKey();
1487
+ const data = JSON.stringify(tokens);
1488
+ const encrypted = encrypt(data, key);
1489
+ await fs.writeFile(AUTH_FILE, encrypted, "utf-8");
1490
+ }
1491
+ function loadAuthTokens() {
1492
+ try {
1493
+ if (!fs.existsSync(AUTH_FILE)) return null;
1494
+ const encrypted = fs.readFileSync(AUTH_FILE, "utf-8");
1495
+ const key = getMachineKey();
1496
+ const data = decrypt(encrypted, key);
1497
+ return JSON.parse(data);
1498
+ } catch {
1499
+ return null;
1500
+ }
1501
+ }
1502
+ async function clearAuthTokens() {
1503
+ try {
1504
+ await fs.remove(AUTH_FILE);
1505
+ } catch {
1506
+ }
1507
+ }
1508
+ function isTokenExpired(tokens) {
1509
+ return Date.now() / 1e3 > tokens.expiresAt - 60;
1510
+ }
1511
+ async function getValidTokens() {
1512
+ const tokens = loadAuthTokens();
1513
+ if (!tokens) return null;
1514
+ if (!isTokenExpired(tokens)) return tokens;
1515
+ try {
1516
+ const apiUrl = getApiUrl();
1517
+ const response = await fetch(`${apiUrl}/auth/refresh`, {
1518
+ method: "POST",
1519
+ headers: { "Content-Type": "application/json" },
1520
+ body: JSON.stringify({ refresh_token: tokens.refreshToken })
1521
+ });
1522
+ if (!response.ok) {
1523
+ await clearAuthTokens();
1524
+ return null;
1525
+ }
1526
+ const data = await response.json();
1527
+ const body = data.statusCode ? data.body : data;
1528
+ const refreshed = {
1529
+ ...tokens,
1530
+ accessToken: body.AccessToken || tokens.accessToken,
1531
+ idToken: body.IdToken || tokens.idToken,
1532
+ expiresAt: Math.floor(Date.now() / 1e3) + (body.ExpiresIn || 3600)
1533
+ };
1534
+ await saveAuthTokens(refreshed);
1535
+ return refreshed;
1536
+ } catch {
1537
+ await clearAuthTokens();
1538
+ return null;
1539
+ }
1540
+ }
1541
+ async function authenticatedFetch(url, init) {
1542
+ const tokens = await getValidTokens();
1543
+ if (!tokens) {
1544
+ throw new Error("Not logged in. Run: onexthm login");
1545
+ }
1546
+ const headers = new Headers(init?.headers);
1547
+ headers.set("Authorization", `Bearer ${tokens.idToken}`);
1548
+ headers.set("Content-Type", "application/json");
1549
+ return fetch(url, { ...init, headers });
1550
+ }
1551
+ function getMachineKey() {
1552
+ let seed;
1553
+ if (process.platform === "darwin") {
1554
+ seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
1555
+ } else if (process.platform === "linux") {
1556
+ try {
1557
+ seed = `onexthm:${fs.readFileSync("/etc/machine-id", "utf-8").trim()}`;
1558
+ } catch {
1559
+ seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
1560
+ }
1561
+ } else {
1562
+ seed = `onexthm:${os.hostname()}:${os.userInfo().username}`;
1563
+ }
1564
+ return crypto.createHash("sha256").update(seed).digest();
1565
+ }
1566
+ function encrypt(text, key) {
1567
+ const iv = crypto.randomBytes(16);
1568
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
1569
+ let encrypted = cipher.update(text, "utf-8", "hex");
1570
+ encrypted += cipher.final("hex");
1571
+ const tag = cipher.getAuthTag();
1572
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted}`;
1573
+ }
1574
+ function decrypt(text, key) {
1575
+ const [ivHex, tagHex, encrypted] = text.split(":");
1576
+ const iv = Buffer.from(ivHex, "hex");
1577
+ const tag = Buffer.from(tagHex, "hex");
1578
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
1579
+ decipher.setAuthTag(tag);
1580
+ let decrypted = decipher.update(encrypted, "hex", "utf-8");
1581
+ decrypted += decipher.final("utf-8");
1582
+ return decrypted;
1583
+ }
1584
+ function parseJwtClaims(idToken) {
1585
+ try {
1586
+ const payload = idToken.split(".")[1];
1587
+ const decoded = Buffer.from(payload, "base64url").toString("utf-8");
1588
+ return JSON.parse(decoded);
1589
+ } catch {
1590
+ return {};
1591
+ }
1592
+ }
1478
1593
 
1479
1594
  // src/commands/init.ts
1480
1595
  async function initCommand(projectName, options = {}) {
@@ -1492,7 +1607,7 @@ async function initCommand(projectName, options = {}) {
1492
1607
  if (!validateThemeName(kebabName)) {
1493
1608
  return "Invalid project name. Use lowercase letters, numbers, and hyphens only.";
1494
1609
  }
1495
- if (fs2.existsSync(path8.join(process.cwd(), kebabName))) {
1610
+ if (fs3.existsSync(path9.join(process.cwd(), kebabName))) {
1496
1611
  return `Directory "${kebabName}" already exists`;
1497
1612
  }
1498
1613
  return true;
@@ -1503,11 +1618,46 @@ async function initCommand(projectName, options = {}) {
1503
1618
  } else {
1504
1619
  name = toKebabCase(projectName);
1505
1620
  }
1506
- const projectPath = path8.join(process.cwd(), name);
1507
- if (fs2.existsSync(projectPath)) {
1621
+ const projectPath = path9.join(process.cwd(), name);
1622
+ if (fs3.existsSync(projectPath)) {
1508
1623
  logger.error(`Directory "${name}" already exists.`);
1509
1624
  process.exit(1);
1510
1625
  }
1626
+ if (!options.yes) {
1627
+ try {
1628
+ const apiUrl = getApiUrl();
1629
+ const controller = new AbortController();
1630
+ const timeout = setTimeout(() => controller.abort(), 3e3);
1631
+ const response = await fetch(
1632
+ `${apiUrl}/website-api/themes/${encodeURIComponent(name)}/exists`,
1633
+ { signal: controller.signal }
1634
+ );
1635
+ clearTimeout(timeout);
1636
+ if (response.ok) {
1637
+ const data2 = await response.json();
1638
+ const body = data2.statusCode ? data2.body : data2;
1639
+ if (body.exists && body.owner === "other") {
1640
+ logger.warning(
1641
+ `Theme ID "${name}" is already registered by another developer.
1642
+ You can still create this locally, but publishing will fail.
1643
+ Consider using a different name.`
1644
+ );
1645
+ const { proceed } = await inquirer.prompt([
1646
+ {
1647
+ type: "confirm",
1648
+ name: "proceed",
1649
+ message: "Continue anyway?",
1650
+ default: false
1651
+ }
1652
+ ]);
1653
+ if (!proceed) {
1654
+ process.exit(0);
1655
+ }
1656
+ }
1657
+ }
1658
+ } catch {
1659
+ }
1660
+ }
1511
1661
  let displayName;
1512
1662
  let description;
1513
1663
  let author;
@@ -1575,7 +1725,7 @@ async function initCommand(projectName, options = {}) {
1575
1725
  }
1576
1726
  logger.startSpinner("Creating project structure...");
1577
1727
  try {
1578
- fs2.mkdirSync(projectPath, { recursive: true });
1728
+ fs3.mkdirSync(projectPath, { recursive: true });
1579
1729
  await copyTemplate(template, projectPath, data);
1580
1730
  await renameThemeInFiles(
1581
1731
  projectPath,
@@ -1584,9 +1734,9 @@ async function initCommand(projectName, options = {}) {
1584
1734
  description,
1585
1735
  author
1586
1736
  );
1587
- const mcpJsonPath = path8.join(projectPath, ".mcp.json");
1588
- if (fs2.existsSync(mcpJsonPath)) {
1589
- let mcpContent = fs2.readFileSync(mcpJsonPath, "utf-8");
1737
+ const mcpJsonPath = path9.join(projectPath, ".mcp.json");
1738
+ if (fs3.existsSync(mcpJsonPath)) {
1739
+ let mcpContent = fs3.readFileSync(mcpJsonPath, "utf-8");
1590
1740
  if (figmaApiKey) {
1591
1741
  mcpContent = mcpContent.replace("__FIGMA_API_KEY__", figmaApiKey);
1592
1742
  } else {
@@ -1597,7 +1747,7 @@ async function initCommand(projectName, options = {}) {
1597
1747
  } catch {
1598
1748
  }
1599
1749
  }
1600
- fs2.writeFileSync(mcpJsonPath, mcpContent, "utf-8");
1750
+ fs3.writeFileSync(mcpJsonPath, mcpContent, "utf-8");
1601
1751
  }
1602
1752
  logger.stopSpinner(true, "Project structure created!");
1603
1753
  if (options.git) {
@@ -1657,16 +1807,16 @@ async function initCommand(projectName, options = {}) {
1657
1807
  logger.error(
1658
1808
  error instanceof Error ? error.message : "Unknown error occurred"
1659
1809
  );
1660
- if (fs2.existsSync(projectPath)) {
1661
- fs2.rmSync(projectPath, { recursive: true, force: true });
1810
+ if (fs3.existsSync(projectPath)) {
1811
+ fs3.rmSync(projectPath, { recursive: true, force: true });
1662
1812
  }
1663
1813
  process.exit(1);
1664
1814
  }
1665
1815
  }
1666
1816
  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");
1817
+ const configPath = path9.join(projectPath, "theme.config.ts");
1818
+ if (fs3.existsSync(configPath)) {
1819
+ let content = fs3.readFileSync(configPath, "utf-8");
1670
1820
  content = content.replace(
1671
1821
  /name: "My Simple Theme"/,
1672
1822
  `name: "${displayName}"`
@@ -1675,11 +1825,11 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
1675
1825
  /description: ".*?"/,
1676
1826
  `description: "${description}"`
1677
1827
  );
1678
- fs2.writeFileSync(configPath, content, "utf-8");
1828
+ fs3.writeFileSync(configPath, content, "utf-8");
1679
1829
  }
1680
- const pkgPath = path8.join(projectPath, "package.json");
1681
- if (fs2.existsSync(pkgPath)) {
1682
- let content = fs2.readFileSync(pkgPath, "utf-8");
1830
+ const pkgPath = path9.join(projectPath, "package.json");
1831
+ if (fs3.existsSync(pkgPath)) {
1832
+ let content = fs3.readFileSync(pkgPath, "utf-8");
1683
1833
  content = content.replace(
1684
1834
  /@onex-themes\/my-simple/g,
1685
1835
  `@onex-themes/${themeName}`
@@ -1688,7 +1838,7 @@ async function renameThemeInFiles(projectPath, themeName, displayName, descripti
1688
1838
  /"description": ".*?"/,
1689
1839
  `"description": "${description}"`
1690
1840
  );
1691
- fs2.writeFileSync(pkgPath, content, "utf-8");
1841
+ fs3.writeFileSync(pkgPath, content, "utf-8");
1692
1842
  }
1693
1843
  }
1694
1844
 
@@ -1699,10 +1849,10 @@ async function createSectionCommand(name, options) {
1699
1849
  ensureOneXProject();
1700
1850
  if (!options.theme) {
1701
1851
  const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
1702
- (f) => fs.existsSync(path8.join(process.cwd(), f))
1852
+ (f) => fs.existsSync(path9.join(process.cwd(), f))
1703
1853
  );
1704
1854
  if (isStandaloneTheme) {
1705
- options.theme = path8.basename(process.cwd());
1855
+ options.theme = path9.basename(process.cwd());
1706
1856
  }
1707
1857
  }
1708
1858
  const sectionName = toKebabCase(name);
@@ -1765,35 +1915,35 @@ async function createSectionCommand(name, options) {
1765
1915
  };
1766
1916
  logger.startSpinner("Creating section files...");
1767
1917
  try {
1768
- const themePath = path8.join(getThemesDir(), themeName);
1769
- const sectionPath = path8.join(themePath, "sections", sectionName);
1918
+ const themePath = path9.join(getThemesDir(), themeName);
1919
+ const sectionPath = path9.join(themePath, "sections", sectionName);
1770
1920
  const schemaContent = generateSectionSchema(data);
1771
1921
  await writeFile(
1772
- path8.join(sectionPath, `${sectionName}.schema.ts`),
1922
+ path9.join(sectionPath, `${sectionName}.schema.ts`),
1773
1923
  schemaContent
1774
1924
  );
1775
1925
  if (createTemplate) {
1776
1926
  const templateContent = generateSectionTemplate(data);
1777
1927
  await writeFile(
1778
- path8.join(sectionPath, `${sectionName}-default.tsx`),
1928
+ path9.join(sectionPath, `${sectionName}-default.tsx`),
1779
1929
  templateContent
1780
1930
  );
1781
1931
  }
1782
1932
  const indexContent = generateSectionIndex(data, createTemplate);
1783
- await writeFile(path8.join(sectionPath, "index.ts"), indexContent);
1933
+ await writeFile(path9.join(sectionPath, "index.ts"), indexContent);
1784
1934
  logger.stopSpinner(true, "Section files created successfully!");
1785
1935
  logger.newLine();
1786
1936
  logger.section("Next steps:");
1787
1937
  logger.log(
1788
- ` 1. Edit schema: ${path8.relative(process.cwd(), path8.join(sectionPath, `${sectionName}.schema.ts`))}`
1938
+ ` 1. Edit schema: ${path9.relative(process.cwd(), path9.join(sectionPath, `${sectionName}.schema.ts`))}`
1789
1939
  );
1790
1940
  if (createTemplate) {
1791
1941
  logger.log(
1792
- ` 2. Edit template: ${path8.relative(process.cwd(), path8.join(sectionPath, `${sectionName}-default.tsx`))}`
1942
+ ` 2. Edit template: ${path9.relative(process.cwd(), path9.join(sectionPath, `${sectionName}-default.tsx`))}`
1793
1943
  );
1794
1944
  }
1795
1945
  logger.log(
1796
- ` 3. Add to theme manifest: ${path8.relative(process.cwd(), path8.join(themePath, "manifest.ts"))}`
1946
+ ` 3. Add to theme manifest: ${path9.relative(process.cwd(), path9.join(themePath, "manifest.ts"))}`
1797
1947
  );
1798
1948
  logger.newLine();
1799
1949
  logger.success("Section created successfully!");
@@ -1941,10 +2091,10 @@ async function createBlockCommand(name, options) {
1941
2091
  ensureOneXProject();
1942
2092
  if (!options.theme) {
1943
2093
  const isStandaloneTheme = ["theme.config.ts", "bundle-entry.ts"].some(
1944
- (f) => fs.existsSync(path8.join(process.cwd(), f))
2094
+ (f) => fs.existsSync(path9.join(process.cwd(), f))
1945
2095
  );
1946
2096
  if (isStandaloneTheme) {
1947
- options.theme = path8.basename(process.cwd());
2097
+ options.theme = path9.basename(process.cwd());
1948
2098
  }
1949
2099
  }
1950
2100
  const blockName = toKebabCase(name);
@@ -2019,24 +2169,24 @@ async function createBlockCommand(name, options) {
2019
2169
  };
2020
2170
  logger.startSpinner("Creating block files...");
2021
2171
  try {
2022
- const blockPath = scope === "shared" ? path8.join(getFeaturesDir(), "blocks", blockName) : path8.join(getThemesDir(), themeName, "blocks", blockName);
2172
+ const blockPath = scope === "shared" ? path9.join(getFeaturesDir(), "blocks", blockName) : path9.join(getThemesDir(), themeName, "blocks", blockName);
2023
2173
  const schemaContent = generateBlockSchema(data);
2024
2174
  await writeFile(
2025
- path8.join(blockPath, `${blockName}.schema.ts`),
2175
+ path9.join(blockPath, `${blockName}.schema.ts`),
2026
2176
  schemaContent
2027
2177
  );
2028
2178
  const componentContent = generateBlockComponent(data);
2029
- await writeFile(path8.join(blockPath, `${blockName}.tsx`), componentContent);
2179
+ await writeFile(path9.join(blockPath, `${blockName}.tsx`), componentContent);
2030
2180
  const indexContent = generateBlockIndex(data);
2031
- await writeFile(path8.join(blockPath, "index.ts"), indexContent);
2181
+ await writeFile(path9.join(blockPath, "index.ts"), indexContent);
2032
2182
  logger.stopSpinner(true, "Block files created successfully!");
2033
2183
  logger.newLine();
2034
2184
  logger.section("Next steps:");
2035
2185
  logger.log(
2036
- ` 1. Edit schema: ${path8.relative(process.cwd(), path8.join(blockPath, `${blockName}.schema.ts`))}`
2186
+ ` 1. Edit schema: ${path9.relative(process.cwd(), path9.join(blockPath, `${blockName}.schema.ts`))}`
2037
2187
  );
2038
2188
  logger.log(
2039
- ` 2. Edit component: ${path8.relative(process.cwd(), path8.join(blockPath, `${blockName}.tsx`))}`
2189
+ ` 2. Edit component: ${path9.relative(process.cwd(), path9.join(blockPath, `${blockName}.tsx`))}`
2040
2190
  );
2041
2191
  logger.log(
2042
2192
  ` 3. Register in block registry: src/lib/registry/block-registry.ts`
@@ -2214,31 +2364,31 @@ async function createComponentCommand(name, options) {
2214
2364
  };
2215
2365
  logger.startSpinner("Creating component files...");
2216
2366
  try {
2217
- const componentPath = path8.join(
2367
+ const componentPath = path9.join(
2218
2368
  getFeaturesDir(),
2219
2369
  "components",
2220
2370
  componentName
2221
2371
  );
2222
2372
  const schemaContent = generateComponentSchema(data);
2223
2373
  await writeFile(
2224
- path8.join(componentPath, `${componentName}.schema.ts`),
2374
+ path9.join(componentPath, `${componentName}.schema.ts`),
2225
2375
  schemaContent
2226
2376
  );
2227
2377
  const componentContent = generateComponent(data);
2228
2378
  await writeFile(
2229
- path8.join(componentPath, `${componentName}.tsx`),
2379
+ path9.join(componentPath, `${componentName}.tsx`),
2230
2380
  componentContent
2231
2381
  );
2232
2382
  const indexContent = generateComponentIndex(data);
2233
- await writeFile(path8.join(componentPath, "index.ts"), indexContent);
2383
+ await writeFile(path9.join(componentPath, "index.ts"), indexContent);
2234
2384
  logger.stopSpinner(true, "Component files created successfully!");
2235
2385
  logger.newLine();
2236
2386
  logger.section("Next steps:");
2237
2387
  logger.log(
2238
- ` 1. Edit schema: ${path8.relative(process.cwd(), path8.join(componentPath, `${componentName}.schema.ts`))}`
2388
+ ` 1. Edit schema: ${path9.relative(process.cwd(), path9.join(componentPath, `${componentName}.schema.ts`))}`
2239
2389
  );
2240
2390
  logger.log(
2241
- ` 2. Edit component: ${path8.relative(process.cwd(), path8.join(componentPath, `${componentName}.tsx`))}`
2391
+ ` 2. Edit component: ${path9.relative(process.cwd(), path9.join(componentPath, `${componentName}.tsx`))}`
2242
2392
  );
2243
2393
  logger.log(
2244
2394
  ` 3. Register in component registry: src/lib/registry/component-registry.ts`
@@ -2395,13 +2545,13 @@ async function listSections(themeFilter) {
2395
2545
  return;
2396
2546
  }
2397
2547
  for (const theme of themes) {
2398
- const sectionsDir = path8.join(getThemesDir(), theme, "sections");
2548
+ const sectionsDir = path9.join(getThemesDir(), theme, "sections");
2399
2549
  if (!fs.existsSync(sectionsDir)) {
2400
2550
  continue;
2401
2551
  }
2402
2552
  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"));
2553
+ const sectionPath = path9.join(sectionsDir, name);
2554
+ return fs.statSync(sectionPath).isDirectory() && fs.existsSync(path9.join(sectionPath, "index.ts"));
2405
2555
  });
2406
2556
  if (sections.length > 0) {
2407
2557
  logger.log(chalk4.cyan(`
@@ -2415,11 +2565,11 @@ async function listSections(themeFilter) {
2415
2565
  }
2416
2566
  async function listBlocks(themeFilter) {
2417
2567
  logger.section("\u{1F9F1} Blocks");
2418
- const sharedBlocksDir = path8.join(getFeaturesDir(), "blocks");
2568
+ const sharedBlocksDir = path9.join(getFeaturesDir(), "blocks");
2419
2569
  if (fs.existsSync(sharedBlocksDir)) {
2420
2570
  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"));
2571
+ const blockPath = path9.join(sharedBlocksDir, name);
2572
+ return fs.statSync(blockPath).isDirectory() && fs.existsSync(path9.join(blockPath, "index.ts"));
2423
2573
  });
2424
2574
  if (sharedBlocks.length > 0) {
2425
2575
  logger.log(chalk4.cyan("\n Shared:"));
@@ -2430,13 +2580,13 @@ async function listBlocks(themeFilter) {
2430
2580
  }
2431
2581
  const themes = themeFilter ? [themeFilter] : listThemes();
2432
2582
  for (const theme of themes) {
2433
- const blocksDir = path8.join(getThemesDir(), theme, "blocks");
2583
+ const blocksDir = path9.join(getThemesDir(), theme, "blocks");
2434
2584
  if (!fs.existsSync(blocksDir)) {
2435
2585
  continue;
2436
2586
  }
2437
2587
  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"));
2588
+ const blockPath = path9.join(blocksDir, name);
2589
+ return fs.statSync(blockPath).isDirectory() && fs.existsSync(path9.join(blockPath, "index.ts"));
2440
2590
  });
2441
2591
  if (blocks.length > 0) {
2442
2592
  logger.log(chalk4.cyan(`
@@ -2450,14 +2600,14 @@ async function listBlocks(themeFilter) {
2450
2600
  }
2451
2601
  async function listComponents() {
2452
2602
  logger.section("\u2699\uFE0F Components");
2453
- const componentsDir = path8.join(getFeaturesDir(), "components");
2603
+ const componentsDir = path9.join(getFeaturesDir(), "components");
2454
2604
  if (!fs.existsSync(componentsDir)) {
2455
2605
  logger.warning("No components directory found");
2456
2606
  return;
2457
2607
  }
2458
2608
  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"));
2609
+ const componentPath = path9.join(componentsDir, name);
2610
+ return fs.statSync(componentPath).isDirectory() && fs.existsSync(path9.join(componentPath, "index.ts"));
2461
2611
  });
2462
2612
  if (components.length === 0) {
2463
2613
  logger.warning("No components found");
@@ -2478,11 +2628,11 @@ async function listThemesInfo() {
2478
2628
  }
2479
2629
  logger.log("");
2480
2630
  for (const theme of themes) {
2481
- const themeDir = path8.join(getThemesDir(), theme);
2631
+ const themeDir = path9.join(getThemesDir(), theme);
2482
2632
  const candidates = ["theme.config.ts", "bundle-entry.ts", "manifest.ts"];
2483
2633
  let manifestContent = "";
2484
2634
  for (const candidate of candidates) {
2485
- const candidatePath = path8.join(themeDir, candidate);
2635
+ const candidatePath = path9.join(themeDir, candidate);
2486
2636
  if (fs.existsSync(candidatePath)) {
2487
2637
  manifestContent = fs.readFileSync(candidatePath, "utf-8");
2488
2638
  break;
@@ -2520,9 +2670,9 @@ async function validateCommand(options) {
2520
2670
  "theme.config.ts",
2521
2671
  "bundle-entry.ts",
2522
2672
  "manifest.ts"
2523
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
2673
+ ].some((f) => fs.existsSync(path9.join(process.cwd(), f)));
2524
2674
  if (isThemeDir) {
2525
- themeToValidate = path8.basename(process.cwd());
2675
+ themeToValidate = path9.basename(process.cwd());
2526
2676
  logger.info(`Validating current theme: ${themeToValidate}`);
2527
2677
  } else {
2528
2678
  logger.error(
@@ -2531,11 +2681,11 @@ async function validateCommand(options) {
2531
2681
  process.exit(1);
2532
2682
  }
2533
2683
  }
2534
- const themePath = path8.join(getThemesDir(), themeToValidate);
2684
+ const themePath = path9.join(getThemesDir(), themeToValidate);
2535
2685
  logger.startSpinner("Running validation checks...");
2536
2686
  const entryFiles = ["manifest.ts", "theme.config.ts", "bundle-entry.ts"];
2537
2687
  const foundEntry = entryFiles.find(
2538
- (f) => fs.existsSync(path8.join(themePath, f))
2688
+ (f) => fs.existsSync(path9.join(themePath, f))
2539
2689
  );
2540
2690
  if (!foundEntry) {
2541
2691
  issues.push({
@@ -2545,7 +2695,7 @@ async function validateCommand(options) {
2545
2695
  });
2546
2696
  } else if (foundEntry === "manifest.ts") {
2547
2697
  const manifestContent = fs.readFileSync(
2548
- path8.join(themePath, foundEntry),
2698
+ path9.join(themePath, foundEntry),
2549
2699
  "utf-8"
2550
2700
  );
2551
2701
  if (!manifestContent.includes("export const") && !manifestContent.includes("export default") && !manifestContent.includes("export interface")) {
@@ -2556,7 +2706,7 @@ async function validateCommand(options) {
2556
2706
  });
2557
2707
  }
2558
2708
  }
2559
- const configPath = path8.join(themePath, "theme.config.ts");
2709
+ const configPath = path9.join(themePath, "theme.config.ts");
2560
2710
  if (!fs.existsSync(configPath)) {
2561
2711
  issues.push({
2562
2712
  type: "warning",
@@ -2564,7 +2714,7 @@ async function validateCommand(options) {
2564
2714
  message: "Theme config file not found (recommended)"
2565
2715
  });
2566
2716
  }
2567
- const indexPath = path8.join(themePath, "index.ts");
2717
+ const indexPath = path9.join(themePath, "index.ts");
2568
2718
  if (!fs.existsSync(indexPath)) {
2569
2719
  issues.push({
2570
2720
  type: "warning",
@@ -2572,7 +2722,7 @@ async function validateCommand(options) {
2572
2722
  message: "Index file not found (recommended)"
2573
2723
  });
2574
2724
  }
2575
- const sectionsDir = path8.join(themePath, "sections");
2725
+ const sectionsDir = path9.join(themePath, "sections");
2576
2726
  if (!fs.existsSync(sectionsDir)) {
2577
2727
  issues.push({
2578
2728
  type: "warning",
@@ -2581,16 +2731,16 @@ async function validateCommand(options) {
2581
2731
  });
2582
2732
  } else {
2583
2733
  const sections = fs.readdirSync(sectionsDir).filter(
2584
- (name) => fs.statSync(path8.join(sectionsDir, name)).isDirectory()
2734
+ (name) => fs.statSync(path9.join(sectionsDir, name)).isDirectory()
2585
2735
  );
2586
2736
  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(
2737
+ const sectionPath = path9.join(sectionsDir, sectionName);
2738
+ const schemaFile = path9.join(sectionPath, `${sectionName}.schema.ts`);
2739
+ const defaultTemplate = path9.join(
2590
2740
  sectionPath,
2591
2741
  `${sectionName}-default.tsx`
2592
2742
  );
2593
- const indexFile = path8.join(sectionPath, "index.ts");
2743
+ const indexFile = path9.join(sectionPath, "index.ts");
2594
2744
  if (!fs.existsSync(schemaFile)) {
2595
2745
  issues.push({
2596
2746
  type: "error",
@@ -2614,14 +2764,14 @@ async function validateCommand(options) {
2614
2764
  }
2615
2765
  }
2616
2766
  }
2617
- const blocksDir = path8.join(themePath, "blocks");
2767
+ const blocksDir = path9.join(themePath, "blocks");
2618
2768
  if (fs.existsSync(blocksDir)) {
2619
- const blocks = fs.readdirSync(blocksDir).filter((name) => fs.statSync(path8.join(blocksDir, name)).isDirectory());
2769
+ const blocks = fs.readdirSync(blocksDir).filter((name) => fs.statSync(path9.join(blocksDir, name)).isDirectory());
2620
2770
  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");
2771
+ const blockPath = path9.join(blocksDir, blockName);
2772
+ const schemaFile = path9.join(blockPath, `${blockName}.schema.ts`);
2773
+ const componentFile = path9.join(blockPath, `${blockName}.tsx`);
2774
+ const indexFile = path9.join(blockPath, "index.ts");
2625
2775
  if (!fs.existsSync(schemaFile)) {
2626
2776
  issues.push({
2627
2777
  type: "error",
@@ -2647,13 +2797,13 @@ async function validateCommand(options) {
2647
2797
  }
2648
2798
  if (fs.existsSync(sectionsDir)) {
2649
2799
  const sections = fs.readdirSync(sectionsDir).filter(
2650
- (name) => fs.statSync(path8.join(sectionsDir, name)).isDirectory()
2800
+ (name) => fs.statSync(path9.join(sectionsDir, name)).isDirectory()
2651
2801
  );
2652
2802
  for (const sectionName of sections) {
2653
- const sectionPath = path8.join(sectionsDir, sectionName);
2803
+ const sectionPath = path9.join(sectionsDir, sectionName);
2654
2804
  const tsxFiles = fs.readdirSync(sectionPath).filter((f) => f.endsWith(".tsx") && !f.endsWith(".schema.ts"));
2655
2805
  for (const tsxFile of tsxFiles) {
2656
- const filePath = path8.join(sectionPath, tsxFile);
2806
+ const filePath = path9.join(sectionPath, tsxFile);
2657
2807
  const content = fs.readFileSync(filePath, "utf-8");
2658
2808
  const relPath = `sections/${sectionName}/${tsxFile}`;
2659
2809
  if (!content.includes('"use client"') && !content.includes("'use client'")) {
@@ -2701,23 +2851,46 @@ async function validateCommand(options) {
2701
2851
  }
2702
2852
  }
2703
2853
  }
2704
- const registryPath = path8.join(themePath, "sections-registry.ts");
2705
- const bundleEntryPath = path8.join(themePath, "bundle-entry.ts");
2854
+ const registryPath = path9.join(themePath, "sections-registry.ts");
2855
+ const bundleEntryPath = path9.join(themePath, "bundle-entry.ts");
2706
2856
  const registryContent = fs.existsSync(registryPath) ? fs.readFileSync(registryPath, "utf-8") : fs.existsSync(bundleEntryPath) ? fs.readFileSync(bundleEntryPath, "utf-8") : "";
2707
2857
  if (fs.existsSync(sectionsDir) && registryContent) {
2708
2858
  const sections = fs.readdirSync(sectionsDir).filter(
2709
- (name) => fs.statSync(path8.join(sectionsDir, name)).isDirectory()
2859
+ (name) => fs.statSync(path9.join(sectionsDir, name)).isDirectory()
2710
2860
  );
2711
2861
  for (const sectionName of sections) {
2712
2862
  if (!registryContent.includes(`sections/${sectionName}`) && !registryContent.includes(`"${sectionName}"`)) {
2713
2863
  issues.push({
2714
- type: "warning",
2864
+ type: "error",
2715
2865
  file: `sections/${sectionName}/`,
2716
- message: "Section not found in sections-registry.ts or bundle-entry.ts \u2014 may not be included in build"
2866
+ message: "Section not exported in sections-registry.ts or bundle-entry.ts \u2014 will not be included in build"
2717
2867
  });
2718
2868
  }
2719
2869
  }
2720
2870
  }
2871
+ if (fs.existsSync(sectionsDir)) {
2872
+ const schemaTypes = await loadSchemaTypes(themePath, sectionsDir);
2873
+ for (const { folderName, schemaType } of schemaTypes) {
2874
+ if (schemaType && schemaType !== folderName) {
2875
+ issues.push({
2876
+ type: "error",
2877
+ file: `sections/${folderName}/${folderName}.schema.ts`,
2878
+ message: `Schema type "${schemaType}" doesn't match folder name "${folderName}". Rename folder to "${schemaType}/" or change schema type to "${folderName}".`
2879
+ });
2880
+ }
2881
+ }
2882
+ const pagesDir = path9.join(themePath, "pages");
2883
+ if (fs.existsSync(pagesDir)) {
2884
+ const allSchemaTypeSet = new Set(
2885
+ schemaTypes.map((s) => s.schemaType || s.folderName)
2886
+ );
2887
+ const pageIssues = await validatePageSectionTypes(
2888
+ pagesDir,
2889
+ allSchemaTypeSet
2890
+ );
2891
+ issues.push(...pageIssues);
2892
+ }
2893
+ }
2721
2894
  logger.stopSpinner(true, "Validation complete");
2722
2895
  const errors = issues.filter((i) => i.type === "error");
2723
2896
  const warnings = issues.filter((i) => i.type === "warning");
@@ -2756,6 +2929,116 @@ async function validateCommand(options) {
2756
2929
  }
2757
2930
  }
2758
2931
  }
2932
+ async function loadSchemaTypes(themePath, sectionsDir) {
2933
+ const results = [];
2934
+ const sections = fs.readdirSync(sectionsDir).filter((name) => fs.statSync(path9.join(sectionsDir, name)).isDirectory());
2935
+ for (const sectionName of sections) {
2936
+ const schemaFile = path9.join(
2937
+ sectionsDir,
2938
+ sectionName,
2939
+ `${sectionName}.schema.ts`
2940
+ );
2941
+ if (!fs.existsSync(schemaFile)) {
2942
+ results.push({ folderName: sectionName, schemaType: null });
2943
+ continue;
2944
+ }
2945
+ const content = fs.readFileSync(schemaFile, "utf-8");
2946
+ let schemaType = null;
2947
+ const schemaExportMatch = content.match(
2948
+ /:\s*SectionSchema\s*=\s*\{[\s\S]*?\btype:\s*["']([^"']+)["']/
2949
+ );
2950
+ if (schemaExportMatch) {
2951
+ schemaType = schemaExportMatch[1];
2952
+ } else {
2953
+ const allTypeMatches = [
2954
+ ...content.matchAll(/\btype:\s*["']([^"']+)["']/g)
2955
+ ];
2956
+ for (const m of allTypeMatches) {
2957
+ if (!FIELD_TYPES.has(m[1])) {
2958
+ schemaType = m[1];
2959
+ break;
2960
+ }
2961
+ }
2962
+ }
2963
+ results.push({
2964
+ folderName: sectionName,
2965
+ schemaType
2966
+ });
2967
+ }
2968
+ return results;
2969
+ }
2970
+ async function validatePageSectionTypes(pagesDir, validTypes) {
2971
+ const issues = [];
2972
+ const files = fs.readdirSync(pagesDir).filter((f) => f.match(/\.(ts|js)$/));
2973
+ for (const file of files) {
2974
+ const content = fs.readFileSync(path9.join(pagesDir, file), "utf-8");
2975
+ const pageName = file.replace(/\.(ts|js)$/, "");
2976
+ const sectionsMatch = content.match(/\bsections:\s*\[/);
2977
+ if (!sectionsMatch || sectionsMatch.index === void 0) continue;
2978
+ const startIdx = sectionsMatch.index + sectionsMatch[0].length;
2979
+ let depth = 1;
2980
+ let endIdx = startIdx;
2981
+ for (let i = startIdx; i < content.length && depth > 0; i++) {
2982
+ if (content[i] === "[") depth++;
2983
+ else if (content[i] === "]") depth--;
2984
+ endIdx = i;
2985
+ }
2986
+ const sectionsBlock = content.slice(startIdx, endIdx);
2987
+ const typeMatches = sectionsBlock.matchAll(/\btype:\s*["']([^"']+)["']/g);
2988
+ for (const match of typeMatches) {
2989
+ const sectionType = match[1];
2990
+ if (!validTypes.has(sectionType)) {
2991
+ issues.push({
2992
+ type: "error",
2993
+ file: `pages/${file}`,
2994
+ message: `Page "${pageName}" uses section type "${sectionType}" which doesn't exist in sections/`
2995
+ });
2996
+ }
2997
+ }
2998
+ }
2999
+ return issues;
3000
+ }
3001
+ var FIELD_TYPES = /* @__PURE__ */ new Set([
3002
+ "text",
3003
+ "textarea",
3004
+ "richtext",
3005
+ "number",
3006
+ "range",
3007
+ "toggle",
3008
+ "switch",
3009
+ "select",
3010
+ "radio",
3011
+ "checkbox",
3012
+ "color",
3013
+ "image",
3014
+ "video",
3015
+ "url",
3016
+ "link",
3017
+ "icon",
3018
+ "date",
3019
+ "datetime",
3020
+ "collection",
3021
+ "product",
3022
+ "blog",
3023
+ "page",
3024
+ "html",
3025
+ "code",
3026
+ "json",
3027
+ "array",
3028
+ "object",
3029
+ "group",
3030
+ "section",
3031
+ "boolean",
3032
+ "color_token",
3033
+ "color_background",
3034
+ "image_picker",
3035
+ "video_url",
3036
+ "font",
3037
+ "font_picker",
3038
+ "text_alignment",
3039
+ "inline_richtext",
3040
+ "repeater"
3041
+ ]);
2759
3042
 
2760
3043
  // src/commands/build.ts
2761
3044
  init_logger();
@@ -2766,14 +3049,14 @@ async function buildCommand(options) {
2766
3049
  if (options.theme) {
2767
3050
  themeName = options.theme;
2768
3051
  try {
2769
- const workspaceThemePath = path8.join(getThemesDir(), themeName);
3052
+ const workspaceThemePath = path9.join(getThemesDir(), themeName);
2770
3053
  if (fs.existsSync(workspaceThemePath)) {
2771
3054
  themePath = workspaceThemePath;
2772
3055
  } else {
2773
- themePath = path8.join(process.cwd(), themeName);
3056
+ themePath = path9.join(process.cwd(), themeName);
2774
3057
  }
2775
3058
  } catch {
2776
- themePath = path8.join(process.cwd(), themeName);
3059
+ themePath = path9.join(process.cwd(), themeName);
2777
3060
  }
2778
3061
  if (!fs.existsSync(themePath)) {
2779
3062
  logger.error(`Theme "${themeName}" not found.`);
@@ -2784,10 +3067,10 @@ async function buildCommand(options) {
2784
3067
  "theme.config.ts",
2785
3068
  "bundle-entry.ts",
2786
3069
  "manifest.ts"
2787
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
3070
+ ].some((f) => fs.existsSync(path9.join(process.cwd(), f)));
2788
3071
  if (isThemeDir) {
2789
3072
  themePath = process.cwd();
2790
- themeName = path8.basename(themePath);
3073
+ themeName = path9.basename(themePath);
2791
3074
  logger.info(`Building current theme: ${themeName}`);
2792
3075
  } else {
2793
3076
  logger.error(
@@ -2796,7 +3079,7 @@ async function buildCommand(options) {
2796
3079
  process.exit(1);
2797
3080
  }
2798
3081
  }
2799
- const packageJsonPath = path8.join(themePath, "package.json");
3082
+ const packageJsonPath = path9.join(themePath, "package.json");
2800
3083
  const hasPkgJson = fs.existsSync(packageJsonPath);
2801
3084
  if (!hasPkgJson) {
2802
3085
  logger.warning(
@@ -2852,9 +3135,9 @@ async function buildCommand(options) {
2852
3135
  logger.success("\u2713 Theme built successfully!");
2853
3136
  logger.newLine();
2854
3137
  logger.info(`Theme: ${themeName}`);
2855
- const distPath = path8.join(themePath, "dist");
3138
+ const distPath = path9.join(themePath, "dist");
2856
3139
  if (fs.existsSync(distPath)) {
2857
- logger.log(`Output: ${path8.relative(process.cwd(), distPath)}`);
3140
+ logger.log(`Output: ${path9.relative(process.cwd(), distPath)}`);
2858
3141
  const files = fs.readdirSync(distPath);
2859
3142
  logger.log(`Files: ${files.length}`);
2860
3143
  }
@@ -2910,7 +3193,7 @@ async function packageCommand(options) {
2910
3193
  let themeName;
2911
3194
  if (options.theme) {
2912
3195
  themeName = options.theme;
2913
- themePath = path8.join(getThemesDir(), themeName);
3196
+ themePath = path9.join(getThemesDir(), themeName);
2914
3197
  if (!fs.existsSync(themePath)) {
2915
3198
  logger.error(`Theme "${themeName}" not found.`);
2916
3199
  process.exit(1);
@@ -2920,10 +3203,10 @@ async function packageCommand(options) {
2920
3203
  "theme.config.ts",
2921
3204
  "bundle-entry.ts",
2922
3205
  "manifest.ts"
2923
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
3206
+ ].some((f) => fs.existsSync(path9.join(process.cwd(), f)));
2924
3207
  if (isThemeDir) {
2925
3208
  themePath = process.cwd();
2926
- themeName = path8.basename(themePath);
3209
+ themeName = path9.basename(themePath);
2927
3210
  logger.info(`Packaging current theme: ${themeName}`);
2928
3211
  } else {
2929
3212
  logger.error(
@@ -2932,7 +3215,7 @@ async function packageCommand(options) {
2932
3215
  process.exit(1);
2933
3216
  }
2934
3217
  }
2935
- const packageJsonPath = path8.join(themePath, "package.json");
3218
+ const packageJsonPath = path9.join(themePath, "package.json");
2936
3219
  let version2 = "1.0.0";
2937
3220
  if (fs.existsSync(packageJsonPath)) {
2938
3221
  const packageJson = await fs.readJson(packageJsonPath);
@@ -2942,7 +3225,7 @@ async function packageCommand(options) {
2942
3225
  logger.info(`Theme: ${themeName}`);
2943
3226
  logger.info(`Version: ${version2}`);
2944
3227
  logger.newLine();
2945
- const compiledThemePath = path8.join(
3228
+ const compiledThemePath = path9.join(
2946
3229
  process.cwd(),
2947
3230
  "themes",
2948
3231
  themeName,
@@ -2976,8 +3259,8 @@ async function packageCommand(options) {
2976
3259
  logger.newLine();
2977
3260
  logger.section("Step 2: Create Package");
2978
3261
  const packageName = options.name || `${themeName}-${version2}`;
2979
- const outputDir = options.output || path8.join(process.cwd(), "dist");
2980
- const outputPath = path8.join(outputDir, `${packageName}.zip`);
3262
+ const outputDir = options.output || path9.join(process.cwd(), "dist");
3263
+ const outputPath = path9.join(outputDir, `${packageName}.zip`);
2981
3264
  await fs.ensureDir(outputDir);
2982
3265
  logger.startSpinner("Creating zip archive...");
2983
3266
  try {
@@ -2990,11 +3273,11 @@ async function packageCommand(options) {
2990
3273
  logger.newLine();
2991
3274
  logger.info(`Package: ${packageName}.zip`);
2992
3275
  logger.log(`Size: ${sizeMB} MB`);
2993
- logger.log(`Location: ${path8.relative(process.cwd(), outputPath)}`);
3276
+ logger.log(`Location: ${path9.relative(process.cwd(), outputPath)}`);
2994
3277
  logger.newLine();
2995
3278
  logger.section("Next steps:");
2996
3279
  logger.log(
2997
- ` onexthm deploy --package ${path8.relative(process.cwd(), outputPath)}`
3280
+ ` onexthm deploy --package ${path9.relative(process.cwd(), outputPath)}`
2998
3281
  );
2999
3282
  } catch (error) {
3000
3283
  logger.stopSpinner(false, "Failed to create package");
@@ -3052,9 +3335,9 @@ async function deployCommand(options) {
3052
3335
  ensureOneXProject();
3053
3336
  let packagePath;
3054
3337
  if (options.package) {
3055
- packagePath = path8.resolve(options.package);
3338
+ packagePath = path9.resolve(options.package);
3056
3339
  } else if (options.theme) {
3057
- const distDir = path8.join(process.cwd(), "dist");
3340
+ const distDir = path9.join(process.cwd(), "dist");
3058
3341
  if (!fs.existsSync(distDir)) {
3059
3342
  logger.error("No dist/ directory found. Run 'onexthm package' first.");
3060
3343
  process.exit(1);
@@ -3069,7 +3352,7 @@ async function deployCommand(options) {
3069
3352
  process.exit(1);
3070
3353
  }
3071
3354
  packageFiles.sort().reverse();
3072
- packagePath = path8.join(distDir, packageFiles[0]);
3355
+ packagePath = path9.join(distDir, packageFiles[0]);
3073
3356
  } else {
3074
3357
  logger.error("Either --package or --theme must be specified.");
3075
3358
  logger.info("Examples:");
@@ -3083,11 +3366,11 @@ async function deployCommand(options) {
3083
3366
  }
3084
3367
  const stats = await fs.stat(packagePath);
3085
3368
  const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
3086
- const fileName = path8.basename(packagePath);
3369
+ const fileName = path9.basename(packagePath);
3087
3370
  logger.newLine();
3088
3371
  logger.info(`Package: ${fileName}`);
3089
3372
  logger.log(`Size: ${sizeMB} MB`);
3090
- logger.log(`Path: ${path8.relative(process.cwd(), packagePath)}`);
3373
+ logger.log(`Path: ${path9.relative(process.cwd(), packagePath)}`);
3091
3374
  logger.newLine();
3092
3375
  const apiUrl = options.apiUrl || process.env.ONEX_API_URL || "http://localhost:3001";
3093
3376
  const uploadEndpoint = `${apiUrl}/website-api/themes/upload`;
@@ -3191,11 +3474,11 @@ function getBucketName(env) {
3191
3474
  return environment === "production" ? "theme-s3-bucket" : "theme-s3-bucket";
3192
3475
  }
3193
3476
  async function findCompiledThemeDir(themeId, version2) {
3194
- const searchPaths = [path8.resolve(process.cwd(), "dist")];
3477
+ const searchPaths = [path9.resolve(process.cwd(), "dist")];
3195
3478
  for (const dir of searchPaths) {
3196
3479
  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"));
3480
+ const hasManifest = await fs.pathExists(path9.join(dir, "manifest.json"));
3481
+ const hasThemeEntry = await fs.pathExists(path9.join(dir, "bundle-entry.js")) || await fs.pathExists(path9.join(dir, "theme.config.js")) || await fs.pathExists(path9.join(dir, "index.js"));
3199
3482
  if (hasManifest || hasThemeEntry) {
3200
3483
  return dir;
3201
3484
  }
@@ -3204,7 +3487,7 @@ async function findCompiledThemeDir(themeId, version2) {
3204
3487
  return null;
3205
3488
  }
3206
3489
  async function readManifest() {
3207
- const manifestTsPath = path8.resolve(process.cwd(), "manifest.ts");
3490
+ const manifestTsPath = path9.resolve(process.cwd(), "manifest.ts");
3208
3491
  if (await fs.pathExists(manifestTsPath)) {
3209
3492
  try {
3210
3493
  const module = await import(manifestTsPath);
@@ -3213,7 +3496,7 @@ async function readManifest() {
3213
3496
  logger.warning("Failed to import manifest.ts, trying package.json");
3214
3497
  }
3215
3498
  }
3216
- const packageJsonPath = path8.resolve(process.cwd(), "package.json");
3499
+ const packageJsonPath = path9.resolve(process.cwd(), "package.json");
3217
3500
  if (await fs.pathExists(packageJsonPath)) {
3218
3501
  const pkg = await fs.readJson(packageJsonPath);
3219
3502
  return {
@@ -3247,13 +3530,13 @@ async function findSourceDir(themeId, explicitDir) {
3247
3530
  }
3248
3531
  const searchPaths = [
3249
3532
  process.cwd(),
3250
- path8.resolve(process.cwd(), `../../themes/${themeId}`),
3251
- path8.resolve(process.cwd(), `../themes/${themeId}`)
3533
+ path9.resolve(process.cwd(), `../../themes/${themeId}`),
3534
+ path9.resolve(process.cwd(), `../themes/${themeId}`)
3252
3535
  ];
3253
3536
  const markers = ["theme.config.ts", "bundle-entry.ts"];
3254
3537
  for (const dir of searchPaths) {
3255
3538
  for (const marker of markers) {
3256
- if (await fs.pathExists(path8.join(dir, marker))) {
3539
+ if (await fs.pathExists(path9.join(dir, marker))) {
3257
3540
  return dir;
3258
3541
  }
3259
3542
  }
@@ -3304,8 +3587,8 @@ async function uploadCommand(options) {
3304
3587
  }
3305
3588
  spinner.succeed(`Found compiled theme at: ${compiledDir}`);
3306
3589
  spinner.start("Creating bundle.zip...");
3307
- const tmpDir = os3.tmpdir();
3308
- const bundleZipPath = path8.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
3590
+ const tmpDir = os.tmpdir();
3591
+ const bundleZipPath = path9.join(tmpDir, `${themeId}-${version2}-bundle.zip`);
3309
3592
  await createZipFromDir(compiledDir, bundleZipPath);
3310
3593
  const bundleZipBuffer = await fs.readFile(bundleZipPath);
3311
3594
  const bundleSizeMB = (bundleZipBuffer.length / 1024 / 1024).toFixed(2);
@@ -3359,7 +3642,7 @@ async function uploadCommand(options) {
3359
3642
  if (sourceDir) {
3360
3643
  spinner.succeed(`Found source at: ${sourceDir}`);
3361
3644
  spinner.start("Creating source.zip...");
3362
- const sourceZipPath = path8.join(
3645
+ const sourceZipPath = path9.join(
3363
3646
  tmpDir,
3364
3647
  `${themeId}-${version2}-source.zip`
3365
3648
  );
@@ -3493,8 +3776,8 @@ async function resolveLatestVersion(s3Client, bucket, themeId) {
3493
3776
  async function createCompatibilityFiles(outputDir, manifest) {
3494
3777
  const entryFile = manifest.output?.entry || "bundle-entry.js";
3495
3778
  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");
3779
+ const hashedPath = path9.join(outputDir, entryFile);
3780
+ const stablePath = path9.join(outputDir, "bundle-entry.js");
3498
3781
  if (await fs.pathExists(hashedPath)) {
3499
3782
  await fs.copy(hashedPath, stablePath);
3500
3783
  const mapPath = hashedPath + ".map";
@@ -3503,13 +3786,13 @@ async function createCompatibilityFiles(outputDir, manifest) {
3503
3786
  }
3504
3787
  }
3505
3788
  }
3506
- const sectionsRegistryPath = path8.join(outputDir, "sections-registry.js");
3789
+ const sectionsRegistryPath = path9.join(outputDir, "sections-registry.js");
3507
3790
  const content = `// Re-export all sections from bundle-entry
3508
3791
  // This file exists to maintain compatibility with the import path
3509
3792
  export * from './bundle-entry.js';
3510
3793
  `;
3511
3794
  await fs.writeFile(sectionsRegistryPath, content, "utf-8");
3512
- const pkgJsonPath = path8.join(outputDir, "package.json");
3795
+ const pkgJsonPath = path9.join(outputDir, "package.json");
3513
3796
  await fs.writeFile(pkgJsonPath, '{\n "type": "module"\n}\n', "utf-8");
3514
3797
  }
3515
3798
  function showDownloadFailureHelp(themeId, bucket) {
@@ -3571,6 +3854,18 @@ async function downloadCommand(options) {
3571
3854
  spinner.succeed(
3572
3855
  `Resolved latest version: ${chalk4.cyan(resolvedVersion)}`
3573
3856
  );
3857
+ const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS || process.env.VERCEL);
3858
+ if (isCI) {
3859
+ console.log(
3860
+ chalk4.yellow(
3861
+ `
3862
+ Warning: Resolved "latest" to ${resolvedVersion} in CI environment.
3863
+ For production builds, pin to a specific version:
3864
+ THEME_VERSION=${resolvedVersion}
3865
+ `
3866
+ )
3867
+ );
3868
+ }
3574
3869
  }
3575
3870
  spinner.start(
3576
3871
  `Downloading bundle.zip for ${themeId}@${resolvedVersion}...`
@@ -3592,7 +3887,7 @@ async function downloadCommand(options) {
3592
3887
  zip.extractAllTo(outputDir, true);
3593
3888
  const entries = zip.getEntries().filter((e) => !e.isDirectory);
3594
3889
  spinner.succeed(`Extracted ${entries.length} files to ${outputDir}`);
3595
- const manifestPath = path8.join(outputDir, "manifest.json");
3890
+ const manifestPath = path9.join(outputDir, "manifest.json");
3596
3891
  const manifest = await fs.readJson(manifestPath);
3597
3892
  await createCompatibilityFiles(outputDir, manifest);
3598
3893
  console.log();
@@ -3726,7 +4021,7 @@ async function renameTheme(themeDir, oldName, newName) {
3726
4021
  const oldPrefix = `${oldName}-`;
3727
4022
  const newPrefix = `${newName}-`;
3728
4023
  const newDisplayName = newName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3729
- const pkgPath = path8.join(themeDir, "package.json");
4024
+ const pkgPath = path9.join(themeDir, "package.json");
3730
4025
  if (await fs.pathExists(pkgPath)) {
3731
4026
  const pkg = await fs.readJson(pkgPath);
3732
4027
  pkg.name = `@onex-themes/${newName}`;
@@ -3742,7 +4037,7 @@ async function renameTheme(themeDir, oldName, newName) {
3742
4037
  }
3743
4038
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3744
4039
  }
3745
- const configPath = path8.join(themeDir, "theme.config.ts");
4040
+ const configPath = path9.join(themeDir, "theme.config.ts");
3746
4041
  if (await fs.pathExists(configPath)) {
3747
4042
  let content = await fs.readFile(configPath, "utf-8");
3748
4043
  content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
@@ -3752,7 +4047,7 @@ async function renameTheme(themeDir, oldName, newName) {
3752
4047
  );
3753
4048
  await fs.writeFile(configPath, content);
3754
4049
  }
3755
- const layoutPath = path8.join(themeDir, "theme.layout.ts");
4050
+ const layoutPath = path9.join(themeDir, "theme.layout.ts");
3756
4051
  if (await fs.pathExists(layoutPath)) {
3757
4052
  let content = await fs.readFile(layoutPath, "utf-8");
3758
4053
  content = content.replace(/id:\s*"[^"]*"/, `id: "${newName}"`);
@@ -3765,7 +4060,7 @@ async function renameTheme(themeDir, oldName, newName) {
3765
4060
  const oldDisplayName = oldName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3766
4061
  const tsFiles = await glob("**/*.ts", { cwd: themeDir, nodir: true });
3767
4062
  for (const file of tsFiles) {
3768
- const filePath = path8.join(themeDir, file);
4063
+ const filePath = path9.join(themeDir, file);
3769
4064
  let content = await fs.readFile(filePath, "utf-8");
3770
4065
  const original = content;
3771
4066
  content = content.replace(
@@ -3794,7 +4089,7 @@ async function cloneCommand(themeName, options) {
3794
4089
  const spinner = ora("Initializing clone...").start();
3795
4090
  try {
3796
4091
  const bucket = options.bucket || getBucketName3(options.environment);
3797
- const outputDir = options.output || path8.resolve(process.cwd(), newName);
4092
+ const outputDir = options.output || path9.resolve(process.cwd(), newName);
3798
4093
  const s3Client = getS3Client3();
3799
4094
  if (await fs.pathExists(outputDir)) {
3800
4095
  spinner.fail(chalk4.red(`Directory already exists: ${outputDir}`));
@@ -3849,7 +4144,7 @@ async function cloneCommand(themeName, options) {
3849
4144
  spinner.succeed(
3850
4145
  `Renamed theme: ${chalk4.gray(themeName)} \u2192 ${chalk4.cyan(newName)}`
3851
4146
  );
3852
- const envExamplePath = path8.join(outputDir, ".env.example");
4147
+ const envExamplePath = path9.join(outputDir, ".env.example");
3853
4148
  if (!await fs.pathExists(envExamplePath)) {
3854
4149
  await fs.writeFile(
3855
4150
  envExamplePath,
@@ -3862,7 +4157,7 @@ async function cloneCommand(themeName, options) {
3862
4157
  ].join("\n")
3863
4158
  );
3864
4159
  }
3865
- const mcpJsonPath = path8.join(outputDir, ".mcp.json");
4160
+ const mcpJsonPath = path9.join(outputDir, ".mcp.json");
3866
4161
  if (await fs.pathExists(mcpJsonPath)) {
3867
4162
  const { default: inquirerMod } = await import('inquirer');
3868
4163
  const { figmaApiKey } = await inquirerMod.prompt([
@@ -3887,7 +4182,7 @@ async function cloneCommand(themeName, options) {
3887
4182
  }
3888
4183
  if (options.install !== false) {
3889
4184
  const hasPkgJson = await fs.pathExists(
3890
- path8.join(outputDir, "package.json")
4185
+ path9.join(outputDir, "package.json")
3891
4186
  );
3892
4187
  if (hasPkgJson) {
3893
4188
  spinner.start("Installing dependencies...");
@@ -3914,7 +4209,7 @@ 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 ${path9.relative(process.cwd(), outputDir)}`));
3918
4213
  console.log(
3919
4214
  chalk4.gray(" cp .env.example .env # then add your Company ID")
3920
4215
  );
@@ -3949,7 +4244,7 @@ var MIME_TYPES = {
3949
4244
  };
3950
4245
  function createDevServer(options) {
3951
4246
  const clients = /* @__PURE__ */ new Set();
3952
- const themeDataPath = path8.join(options.distDir, "theme-data.json");
4247
+ const themeDataPath = path9.join(options.distDir, "theme-data.json");
3953
4248
  const server = http.createServer((req, res) => {
3954
4249
  res.setHeader("Access-Control-Allow-Origin", "*");
3955
4250
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
@@ -3975,8 +4270,8 @@ function createDevServer(options) {
3975
4270
  if (pathname.startsWith("/_assets/")) {
3976
4271
  const parts = pathname.replace(/^\/_assets\//, "").split("/");
3977
4272
  const assetSubpath = parts.slice(1).join("/");
3978
- const assetPath = path8.join(options.themePath, "assets", assetSubpath);
3979
- if (!assetPath.startsWith(path8.join(options.themePath, "assets"))) {
4273
+ const assetPath = path9.join(options.themePath, "assets", assetSubpath);
4274
+ if (!assetPath.startsWith(path9.join(options.themePath, "assets"))) {
3980
4275
  res.writeHead(403);
3981
4276
  res.end("Forbidden");
3982
4277
  return;
@@ -3987,8 +4282,8 @@ function createDevServer(options) {
3987
4282
  if (pathname.startsWith("/themes/")) {
3988
4283
  const match = pathname.match(/^\/themes\/[^/]+\/assets\/(.+)/);
3989
4284
  if (match) {
3990
- const assetPath = path8.join(options.themePath, "assets", match[1]);
3991
- if (!assetPath.startsWith(path8.join(options.themePath, "assets"))) {
4285
+ const assetPath = path9.join(options.themePath, "assets", match[1]);
4286
+ if (!assetPath.startsWith(path9.join(options.themePath, "assets"))) {
3992
4287
  res.writeHead(403);
3993
4288
  res.end("Forbidden");
3994
4289
  return;
@@ -4002,26 +4297,26 @@ function createDevServer(options) {
4002
4297
  const segments = subpath.split("/");
4003
4298
  let assetPath;
4004
4299
  if (segments[0] === options.themeName || segments[0] === options.themeName.replace(/^my-/, "")) {
4005
- assetPath = path8.join(
4300
+ assetPath = path9.join(
4006
4301
  options.themePath,
4007
4302
  "assets",
4008
4303
  segments.slice(1).join("/")
4009
4304
  );
4010
4305
  } else {
4011
- assetPath = path8.join(options.themePath, "assets", subpath);
4306
+ assetPath = path9.join(options.themePath, "assets", subpath);
4012
4307
  }
4013
- if (assetPath.startsWith(path8.join(options.themePath, "assets")) && fs2.existsSync(assetPath)) {
4308
+ if (assetPath.startsWith(path9.join(options.themePath, "assets")) && fs3.existsSync(assetPath)) {
4014
4309
  serveFile(res, assetPath);
4015
4310
  return;
4016
4311
  }
4017
4312
  }
4018
- const filePath = path8.join(options.distDir, pathname);
4313
+ const filePath = path9.join(options.distDir, pathname);
4019
4314
  if (!filePath.startsWith(options.distDir)) {
4020
4315
  res.writeHead(403);
4021
4316
  res.end("Forbidden");
4022
4317
  return;
4023
4318
  }
4024
- if (fs2.existsSync(filePath) && fs2.statSync(filePath).isFile()) {
4319
+ if (fs3.existsSync(filePath) && fs3.statSync(filePath).isFile()) {
4025
4320
  serveFile(res, filePath);
4026
4321
  } else {
4027
4322
  res.writeHead(200, { "Content-Type": "text/html" });
@@ -4053,14 +4348,14 @@ function createDevServer(options) {
4053
4348
  }
4054
4349
  function serveFile(res, filePath) {
4055
4350
  try {
4056
- if (!fs2.existsSync(filePath)) {
4351
+ if (!fs3.existsSync(filePath)) {
4057
4352
  res.writeHead(404);
4058
4353
  res.end("Not Found");
4059
4354
  return;
4060
4355
  }
4061
- const ext = path8.extname(filePath);
4356
+ const ext = path9.extname(filePath);
4062
4357
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
4063
- const content = fs2.readFileSync(filePath);
4358
+ const content = fs3.readFileSync(filePath);
4064
4359
  res.writeHead(200, { "Content-Type": contentType });
4065
4360
  res.end(content);
4066
4361
  } catch {
@@ -4073,7 +4368,7 @@ function generatePreviewHTML(themeName, port, themeDataPath) {
4073
4368
  let fontVarsCSS = "";
4074
4369
  if (themeDataPath) {
4075
4370
  try {
4076
- const themeData = JSON.parse(fs2.readFileSync(themeDataPath, "utf-8"));
4371
+ const themeData = JSON.parse(fs3.readFileSync(themeDataPath, "utf-8"));
4077
4372
  const typography = (themeData?.themeConfig || themeData?.theme?.config)?.typography?.fontFamily;
4078
4373
  if (typography) {
4079
4374
  const fontFamilies = /* @__PURE__ */ new Set();
@@ -4161,14 +4456,14 @@ async function devCommand(options) {
4161
4456
  if (options.theme) {
4162
4457
  themeName = options.theme;
4163
4458
  try {
4164
- const workspaceThemePath = path8.join(getThemesDir(), themeName);
4459
+ const workspaceThemePath = path9.join(getThemesDir(), themeName);
4165
4460
  if (fs.existsSync(workspaceThemePath)) {
4166
4461
  themePath = workspaceThemePath;
4167
4462
  } else {
4168
- themePath = path8.join(process.cwd(), themeName);
4463
+ themePath = path9.join(process.cwd(), themeName);
4169
4464
  }
4170
4465
  } catch {
4171
- themePath = path8.join(process.cwd(), themeName);
4466
+ themePath = path9.join(process.cwd(), themeName);
4172
4467
  }
4173
4468
  if (!fs.existsSync(themePath)) {
4174
4469
  logger.error(`Theme "${themeName}" not found.`);
@@ -4179,10 +4474,10 @@ async function devCommand(options) {
4179
4474
  "theme.config.ts",
4180
4475
  "bundle-entry.ts",
4181
4476
  "manifest.ts"
4182
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
4477
+ ].some((f) => fs.existsSync(path9.join(process.cwd(), f)));
4183
4478
  if (isThemeDir) {
4184
4479
  themePath = process.cwd();
4185
- themeName = path8.basename(themePath);
4480
+ themeName = path9.basename(themePath);
4186
4481
  } else {
4187
4482
  logger.error(
4188
4483
  "Not in a theme directory and no --theme specified. Run from theme root or use --theme flag."
@@ -4251,9 +4546,9 @@ async function devCommand(options) {
4251
4546
  watcher.close();
4252
4547
  await context2.dispose();
4253
4548
  server.close();
4254
- const shimPath = path8.join(outputDir, ".process-shim.js");
4549
+ const shimPath = path9.join(outputDir, ".process-shim.js");
4255
4550
  try {
4256
- await fs7.unlink(shimPath);
4551
+ await fs8.unlink(shimPath);
4257
4552
  } catch {
4258
4553
  }
4259
4554
  process.exit(0);
@@ -4262,8 +4557,8 @@ async function devCommand(options) {
4262
4557
 
4263
4558
  // src/commands/config.ts
4264
4559
  init_logger();
4265
- var CONFIG_DIR = path8.join(os3.homedir(), ".onexthm");
4266
- var CONFIG_FILE = path8.join(CONFIG_DIR, ".env");
4560
+ var CONFIG_DIR = path9.join(os.homedir(), ".onexthm");
4561
+ var CONFIG_FILE = path9.join(CONFIG_DIR, ".env");
4267
4562
  var CONFIG_ENTRIES = [
4268
4563
  {
4269
4564
  key: "AWS_ACCESS_KEY_ID",
@@ -4405,122 +4700,6 @@ async function configCommand() {
4405
4700
 
4406
4701
  // src/commands/login.ts
4407
4702
  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
4703
  async function loginCommand() {
4525
4704
  logger.header("OneX Theme Developer Login");
4526
4705
  const existing = loadAuthTokens();
@@ -4646,13 +4825,13 @@ async function publishCommand(options) {
4646
4825
  logger.info(`Logged in as: ${tokens.user.email}`);
4647
4826
  let themePath;
4648
4827
  if (options.theme) {
4649
- themePath = path8.resolve(options.theme);
4828
+ themePath = path9.resolve(options.theme);
4650
4829
  } else {
4651
4830
  const isThemeDir = [
4652
4831
  "theme.config.ts",
4653
4832
  "bundle-entry.ts",
4654
4833
  "manifest.ts"
4655
- ].some((f) => fs.existsSync(path8.join(process.cwd(), f)));
4834
+ ].some((f) => fs.existsSync(path9.join(process.cwd(), f)));
4656
4835
  if (isThemeDir) {
4657
4836
  themePath = process.cwd();
4658
4837
  } else {
@@ -4662,14 +4841,31 @@ async function publishCommand(options) {
4662
4841
  process.exit(1);
4663
4842
  }
4664
4843
  }
4665
- const pkgPath = path8.join(themePath, "package.json");
4844
+ const pkgPath = path9.join(themePath, "package.json");
4666
4845
  if (!fs.existsSync(pkgPath)) {
4667
4846
  logger.error("No package.json found in theme directory");
4668
4847
  process.exit(1);
4669
4848
  }
4670
4849
  const pkg = fs.readJsonSync(pkgPath);
4671
- const themeId = pkg.name?.replace("@onex-themes/", "") || path8.basename(themePath);
4850
+ const themeId = pkg.name?.replace("@onex-themes/", "") || path9.basename(themePath);
4851
+ if (options.bump) {
4852
+ const currentVersion = pkg.version || "1.0.0";
4853
+ const newVersion = semver.inc(currentVersion, options.bump);
4854
+ if (!newVersion) {
4855
+ logger.error(`Failed to bump version from ${currentVersion}`);
4856
+ process.exit(1);
4857
+ }
4858
+ pkg.version = newVersion;
4859
+ fs.writeJsonSync(pkgPath, pkg, { spaces: 2 });
4860
+ logger.info(`Bumped version: ${currentVersion} -> ${newVersion}`);
4861
+ }
4672
4862
  const version2 = pkg.version || "1.0.0";
4863
+ if (!semver.valid(version2)) {
4864
+ logger.error(
4865
+ `Invalid version "${version2}" in package.json. Must be valid semver (e.g., 1.0.0)`
4866
+ );
4867
+ process.exit(1);
4868
+ }
4673
4869
  logger.newLine();
4674
4870
  logger.info(`Theme: ${themeId}`);
4675
4871
  logger.info(`Version: ${version2}`);
@@ -4706,6 +4902,38 @@ async function publishCommand(options) {
4706
4902
  logger.error(error instanceof Error ? error.message : "Connection failed");
4707
4903
  process.exit(1);
4708
4904
  }
4905
+ logger.startSpinner("Checking version availability...");
4906
+ try {
4907
+ const checkResponse = await authenticatedFetch(
4908
+ `${apiUrl}/website-api/themes/${encodeURIComponent(themeId)}/versions/${encodeURIComponent(version2)}/exists`,
4909
+ { method: "GET" }
4910
+ );
4911
+ const checkData = await checkResponse.json();
4912
+ const checkBody = checkData.statusCode ? checkData.body : checkData;
4913
+ if (checkBody.exists) {
4914
+ logger.stopSpinner(false, "Version already published");
4915
+ const patchVer = semver.inc(version2, "patch") || "?";
4916
+ const minorVer = semver.inc(version2, "minor") || "?";
4917
+ const majorVer = semver.inc(version2, "major") || "?";
4918
+ logger.error(
4919
+ `
4920
+ Version ${version2} of "${themeId}" is already published and cannot be overwritten.
4921
+
4922
+ To publish a new version:
4923
+ 1. Bump version in package.json (e.g., ${version2} -> ${patchVer})
4924
+ 2. Run: onexthm publish
4925
+
4926
+ Or use the --bump flag:
4927
+ onexthm publish --bump patch (${version2} -> ${patchVer})
4928
+ onexthm publish --bump minor (${version2} -> ${minorVer})
4929
+ onexthm publish --bump major (${version2} -> ${majorVer})`
4930
+ );
4931
+ process.exit(1);
4932
+ }
4933
+ logger.stopSpinner(true, `Version ${version2} is available`);
4934
+ } catch (error) {
4935
+ logger.stopSpinner(true, "Version check skipped (endpoint not available)");
4936
+ }
4709
4937
  logger.startSpinner("Building theme...");
4710
4938
  try {
4711
4939
  const { compileStandaloneTheme: compileStandaloneTheme2 } = await Promise.resolve().then(() => (init_compile_theme(), compile_theme_exports));
@@ -4751,13 +4979,13 @@ async function publishCommand(options) {
4751
4979
  try {
4752
4980
  const archiver3 = await import('archiver');
4753
4981
  const { createWriteStream } = await import('fs');
4754
- const distDir = path8.join(themePath, "dist");
4982
+ const distDir = path9.join(themePath, "dist");
4755
4983
  if (!fs.existsSync(distDir)) {
4756
4984
  logger.stopSpinner(false, "No dist/ directory");
4757
4985
  logger.error("Build the theme first: onexthm build");
4758
4986
  process.exit(1);
4759
4987
  }
4760
- const bundleZipPath = path8.join(themePath, "dist", "bundle.zip");
4988
+ const bundleZipPath = path9.join(themePath, "dist", "bundle.zip");
4761
4989
  await createZip(distDir, bundleZipPath, [
4762
4990
  "bundle.zip",
4763
4991
  "source.zip",
@@ -4782,7 +5010,7 @@ async function publishCommand(options) {
4782
5010
  }
4783
5011
  logger.startSpinner("Uploading source...");
4784
5012
  try {
4785
- const sourceZipPath = path8.join(themePath, "dist", "source.zip");
5013
+ const sourceZipPath = path9.join(themePath, "dist", "source.zip");
4786
5014
  await createZip(themePath, sourceZipPath, [
4787
5015
  "node_modules",
4788
5016
  "dist",
@@ -4867,17 +5095,23 @@ async function createZip(sourceDir, outputPath, exclude) {
4867
5095
  }
4868
5096
 
4869
5097
  // src/cli.ts
5098
+ dotenv.config({
5099
+ path: path9.join(process.cwd(), ".env.local"),
5100
+ override: true
5101
+ });
5102
+ dotenv.config({ path: path9.join(process.cwd(), ".env") });
4870
5103
  try {
4871
5104
  const projectRoot = getProjectRoot();
4872
- dotenv.config({
4873
- path: path8.join(projectRoot, ".env.local"),
4874
- quiet: true
4875
- });
4876
- dotenv.config({ path: path8.join(projectRoot, ".env"), quiet: true });
5105
+ if (path9.resolve(projectRoot) !== path9.resolve(process.cwd())) {
5106
+ dotenv.config({
5107
+ path: path9.join(projectRoot, ".env.local")
5108
+ });
5109
+ dotenv.config({ path: path9.join(projectRoot, ".env") });
5110
+ }
4877
5111
  } catch {
4878
5112
  }
4879
5113
  dotenv.config({
4880
- path: path8.join(os3.homedir(), ".onexthm", ".env"),
5114
+ path: path9.join(os.homedir(), ".onexthm", ".env"),
4881
5115
  quiet: true
4882
5116
  });
4883
5117
  var require2 = createRequire(import.meta.url);
@@ -4937,7 +5171,10 @@ program.command("config").description("Configure OneX CLI credentials (AWS, API
4937
5171
  program.command("login").description("Login to OneX platform").action(loginCommand);
4938
5172
  program.command("logout").description("Logout from OneX platform").action(logoutCommand);
4939
5173
  program.command("whoami").description("Show current logged-in developer").action(whoamiCommand);
4940
- program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").action(publishCommand);
5174
+ program.command("publish").description("Build, scan, and publish theme to marketplace (requires login)").option("-t, --theme <path>", "Theme directory path").option(
5175
+ "--bump <type>",
5176
+ "Auto-bump version before publish (patch|minor|major)"
5177
+ ).action(publishCommand);
4941
5178
  program.configureOutput({
4942
5179
  writeErr: (str) => process.stderr.write(chalk4.red(str))
4943
5180
  });