@deneb-ui/cli 2.0.7 → 2.0.8

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 (3) hide show
  1. package/README.md +56 -22
  2. package/bin/index.js +554 -223
  3. package/package.json +10 -3
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  > The premier visual-first React component ecosystem.
7
7
  > Proudly presented by **DENEB-UI Collaborate with FIVORA**.
8
8
 
9
- [![Framework: DENEB UI](https://img.shields.io/badge/Framework-DENEB_UI_v2.0-blue.svg)](https://github.com/chamikathereal)
9
+ [![Framework: DENEB UI](https://img.shields.io/badge/Framework-DENEB_UI_v2.0-blue.svg)](https://github.com/deneb-ui/ui)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-teal.svg)](https://opensource.org/licenses/MIT)
11
11
 
12
12
  ---
@@ -50,50 +50,84 @@ npx @deneb-ui/cli add dialog
50
50
  npx @deneb-ui/cli add all
51
51
  ```
52
52
 
53
- ### 2. `deneb init` (Configure an Existing Project)
54
- Converts any **existing Next.js project** into a compliant DENEB storefront template:
53
+ ### 1. `deneb init` (Initialize Existing Project)
54
+ Converts and configures any **ongoing Next.js project** for the Fivora platform:
55
+ - Generates `fivora-template.json` (Version 2 contract with strict visual editing)
56
+ - Creates `src/data/site-data.json` with merchant defaults
57
+ - Injects npm scripts (`lab`, `validate`, `zip`, `validate-and-zip`, `update:deneb`)
58
+ - Installs `@deneb-ui/ui` and `@deneb-ui/cli`
55
59
 
56
60
  ```bash
57
61
  npx @deneb-ui/cli init
62
+ # Or: deneb init
58
63
  ```
59
64
 
60
- ### 3. `deneb create <project-name>` (Start from Scratch)
61
- Scaffolds a brand-new Next.js App Router storefront with Tailwind CSS and `@deneb-ui/ui`:
65
+ ### 2. `deneb update` (Update Packages & DENEB Components)
66
+ Keeps your project up to date by updating both npm packages and installed DENEB components:
67
+ - Updates `@deneb-ui/ui` and `@deneb-ui/cli` to the latest releases
68
+ - Automatically scans `src/components/ui/` and synchronizes existing DENEB UI components with the latest definitions
62
69
 
63
70
  ```bash
64
- npx @deneb-ui/cli create my-store
65
- ```
66
-
67
- ### 4. `deneb lab` (Local Visual Editing Lab)
68
- Launches the interactive visual editing test lab:
69
-
70
- ```bash
71
- npm run lab
72
- # Or: deneb lab .
71
+ npm run update:deneb
72
+ # Or: deneb update
73
73
  ```
74
74
 
75
- ### 5. `deneb validate` (Preflight Contract Validator)
76
- Runs preflight checks to guarantee 100% compliance with visual editing and platform rules:
75
+ ### 3. `deneb validate` (Preflight Contract Validator)
76
+ Runs Fivora preflight verification against manifest contracts, static export fixtures, and visual editing markers (`data-fivora-path`, `data-fivora-page`):
77
77
 
78
78
  ```bash
79
79
  npm run validate
80
80
  # Or: deneb validate .
81
+ # Or validate and immediately bundle on success:
82
+ deneb validate --zip
81
83
  ```
82
84
 
83
- ### 6. `deneb zip` / `deneb pack` (Clean Package Generator)
84
- Creates a clean, production-ready template ZIP excluding `node_modules`, `.next`, `.git`, `.env*`:
85
+ ### 4. `deneb zip` / `deneb pack` (Clean Package Generator)
86
+ Creates a clean, upload-ready `fivora-template.zip` excluding unnecessary directories and secret files (`node_modules`, `.next`, `.git`, `.env*`, `.cache`, `.turbo`, logs):
85
87
 
86
88
  ```bash
87
89
  npm run zip
88
90
  # Or: deneb zip .
89
91
  ```
90
92
 
91
- ### 7. `deneb update` (Dependency Updater)
92
- Updates `@deneb-ui/ui` and `@deneb-ui/cli` to the latest versions:
93
+ ### 5. `deneb validate-and-zip` (One-Step Preflight Check & Clean ZIP)
94
+ The safest, recommended command for releasing templates. Runs full preflight validation; if and only if all platform checks pass 100%, it bundles a clean `fivora-template.zip`:
93
95
 
94
96
  ```bash
95
- npm run update:deneb
96
- # Or: deneb update
97
+ npm run validate-and-zip
98
+ # Or: deneb validate-and-zip .
99
+ # Or: deneb validate and zip
100
+ ```
101
+
102
+ ### 6. `deneb add <component>` (Component Registry)
103
+ Add or update production-ready DENEB UI components into `src/components/ui/`:
104
+
105
+ ```bash
106
+ # List available components:
107
+ npx @deneb-ui/cli add list
108
+
109
+ # Add specific components:
110
+ npx @deneb-ui/cli add product-card
111
+ npx @deneb-ui/cli add contact-actions
112
+ npx @deneb-ui/cli add location-card
113
+ npx @deneb-ui/cli add whatsapp-button
114
+ npx @deneb-ui/cli add dialog
115
+ npx @deneb-ui/cli add all
116
+ ```
117
+
118
+ ### 7. `deneb create <project-name>` (Start from Scratch)
119
+ Scaffolds a brand-new Next.js App Router storefront pre-configured with Tailwind CSS and `@deneb-ui/ui`:
120
+
121
+ ```bash
122
+ npx @deneb-ui/cli create my-store
123
+ ```
124
+
125
+ ### 8. `deneb lab` (Local Visual Editing Lab)
126
+ Launches the interactive visual editing test lab simulating Fivora editor messages:
127
+
128
+ ```bash
129
+ npm run lab
130
+ # Or: deneb lab .
97
131
  ```
98
132
 
99
133
  ---
package/bin/index.js CHANGED
@@ -5,7 +5,6 @@ const fs = require('node:fs');
5
5
  const { spawnSync } = require('node:child_process');
6
6
 
7
7
  const args = process.argv.slice(2);
8
- const command = args[0];
9
8
 
10
9
  const toolsDir = path.join(__dirname, '..', 'src', 'tools');
11
10
 
@@ -154,8 +153,8 @@ function createTemplate(targetName) {
154
153
  console.log(` npm run validate # Run Fivora preflight checks`);
155
154
  console.log(` npm run zip # Create clean upload-ready ZIP\n`);
156
155
  } else {
157
- console.log(`\n🚀 Scaffolding new Fivora Template via @fivora/create-template...\n`);
158
- const res = spawnSync('npx', ['--yes', '@fivora/create-template', projectName], {
156
+ console.log(`\n🚀 Scaffolding new DENEB Storefront Template via @deneb-ui/create-template...\n`);
157
+ const res = spawnSync('npx', ['--yes', '@deneb-ui/create-template', projectName], {
159
158
  stdio: 'inherit',
160
159
  shell: process.platform === 'win32',
161
160
  });
@@ -357,19 +356,170 @@ function getDefaultSiteData(projectName, pages) {
357
356
  };
358
357
  }
359
358
 
359
+ function getComponentRegistry(importPkg) {
360
+ return {
361
+ 'button': {
362
+ file: 'Button.tsx',
363
+ component: 'Button',
364
+ code: `'use client';\n\nimport { Button, type EditableButtonProps } from '${importPkg}';\n\nexport { Button, type EditableButtonProps };\n`,
365
+ },
366
+ 'dialog': {
367
+ file: 'Dialog.tsx',
368
+ component: 'Dialog',
369
+ code: `'use client';\n\nimport { Dialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter, type DialogProps } from '${importPkg}';\n\nexport { Dialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter, type DialogProps };\n`,
370
+ },
371
+ 'card': {
372
+ file: 'Card.tsx',
373
+ component: 'Card',
374
+ code: `'use client';\n\nimport { Card, type EditableCardProps } from '${importPkg}';\n\nexport { Card, type EditableCardProps };\n`,
375
+ },
376
+ 'product-card': {
377
+ file: 'ProductCard.tsx',
378
+ component: 'EditableProductCard',
379
+ code: `'use client';\n\nimport { EditableProductCard, type EditableProductCardProps } from '${importPkg}';\n\nexport function ProductCard(props: EditableProductCardProps) {\n return <EditableProductCard {...props} />;\n}\n`,
380
+ },
381
+ 'pricing-card': {
382
+ file: 'PricingCard.tsx',
383
+ component: 'EditablePricingCard',
384
+ code: `'use client';\n\nimport { EditablePricingCard, type EditablePricingCardProps } from '${importPkg}';\n\nexport function PricingCard(props: EditablePricingCardProps) {\n return <EditablePricingCard {...props} />;\n}\n`,
385
+ },
386
+ 'testimonial-card': {
387
+ file: 'TestimonialCard.tsx',
388
+ component: 'EditableTestimonialCard',
389
+ code: `'use client';\n\nimport { EditableTestimonialCard, type EditableTestimonialCardProps } from '${importPkg}';\n\nexport function TestimonialCard(props: EditableTestimonialCardProps) {\n return <EditableTestimonialCard {...props} />;\n}\n`,
390
+ },
391
+ 'contact-form': {
392
+ file: 'ContactForm.tsx',
393
+ component: 'EditableContactForm',
394
+ code: `'use client';\n\nimport { EditableContactForm, type EditableContactFormProps } from '${importPkg}';\n\nexport function ContactForm(props: EditableContactFormProps) {\n return <EditableContactForm {...props} />;\n}\n`,
395
+ },
396
+ 'faq': {
397
+ file: 'FAQAccordion.tsx',
398
+ component: 'EditableFAQAccordion',
399
+ code: `'use client';\n\nimport { EditableFAQAccordion, EditableFAQItem, type EditableFAQAccordionProps } from '${importPkg}';\n\nexport function FAQAccordion(props: EditableFAQAccordionProps) {\n return <EditableFAQAccordion {...props} />;\n}\n\nexport { EditableFAQItem };\n`,
400
+ },
401
+ 'navbar': {
402
+ file: 'Navbar.tsx',
403
+ component: 'EditableNavbar',
404
+ code: `'use client';\n\nimport { EditableNavbar, type EditableNavbarProps } from '${importPkg}';\n\nexport function Navbar(props: EditableNavbarProps) {\n return <EditableNavbar {...props} />;\n}\n`,
405
+ },
406
+ 'footer': {
407
+ file: 'Footer.tsx',
408
+ component: 'EditableFooter',
409
+ code: `'use client';\n\nimport { EditableFooter, type EditableFooterProps } from '${importPkg}';\n\nexport function Footer(props: EditableFooterProps) {\n return <EditableFooter {...props} />;\n}\n`,
410
+ },
411
+ 'hero': {
412
+ file: 'Hero.tsx',
413
+ component: 'EditableHeroCentered',
414
+ code: `'use client';\n\nimport { EditableHeroCentered, EditableHeroSplit, type EditableHeroCenteredProps, type EditableHeroSplitProps } from '${importPkg}';\n\nexport function HeroCentered(props: EditableHeroCenteredProps) {\n return <EditableHeroCentered {...props} />;\n}\n\nexport function HeroSplit(props: EditableHeroSplitProps) {\n return <EditableHeroSplit {...props} />;\n}\n`,
415
+ },
416
+ 'whatsapp-button': {
417
+ file: 'WhatsAppButton.tsx',
418
+ component: 'WhatsAppButton',
419
+ code: `'use client';\n\nimport { WhatsAppButton, type WhatsAppButtonProps } from '${importPkg}';\n\nexport { WhatsAppButton, type WhatsAppButtonProps };\n`,
420
+ },
421
+ 'phone-button': {
422
+ file: 'PhoneButton.tsx',
423
+ component: 'PhoneButton',
424
+ code: `'use client';\n\nimport { PhoneButton, type PhoneButtonProps } from '${importPkg}';\n\nexport { PhoneButton, type PhoneButtonProps };\n`,
425
+ },
426
+ 'email-button': {
427
+ file: 'EmailButton.tsx',
428
+ component: 'EmailButton',
429
+ code: `'use client';\n\nimport { EmailButton, type EmailButtonProps } from '${importPkg}';\n\nexport { EmailButton, type EmailButtonProps };\n`,
430
+ },
431
+ 'contact-actions': {
432
+ file: 'ContactActions.tsx',
433
+ component: 'ContactActions',
434
+ code: `'use client';\n\nimport { ContactActions, type ContactActionsProps } from '${importPkg}';\n\nexport { ContactActions, type ContactActionsProps };\n`,
435
+ },
436
+ 'location-card': {
437
+ file: 'LocationCard.tsx',
438
+ component: 'LocationCard',
439
+ code: `'use client';\n\nimport { LocationCard, type LocationCardProps } from '${importPkg}';\n\nexport { LocationCard, type LocationCardProps };\n`,
440
+ },
441
+ 'location-link': {
442
+ file: 'LocationLink.tsx',
443
+ component: 'LocationLink',
444
+ code: `'use client';\n\nimport { LocationLink, type LocationLinkProps } from '${importPkg}';\n\nexport { LocationLink, type LocationLinkProps };\n`,
445
+ },
446
+ 'map-embed': {
447
+ file: 'MapEmbed.tsx',
448
+ component: 'MapEmbed',
449
+ code: `'use client';\n\nimport { MapEmbed, type MapEmbedProps } from '${importPkg}';\n\nexport { MapEmbed, type MapEmbedProps };\n`,
450
+ },
451
+ 'social-links': {
452
+ file: 'SocialLinks.tsx',
453
+ component: 'SocialLinks',
454
+ code: `'use client';\n\nimport { SocialLinks, type SocialLinksProps } from '${importPkg}';\n\nexport { SocialLinks, type SocialLinksProps };\n`,
455
+ },
456
+ 'social-button': {
457
+ file: 'SocialButton.tsx',
458
+ component: 'SocialButton',
459
+ code: `'use client';\n\nimport { SocialButton, type SocialButtonProps } from '${importPkg}';\n\nexport { SocialButton, type SocialButtonProps };\n`,
460
+ },
461
+ 'business-hours': {
462
+ file: 'BusinessHours.tsx',
463
+ component: 'BusinessHours',
464
+ code: `'use client';\n\nimport { BusinessHours, type BusinessHoursProps } from '${importPkg}';\n\nexport { BusinessHours, type BusinessHoursProps };\n`,
465
+ },
466
+ 'announcement-bar': {
467
+ file: 'AnnouncementBar.tsx',
468
+ component: 'EditableAnnouncementBar',
469
+ code: `'use client';\n\nimport { EditableAnnouncementBar, AnnouncementBar, type EditableAnnouncementBarProps } from '${importPkg}';\n\nexport { EditableAnnouncementBar, AnnouncementBar, type EditableAnnouncementBarProps };\n`,
470
+ },
471
+ 'category-pills': {
472
+ file: 'CategoryPills.tsx',
473
+ component: 'EditableCategoryPills',
474
+ code: `'use client';\n\nimport { EditableCategoryPills, CategoryPills, type EditableCategoryPillsProps } from '${importPkg}';\n\nexport { EditableCategoryPills, CategoryPills, type EditableCategoryPillsProps };\n`,
475
+ },
476
+ 'floating-contact-widget': {
477
+ file: 'FloatingContactWidget.tsx',
478
+ component: 'FloatingContactWidget',
479
+ code: `'use client';\n\nimport { FloatingContactWidget, type FloatingContactWidgetProps } from '${importPkg}';\n\nexport { FloatingContactWidget, type FloatingContactWidgetProps };\n`,
480
+ },
481
+ 'sticky-mobile-bar': {
482
+ file: 'StickyMobileBar.tsx',
483
+ component: 'StickyMobileBar',
484
+ code: `'use client';\n\nimport { StickyMobileBar, type StickyMobileBarProps, type StickyMobileBarAction } from '${importPkg}';\n\nexport { StickyMobileBar, type StickyMobileBarProps, type StickyMobileBarAction };\n`,
485
+ },
486
+ 'trust-badges': {
487
+ file: 'TrustBadges.tsx',
488
+ component: 'TrustBadges',
489
+ code: `'use client';\n\nimport { TrustBadges, type TrustBadgesProps, type TrustBadgeItem } from '${importPkg}';\n\nexport { TrustBadges, type TrustBadgesProps, type TrustBadgeItem };\n`,
490
+ },
491
+ 'product-quickview': {
492
+ file: 'ProductQuickView.tsx',
493
+ component: 'ProductQuickView',
494
+ code: `'use client';\n\nimport { ProductQuickView, type ProductQuickViewProps, type ProductQuickViewItem } from '${importPkg}';\n\nexport { ProductQuickView, type ProductQuickViewProps, type ProductQuickViewItem };\n`,
495
+ },
496
+ 'cookie-consent': {
497
+ file: 'CookieConsentBanner.tsx',
498
+ component: 'CookieConsentBanner',
499
+ code: `'use client';\n\nimport { CookieConsentBanner, type CookieConsentBannerProps } from '${importPkg}';\n\nexport { CookieConsentBanner, type CookieConsentBannerProps };\n`,
500
+ },
501
+ 'deneb-action': {
502
+ file: 'DenebAction.tsx',
503
+ component: 'DenebAction',
504
+ code: `'use client';\n\nimport { DenebAction, type DenebActionProps } from '${importPkg}';\n\nexport { DenebAction, type DenebActionProps };\n`,
505
+ },
506
+ };
507
+ }
508
+
360
509
  function initProject(targetInput) {
361
510
  const targetDir = path.resolve(process.cwd(), targetInput || '.');
362
511
  const pkgPath = path.join(targetDir, 'package.json');
363
512
 
364
513
  console.log('\n' + createBox([
365
- '\x1b[1m\x1b[37mFIVORA TEMPLATE INITIALIZER\x1b[0m',
366
- '\x1b[90mConfigure existing Next.js app for Fivora\x1b[0m'
367
- ], 55) + '\n');
514
+ '\x1b[1m\x1b[37mDENEB TEMPLATE INITIALIZER\x1b[0m',
515
+ '\x1b[90mConfigure existing Next.js project for Fivora Platform\x1b[0m',
516
+ '\x1b[90mPowered by DENEB-UI Collaborate with FIVORA\x1b[0m'
517
+ ], 58) + '\n');
368
518
 
369
519
  if (!fs.existsSync(pkgPath)) {
370
520
  console.error(`\x1b[31mError:\x1b[0m No package.json found in "${targetDir}".`);
371
- console.error(`\nPlease run 'fivora init' inside your Next.js project root, or scaffold a new project with:`);
372
- console.error(` \x1b[36mnpx @fivora/create-template <app-name>\x1b[0m\n`);
521
+ console.error(`\nPlease run 'deneb init' inside your Next.js project root, or scaffold a new project with:`);
522
+ console.error(` \x1b[36mnpx @deneb-ui/create-template <app-name>\x1b[0m\n`);
373
523
  process.exit(1);
374
524
  }
375
525
 
@@ -396,19 +546,14 @@ function initProject(targetInput) {
396
546
  // 1. Scan / Detect Pages
397
547
  const detectedPages = detectPages(targetDir);
398
548
 
399
- // 2. Generate fivora-template.json (or keep fivora-template.json if present)
400
- const fivoraManifestPath = path.join(targetDir, 'fivora-template.json');
401
- const legacyManifestPath = path.join(targetDir, 'fivora-template.json');
402
- const manifestPath = fs.existsSync(legacyManifestPath) && !fs.existsSync(fivoraManifestPath)
403
- ? legacyManifestPath
404
- : fivoraManifestPath;
405
-
549
+ // 2. Generate fivora-template.json (version 2 contract)
550
+ const manifestPath = path.join(targetDir, 'fivora-template.json');
406
551
  if (!fs.existsSync(manifestPath)) {
407
552
  const manifest = getDefaultManifest(projectName, detectedPages);
408
553
  fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
409
554
  console.log(`\x1b[32m✔ Created\x1b[0m fivora-template.json (version 2, strict visual editing contract)`);
410
555
  } else {
411
- console.log(`\x1b[90m⏩ Kept existing\x1b[0m ${path.basename(manifestPath)}`);
556
+ console.log(`\x1b[90m⏩ Kept existing\x1b[0m fivora-template.json`);
412
557
  }
413
558
 
414
559
  // 3. Generate siteDataFile
@@ -433,24 +578,25 @@ function initProject(targetInput) {
433
578
  // 4. Update package.json scripts
434
579
  pkg.scripts = pkg.scripts || {};
435
580
  const scriptsToAdd = {
436
- 'lab': 'fivora lab .',
437
- 'validate': 'fivora validate .',
438
- 'zip': 'fivora zip .',
439
- 'package:template': 'fivora package .',
440
- 'update:fivora': 'fivora update',
581
+ 'lab': 'deneb lab .',
582
+ 'validate': 'deneb validate .',
583
+ 'zip': 'deneb zip .',
584
+ 'validate-and-zip': 'deneb validate-and-zip .',
585
+ 'package:template': 'deneb package .',
586
+ 'update:deneb': 'deneb update',
441
587
  };
442
588
  let addedCount = 0;
443
589
  for (const [key, val] of Object.entries(scriptsToAdd)) {
444
- if (!pkg.scripts[key]) {
590
+ if (!pkg.scripts[key] || pkg.scripts[key].startsWith('fivora ')) {
445
591
  pkg.scripts[key] = val;
446
592
  addedCount++;
447
593
  }
448
594
  }
449
595
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
450
596
  if (addedCount > 0) {
451
- console.log(`\x1b[32m✔ Injected\x1b[0m Fivora scripts into package.json (lab, validate, zip, package:template, update:fivora)`);
597
+ console.log(`\x1b[32m✔ Configured\x1b[0m DENEB scripts in package.json (lab, validate, zip, validate-and-zip, package:template, update:deneb)`);
452
598
  } else {
453
- console.log(`\x1b[90m⏩ Fivora scripts already present\x1b[0m in package.json`);
599
+ console.log(`\x1b[90m⏩ DENEB scripts already present\x1b[0m in package.json`);
454
600
  }
455
601
 
456
602
  // 5. Check next.config for static export
@@ -474,47 +620,53 @@ function initProject(targetInput) {
474
620
  console.log(` \x1b[90mconst nextConfig = { output: 'export' };\x1b[0m`);
475
621
  }
476
622
 
477
- // 6. Install Fivora packages if missing
623
+ // 6. Install DENEB packages if missing
478
624
  const skipInstall = process.argv.includes('--skip-install');
479
- const hasEditable = Boolean(
480
- (pkg.dependencies && pkg.dependencies['@fivora/editable-components']) ||
481
- (pkg.devDependencies && pkg.devDependencies['@fivora/editable-components'])
625
+ const hasUi = Boolean(
626
+ (pkg.dependencies && (pkg.dependencies['@deneb-ui/ui'] || pkg.dependencies['@deneb/ui'] || pkg.dependencies['@fivora/editable-components'])) ||
627
+ (pkg.devDependencies && (pkg.devDependencies['@deneb-ui/ui'] || pkg.devDependencies['@deneb/ui'] || pkg.devDependencies['@fivora/editable-components']))
482
628
  );
483
629
  const hasCli = Boolean(
484
- (pkg.devDependencies && pkg.devDependencies['@fivora/cli']) ||
485
- (pkg.dependencies && pkg.dependencies['@fivora/cli'])
630
+ (pkg.devDependencies && (pkg.devDependencies['@deneb-ui/cli'] || pkg.devDependencies['@fivora/cli'])) ||
631
+ (pkg.dependencies && (pkg.dependencies['@deneb-ui/cli'] || pkg.dependencies['@fivora/cli']))
486
632
  );
487
633
 
488
- if (!skipInstall && (!hasEditable || !hasCli)) {
489
- const toInstall = [];
490
- if (!hasEditable) toInstall.push('@fivora/editable-components');
491
- if (!hasCli) toInstall.push('@fivora/cli');
634
+ if (!skipInstall && (!hasUi || !hasCli)) {
635
+ const depsToInstall = [];
636
+ const devDepsToInstall = [];
637
+ if (!hasUi) depsToInstall.push('@deneb-ui/ui@latest');
638
+ if (!hasCli) devDepsToInstall.push('@deneb-ui/cli@latest');
492
639
 
493
- console.log(`\n📦 Installing ${toInstall.join(' and ')}...`);
494
- const installRes = spawnSync('npm', ['install', ...toInstall], {
495
- cwd: targetDir,
496
- stdio: 'inherit',
497
- shell: process.platform === 'win32',
498
- });
499
- if (installRes.status === 0) {
500
- console.log(`\x1b[32m✔ Packages installed successfully!\x1b[0m`);
501
- } else {
502
- console.log(`\x1b[33mNote: npm install exited with code ${installRes.status}. You can run 'npm i ${toInstall.join(' ')}' manually.\x1b[0m`);
640
+ if (depsToInstall.length > 0) {
641
+ console.log(`\n📦 Installing ${depsToInstall.join(' ')}...`);
642
+ spawnSync('npm', ['install', ...depsToInstall], {
643
+ cwd: targetDir,
644
+ stdio: 'inherit',
645
+ shell: process.platform === 'win32',
646
+ });
647
+ }
648
+ if (devDepsToInstall.length > 0) {
649
+ console.log(`\n📦 Installing (dev) ${devDepsToInstall.join(' ')}...`);
650
+ spawnSync('npm', ['install', '-D', ...devDepsToInstall], {
651
+ cwd: targetDir,
652
+ stdio: 'inherit',
653
+ shell: process.platform === 'win32',
654
+ });
503
655
  }
504
656
  }
505
657
 
506
- console.log(`\n\x1b[32m✔ Template initialization complete!\x1b[0m`);
658
+ console.log(`\n\x1b[32m✔ Project initialization complete!\x1b[0m`);
507
659
  console.log(`\nYou can now run:`);
508
- console.log(` \x1b[36mnpm run lab\x1b[0m \x1b[90m# Launch Local Visual Editing Lab\x1b[0m`);
509
- console.log(` \x1b[36mnpm run validate\x1b[0m \x1b[90m# Check compliance with Fivora contract\x1b[0m`);
510
- console.log(` \x1b[36mnpm run zip\x1b[0m \x1b[90m# Package clean ZIP for 1-click upload\x1b[0m\n`);
660
+ console.log(` \x1b[36mnpm run lab\x1b[0m \x1b[90m# Launch Local Visual Editing Lab\x1b[0m`);
661
+ console.log(` \x1b[36mnpm run validate\x1b[0m \x1b[90m# Check compliance with Fivora contract\x1b[0m`);
662
+ console.log(` \x1b[36mnpm run zip\x1b[0m \x1b[90m# Package clean ZIP for 1-click upload\x1b[0m`);
663
+ console.log(` \x1b[36mnpm run validate-and-zip\x1b[0m \x1b[90m# Validate preflight and bundle clean ZIP in 1 step\x1b[0m\n`);
511
664
  }
512
665
 
513
-
514
666
  function updateDependencies(cmdArgs = []) {
515
667
  let targetDir = '.';
516
668
  let isLocal = false;
517
- let localPath = '/mnt/GAMES/GitHub/fivora_package';
669
+ let localPath = path.resolve(__dirname, '..', '..', '..');
518
670
 
519
671
  for (let i = 0; i < cmdArgs.length; i++) {
520
672
  const arg = cmdArgs[i];
@@ -548,52 +700,79 @@ function updateDependencies(cmdArgs = []) {
548
700
  }
549
701
 
550
702
  console.log('\n' + createBox([
551
- '\x1b[1m\x1b[37mFIVORA PACKAGE UPDATER\x1b[0m'
552
- ], 55) + '\n');
703
+ '\x1b[1m\x1b[37mDENEB PACKAGE & COMPONENT UPDATER\x1b[0m',
704
+ '\x1b[90mUpdate DENEB packages and UI components to latest\x1b[0m',
705
+ '\x1b[90mPowered by DENEB-UI Collaborate with FIVORA\x1b[0m'
706
+ ], 58) + '\n');
553
707
 
554
- const currentEditable = String(pkg.dependencies?.['@fivora/editable-components'] || pkg.devDependencies?.['@fivora/editable-components'] || '');
555
- const currentCli = String(pkg.dependencies?.['@fivora/cli'] || pkg.devDependencies?.['@fivora/cli'] || '');
556
- if (!isLocal && (currentEditable.startsWith('file:') || currentCli.startsWith('file:'))) {
557
- isLocal = true;
708
+ let importPkg = '@deneb-ui/ui';
709
+ if (pkg.dependencies?.['@deneb-ui/ui'] || pkg.devDependencies?.['@deneb-ui/ui']) {
710
+ importPkg = '@deneb-ui/ui';
711
+ } else if (pkg.dependencies?.['@deneb/ui'] || pkg.devDependencies?.['@deneb/ui']) {
712
+ importPkg = '@deneb/ui';
558
713
  }
559
714
 
715
+ // 1. Update npm packages
560
716
  if (isLocal) {
561
- const editableDir = path.join(localPath, 'packages', 'editable-components');
717
+ const uiDir = path.join(localPath, 'packages', 'deneb-ui');
562
718
  const cliDir = path.join(localPath, 'cli', 'fivora-cli');
563
719
 
564
- if (fs.existsSync(editableDir) && fs.existsSync(cliDir)) {
565
- console.log('🔄 Updating from local Fivora workspace:');
566
- console.log(' - ' + editableDir);
720
+ if (fs.existsSync(uiDir) && fs.existsSync(cliDir)) {
721
+ console.log('🔄 Updating from local workspace:');
722
+ console.log(' - ' + uiDir);
567
723
  console.log(' - ' + cliDir + '\n');
568
724
 
569
- const installRes = spawnSync('npm', ['install', editableDir, cliDir], {
725
+ const installRes = spawnSync('npm', ['install', uiDir, cliDir], {
570
726
  cwd: targetDir,
571
727
  stdio: 'inherit',
572
728
  shell: process.platform === 'win32',
573
729
  });
574
730
 
575
731
  if (installRes.status === 0) {
576
- console.log('\n\x1b[32m✔ Local Fivora packages re-linked & updated successfully!\x1b[0m\n');
732
+ console.log('\n\x1b[32m✔ Local DENEB packages re-linked & updated successfully!\x1b[0m\n');
577
733
  } else {
578
734
  console.log('\n\x1b[31m✖ Failed to link local packages (exit code: ' + installRes.status + ')\x1b[0m\n');
579
735
  }
580
- return;
581
736
  }
582
- }
737
+ } else {
738
+ console.log('📦 Updating @deneb-ui/ui and @deneb-ui/cli to latest from npm...');
739
+ const packagesToInstall = ['@deneb-ui/ui@latest', '@deneb-ui/cli@latest'];
583
740
 
584
- console.log('📦 Updating @fivora/editable-components and @fivora/cli to latest from npm...');
585
- const packagesToInstall = ['@fivora/editable-components@latest', '@fivora/cli@latest'];
741
+ const installRes = spawnSync('npm', ['install', ...packagesToInstall], {
742
+ cwd: targetDir,
743
+ stdio: 'inherit',
744
+ shell: process.platform === 'win32',
745
+ });
586
746
 
587
- const installRes = spawnSync('npm', ['install', ...packagesToInstall], {
588
- cwd: targetDir,
589
- stdio: 'inherit',
590
- shell: process.platform === 'win32',
591
- });
747
+ if (installRes.status === 0) {
748
+ console.log('\n\x1b[32m✔ DENEB packages updated to latest versions successfully!\x1b[0m\n');
749
+ } else {
750
+ console.log('\n\x1b[31m✖ npm install failed with exit code ' + installRes.status + '\x1b[0m\n');
751
+ }
752
+ }
592
753
 
593
- if (installRes.status === 0) {
594
- console.log('\n\x1b[32m✔ Fivora packages updated to latest versions successfully!\x1b[0m\n');
595
- } else {
596
- console.log('\n\x1b[31m✖ npm install failed with exit code ' + installRes.status + '\x1b[0m\n');
754
+ // 2. Update DENEB components in src/components/ui/
755
+ const uiDir = path.join(targetDir, 'src', 'components', 'ui');
756
+ if (fs.existsSync(uiDir)) {
757
+ console.log('🧩 Inspecting and refreshing installed DENEB UI components in src/components/ui/...\n');
758
+ const registry = getComponentRegistry(importPkg);
759
+ const existingFiles = fs.readdirSync(uiDir);
760
+ let updatedComponentsCount = 0;
761
+
762
+ for (const [key, item] of Object.entries(registry)) {
763
+ if (existingFiles.includes(item.file)) {
764
+ const filePath = path.join(uiDir, item.file);
765
+ fs.writeFileSync(filePath, item.code);
766
+ console.log(` \x1b[32m✔ Updated\x1b[0m src/components/ui/${item.file} (${key})`);
767
+ updatedComponentsCount++;
768
+ }
769
+ }
770
+
771
+ if (updatedComponentsCount > 0) {
772
+ console.log(`\n\x1b[32m✔ Successfully updated ${updatedComponentsCount} DENEB component(s) to the latest definitions!\x1b[0m\n`);
773
+ } else {
774
+ console.log(` \x1b[90mNo existing DENEB UI components found in src/components/ui to update.\x1b[0m\n`);
775
+ }
597
776
  }
598
777
  }
599
778
 
@@ -619,133 +798,7 @@ function addComponent(componentName, targetDirInput) {
619
798
  }
620
799
  }
621
800
 
622
- const REGISTRY = {
623
- 'button': {
624
- file: 'Button.tsx',
625
- component: 'Button',
626
- code: `'use client';\n\nimport { Button, type EditableButtonProps } from '${importPkg}';\n\nexport { Button, type EditableButtonProps };\n`,
627
- },
628
- 'dialog': {
629
- file: 'Dialog.tsx',
630
- component: 'Dialog',
631
- code: `'use client';\n\nimport { Dialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter, type DialogProps } from '${importPkg}';\n\nexport { Dialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter, type DialogProps };\n`,
632
- },
633
- 'card': {
634
- file: 'Card.tsx',
635
- component: 'Card',
636
- code: `'use client';\n\nimport { Card, type EditableCardProps } from '${importPkg}';\n\nexport { Card, type EditableCardProps };\n`,
637
- },
638
- 'product-card': {
639
- file: 'ProductCard.tsx',
640
- component: 'EditableProductCard',
641
- code: `'use client';\n\nimport { EditableProductCard, type EditableProductCardProps } from '${importPkg}';\n\nexport function ProductCard(props: EditableProductCardProps) {\n return <EditableProductCard {...props} />;\n}\n`,
642
- },
643
- 'pricing-card': {
644
- file: 'PricingCard.tsx',
645
- component: 'EditablePricingCard',
646
- code: `'use client';\n\nimport { EditablePricingCard, type EditablePricingCardProps } from '${importPkg}';\n\nexport function PricingCard(props: EditablePricingCardProps) {\n return <EditablePricingCard {...props} />;\n}\n`,
647
- },
648
- 'testimonial-card': {
649
- file: 'TestimonialCard.tsx',
650
- component: 'EditableTestimonialCard',
651
- code: `'use client';\n\nimport { EditableTestimonialCard, type EditableTestimonialCardProps } from '${importPkg}';\n\nexport function TestimonialCard(props: EditableTestimonialCardProps) {\n return <EditableTestimonialCard {...props} />;\n}\n`,
652
- },
653
- 'contact-form': {
654
- file: 'ContactForm.tsx',
655
- component: 'EditableContactForm',
656
- code: `'use client';\n\nimport { EditableContactForm, type EditableContactFormProps } from '${importPkg}';\n\nexport function ContactForm(props: EditableContactFormProps) {\n return <EditableContactForm {...props} />;\n}\n`,
657
- },
658
- 'faq': {
659
- file: 'FAQAccordion.tsx',
660
- component: 'EditableFAQAccordion',
661
- code: `'use client';\n\nimport { EditableFAQAccordion, EditableFAQItem, type EditableFAQAccordionProps } from '${importPkg}';\n\nexport function FAQAccordion(props: EditableFAQAccordionProps) {\n return <EditableFAQAccordion {...props} />;\n}\n\nexport { EditableFAQItem };\n`,
662
- },
663
- 'navbar': {
664
- file: 'Navbar.tsx',
665
- component: 'EditableNavbar',
666
- code: `'use client';\n\nimport { EditableNavbar, type EditableNavbarProps } from '${importPkg}';\n\nexport function Navbar(props: EditableNavbarProps) {\n return <EditableNavbar {...props} />;\n}\n`,
667
- },
668
- 'footer': {
669
- file: 'Footer.tsx',
670
- component: 'EditableFooter',
671
- code: `'use client';\n\nimport { EditableFooter, type EditableFooterProps } from '${importPkg}';\n\nexport function Footer(props: EditableFooterProps) {\n return <EditableFooter {...props} />;\n}\n`,
672
- },
673
- 'hero': {
674
- file: 'Hero.tsx',
675
- component: 'EditableHeroCentered',
676
- code: `'use client';\n\nimport { EditableHeroCentered, EditableHeroSplit, type EditableHeroCenteredProps, type EditableHeroSplitProps } from '${importPkg}';\n\nexport function HeroCentered(props: EditableHeroCenteredProps) {\n return <EditableHeroCentered {...props} />;\n}\n\nexport function HeroSplit(props: EditableHeroSplitProps) {\n return <EditableHeroSplit {...props} />;\n}\n`,
677
- },
678
- 'whatsapp-button': {
679
- file: 'WhatsAppButton.tsx',
680
- component: 'WhatsAppButton',
681
- code: `'use client';\n\nimport { WhatsAppButton, type WhatsAppButtonProps } from '${importPkg}';\n\nexport { WhatsAppButton, type WhatsAppButtonProps };\n`,
682
- },
683
- 'phone-button': {
684
- file: 'PhoneButton.tsx',
685
- component: 'PhoneButton',
686
- code: `'use client';\n\nimport { PhoneButton, type PhoneButtonProps } from '${importPkg}';\n\nexport { PhoneButton, type PhoneButtonProps };\n`,
687
- },
688
- 'email-button': {
689
- file: 'EmailButton.tsx',
690
- component: 'EmailButton',
691
- code: `'use client';\n\nimport { EmailButton, type EmailButtonProps } from '${importPkg}';\n\nexport { EmailButton, type EmailButtonProps };\n`,
692
- },
693
- 'contact-actions': {
694
- file: 'ContactActions.tsx',
695
- component: 'ContactActions',
696
- code: `'use client';\n\nimport { ContactActions, type ContactActionsProps } from '${importPkg}';\n\nexport { ContactActions, type ContactActionsProps };\n`,
697
- },
698
- 'location-card': {
699
- file: 'LocationCard.tsx',
700
- component: 'LocationCard',
701
- code: `'use client';\n\nimport { LocationCard, type LocationCardProps } from '${importPkg}';\n\nexport { LocationCard, type LocationCardProps };\n`,
702
- },
703
- 'location-link': {
704
- file: 'LocationLink.tsx',
705
- component: 'LocationLink',
706
- code: `'use client';\n\nimport { LocationLink, type LocationLinkProps } from '${importPkg}';\n\nexport { LocationLink, type LocationLinkProps };\n`,
707
- },
708
- 'map-embed': {
709
- file: 'MapEmbed.tsx',
710
- component: 'MapEmbed',
711
- code: `'use client';\n\nimport { MapEmbed, type MapEmbedProps } from '${importPkg}';\n\nexport { MapEmbed, type MapEmbedProps };\n`,
712
- },
713
- 'social-links': {
714
- file: 'SocialLinks.tsx',
715
- component: 'SocialLinks',
716
- code: `'use client';\n\nimport { SocialLinks, type SocialLinksProps } from '${importPkg}';\n\nexport { SocialLinks, type SocialLinksProps };\n`,
717
- },
718
- 'social-button': {
719
- file: 'SocialButton.tsx',
720
- component: 'SocialButton',
721
- code: `'use client';\n\nimport { SocialButton, type SocialButtonProps } from '${importPkg}';\n\nexport { SocialButton, type SocialButtonProps };\n`,
722
- },
723
- 'business-hours': {
724
- file: 'BusinessHours.tsx',
725
- component: 'BusinessHours',
726
- code: `'use client';\n\nimport { BusinessHours, type BusinessHoursProps } from '${importPkg}';\n\nexport { BusinessHours, type BusinessHoursProps };\n`,
727
- },
728
- 'announcement-bar': {
729
- file: 'AnnouncementBar.tsx',
730
- component: 'EditableAnnouncementBar',
731
- code: `'use client';\n\nimport { EditableAnnouncementBar, AnnouncementBar, type EditableAnnouncementBarProps } from '${importPkg}';\n\nexport { EditableAnnouncementBar, AnnouncementBar, type EditableAnnouncementBarProps };\n`,
732
- },
733
- 'category-pills': {
734
- file: 'CategoryPills.tsx',
735
- component: 'EditableCategoryPills',
736
- code: `'use client';\n\nimport { EditableCategoryPills, CategoryPills, type EditableCategoryPillsProps } from '${importPkg}';\n\nexport { EditableCategoryPills, CategoryPills, type EditableCategoryPillsProps };\n`,
737
- },
738
- 'floating-contact-widget': {
739
- file: 'FloatingContactWidget.tsx',
740
- component: 'FloatingContactWidget',
741
- code: `'use client';\n\nimport { FloatingContactWidget, type FloatingContactWidgetProps } from '${importPkg}';\n\nexport { FloatingContactWidget, type FloatingContactWidgetProps };\n`,
742
- },
743
- 'deneb-action': {
744
- file: 'DenebAction.tsx',
745
- component: 'DenebAction',
746
- code: `'use client';\n\nimport { DenebAction, type DenebActionProps } from '${importPkg}';\n\nexport { DenebAction, type DenebActionProps };\n`,
747
- },
748
- };
801
+ const REGISTRY = getComponentRegistry(importPkg);
749
802
 
750
803
  if (!componentName || componentName === 'list') {
751
804
  console.log('\n' + createBox([
@@ -778,43 +831,321 @@ function addComponent(componentName, targetDirInput) {
778
831
  console.log('Import them in your pages:\n \x1b[90mimport { ... } from "@/components/ui/...";\x1b[0m\n');
779
832
  }
780
833
 
834
+ function runValidateAndZip(targetDirInput, extraArgs = []) {
835
+ if (targetDirInput === '--help' || targetDirInput === '-h' || extraArgs.includes('--help') || extraArgs.includes('-h')) {
836
+ console.log(`\nUsage: deneb validate-and-zip [template-directory] [options]
837
+ (or deneb validate --zip [template-directory])
838
+ (or deneb validate and zip [template-directory])
839
+
840
+ Runs Fivora preflight verification against manifest contracts, static export fixtures,
841
+ and visual editing markers. If and only if all checks pass, packages a clean upload-ready
842
+ fivora-template.zip (excluding node_modules, .next, .git, .env*).
843
+
844
+ Options:
845
+ --skip-install Reuse existing dependencies in working directory (diagnostic only)
846
+ --skip-build Inspect existing build output directory in place (diagnostic only)
847
+ --json Output validation report as JSON\n`);
848
+ process.exit(0);
849
+ }
850
+
851
+ const targetDir = path.resolve(targetDirInput || '.');
852
+ const outputZip = path.resolve(targetDir, 'fivora-template.zip');
853
+
854
+ console.log('\n' + createBox([
855
+ '\x1b[1m\x1b[37mDENEB VALIDATE & ZIP PREFLIGHT\x1b[0m',
856
+ '\x1b[90m1. Validate website configuration with Fivora platform\x1b[0m',
857
+ '\x1b[90m2. Package clean upload-ready ZIP if 100% compliant\x1b[0m'
858
+ ], 58) + '\n');
859
+
860
+ console.log(`[Step 1/2] Running Fivora preflight validation in ${targetDir}...\n`);
861
+
862
+ const validatorScript = path.join(toolsDir, 'fivora-template-validator.cjs');
863
+ const valRes = spawnSync(process.execPath, [validatorScript, 'validate', targetDir, ...extraArgs], {
864
+ stdio: 'inherit',
865
+ });
866
+
867
+ if (valRes.status !== 0) {
868
+ console.error(`\n\x1b[31m✖ Validation failed with exit code ${valRes.status}.\x1b[0m`);
869
+ console.error(`\x1b[33mRefusing to package ZIP: template does not meet Fivora platform requirements.\x1b[0m`);
870
+ console.error(`Fix the validation errors above and re-run "deneb validate-and-zip".\n`);
871
+ process.exit(valRes.status ?? 1);
872
+ }
873
+
874
+ console.log(`\n[Step 2/2] Validation PASSED! Creating clean upload-ready ZIP...\n`);
875
+ packageCleanZip(targetDir, outputZip);
876
+
877
+ console.log('\n' + createBox([
878
+ '\x1b[1m\x1b[32m✔ PREFLIGHT VALIDATION & PACKAGING SUCCESSFUL!\x1b[0m',
879
+ `\x1b[37mOutput:\x1b[0m ${path.basename(outputZip)}`,
880
+ '\x1b[90mReady for 1-click upload at Fivora Developer Portal\x1b[0m'
881
+ ], 58) + '\n');
882
+ }
883
+
884
+ function runDoctor(targetDirInput) {
885
+ const targetDir = path.resolve(targetDirInput || '.');
886
+
887
+ console.log('\n' + createBox([
888
+ '\x1b[1m\x1b[36m🩺 DENEB SYSTEM & TEMPLATE DOCTOR\x1b[0m',
889
+ '\x1b[90mComprehensive diagnostic analysis for Fivora & Next.js\x1b[0m',
890
+ `\x1b[37mTarget:\x1b[0m ${targetDir}`
891
+ ], 60) + '\n');
892
+
893
+ let passed = 0;
894
+ let warnings = 0;
895
+ let errors = 0;
896
+
897
+ function report(type, title, detail) {
898
+ if (type === 'pass') {
899
+ passed++;
900
+ console.log(` \x1b[32m✔\x1b[0m \x1b[1m${title}\x1b[0m${detail ? ` \x1b[90m(${detail})\x1b[0m` : ''}`);
901
+ } else if (type === 'warn') {
902
+ warnings++;
903
+ console.log(` \x1b[33m⚠\x1b[0m \x1b[33m${title}\x1b[0m${detail ? ` \x1b[90m- ${detail}\x1b[0m` : ''}`);
904
+ } else {
905
+ errors++;
906
+ console.log(` \x1b[31m✖\x1b[0m \x1b[31m${title}\x1b[0m${detail ? ` \x1b[90m- ${detail}\x1b[0m` : ''}`);
907
+ }
908
+ }
909
+
910
+ console.log('\x1b[1m[1/6] System & Runtime Environment:\x1b[0m');
911
+ const nodeVersion = process.version;
912
+ const majorNode = parseInt(nodeVersion.replace(/^v/, '').split('.')[0], 10);
913
+ if (majorNode >= 18) {
914
+ report('pass', 'Node.js Runtime', `${nodeVersion} (Supported)`);
915
+ } else {
916
+ report('err', 'Node.js Runtime', `${nodeVersion} (Requires Node.js >= 18.0.0)`);
917
+ }
918
+
919
+ const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
920
+ const npmCheck = spawnSync(npmBin, ['--version'], { encoding: 'utf-8', shell: process.platform === 'win32' });
921
+ if (!npmCheck.error && npmCheck.status === 0) {
922
+ report('pass', 'Package Manager', `npm v${npmCheck.stdout.trim()}`);
923
+ } else {
924
+ report('warn', 'Package Manager', 'npm not found in system PATH');
925
+ }
926
+
927
+ console.log('\n\x1b[1m[2/6] Project Package Configuration:\x1b[0m');
928
+ const pkgPath = path.join(targetDir, 'package.json');
929
+ let pkg = null;
930
+ if (fs.existsSync(pkgPath)) {
931
+ try {
932
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
933
+ report('pass', 'package.json', `Found "${pkg.name || 'unnamed'}"`);
934
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
935
+
936
+ if (allDeps['next']) {
937
+ report('pass', 'Next.js Framework', allDeps['next']);
938
+ } else {
939
+ report('err', 'Next.js Framework', 'next dependency missing in package.json');
940
+ }
941
+
942
+ if (allDeps['@deneb-ui/ui']) {
943
+ report('pass', '@deneb-ui/ui Library', allDeps['@deneb-ui/ui']);
944
+ } else {
945
+ report('warn', '@deneb-ui/ui Library', 'Not installed (run "npm i @deneb-ui/ui")');
946
+ }
947
+
948
+ if (allDeps['@deneb-ui/cli']) {
949
+ report('pass', '@deneb-ui/cli Tooling', allDeps['@deneb-ui/cli']);
950
+ } else {
951
+ report('warn', '@deneb-ui/cli Tooling', 'Recommended for local CLI scripts');
952
+ }
953
+ } catch (e) {
954
+ report('err', 'package.json Syntax', e.message);
955
+ }
956
+ } else {
957
+ report('err', 'package.json', `Not found at ${pkgPath}`);
958
+ }
959
+
960
+ console.log('\n\x1b[1m[3/6] Static Export Configuration:\x1b[0m');
961
+ const nextConfigTs = path.join(targetDir, 'next.config.ts');
962
+ const nextConfigMjs = path.join(targetDir, 'next.config.mjs');
963
+ const nextConfigJs = path.join(targetDir, 'next.config.js');
964
+ let nextConfigFile = [nextConfigTs, nextConfigMjs, nextConfigJs].find((p) => fs.existsSync(p));
965
+
966
+ if (nextConfigFile) {
967
+ const content = fs.readFileSync(nextConfigFile, 'utf-8');
968
+ if (content.includes("output: 'export'") || content.includes('output: "export"')) {
969
+ report('pass', 'Next.js Static Export', `output: 'export' verified in ${path.basename(nextConfigFile)}`);
970
+ } else {
971
+ report('err', 'Next.js Static Export', `Missing output: 'export' in ${path.basename(nextConfigFile)} (Required by Fivora)`);
972
+ }
973
+ } else {
974
+ report('err', 'Next.js Config', 'No next.config.ts, next.config.mjs, or next.config.js found');
975
+ }
976
+
977
+ console.log('\n\x1b[1m[4/6] Fivora Manifest v2 Contract:\x1b[0m');
978
+ const manifestPath = path.join(targetDir, 'fivora-template.json');
979
+ let manifestData = null;
980
+ if (fs.existsSync(manifestPath)) {
981
+ try {
982
+ manifestData = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
983
+ report('pass', 'fivora-template.json', `Valid JSON (strict=${manifestData.strict !== false})`);
984
+
985
+ if (manifestData.version === 2 || manifestData.version === '2') {
986
+ report('pass', 'Manifest Version', 'Version 2 (Current standard)');
987
+ } else {
988
+ report('warn', 'Manifest Version', `Version ${manifestData.version} detected (Recommend version 2)`);
989
+ }
990
+
991
+ const hasHome = Array.isArray(manifestData.pages) && manifestData.pages.some((p) =>
992
+ p.route === '/' || p.slug === '/' || p.path === '/' || p.id === 'home'
993
+ );
994
+ if (hasHome) {
995
+ report('pass', 'Home Page Entry', 'Home page ("/") declared in manifest');
996
+ } else {
997
+ report('err', 'Home Page Entry', 'Manifest pages array missing root slug or route: "/"');
998
+ }
999
+
1000
+ if (manifestData.theme && (manifestData.theme.primary || manifestData.theme.accent)) {
1001
+ report('pass', 'Theme Configuration', 'Primary and accent color tokens declared');
1002
+ } else {
1003
+ // Will check site-data.json below as alternative
1004
+ }
1005
+ } catch (e) {
1006
+ report('err', 'fivora-template.json Syntax', e.message);
1007
+ }
1008
+ } else {
1009
+ report('err', 'fivora-template.json', 'File not found. Run "deneb init" to generate it');
1010
+ }
1011
+
1012
+ console.log('\n\x1b[1m[5/6] Reactive Site Data & Visual Editing:\x1b[0m');
1013
+ const siteDataPath = path.join(targetDir, 'src', 'data', 'site-data.json');
1014
+ if (fs.existsSync(siteDataPath)) {
1015
+ try {
1016
+ const siteData = JSON.parse(fs.readFileSync(siteDataPath, 'utf-8'));
1017
+ report('pass', 'site-data.json', 'src/data/site-data.json exists & valid');
1018
+ const merchantName = (siteData.merchant && (siteData.merchant.businessName || siteData.merchant.name)) || null;
1019
+ if (merchantName) {
1020
+ report('pass', 'Merchant Metadata', `Name: "${merchantName}"`);
1021
+ } else {
1022
+ report('warn', 'Merchant Metadata', 'Missing merchant name in site-data.json');
1023
+ }
1024
+
1025
+ const themeTokens = manifestData?.theme || siteData?.template?.structure?.theme;
1026
+ if (themeTokens && (themeTokens.primaryColor || themeTokens.primary || themeTokens.accentColor || themeTokens.accent)) {
1027
+ report('pass', 'Theme Design Tokens', 'Theme color tokens declared');
1028
+ } else {
1029
+ report('warn', 'Theme Design Tokens', 'No theme color tokens found in manifest or site-data.json');
1030
+ }
1031
+
1032
+ if (siteData.content) {
1033
+ report('pass', 'Visual Content Bindings', 'Content section ready for live sync');
1034
+ } else {
1035
+ report('warn', 'Visual Content Bindings', 'Missing content section in site-data.json');
1036
+ }
1037
+ } catch (e) {
1038
+ report('err', 'site-data.json Syntax', e.message);
1039
+ }
1040
+ } else {
1041
+ report('warn', 'site-data.json', 'src/data/site-data.json not found. Recommended for Fivora live editor');
1042
+ }
1043
+
1044
+ console.log('\n\x1b[1m[6/6] Cleanliness & Security Check:\x1b[0m');
1045
+ const envFiles = ['.env', '.env.local', '.env.production', '.env.development'];
1046
+ const foundEnv = envFiles.filter((f) => fs.existsSync(path.join(targetDir, f)));
1047
+ if (foundEnv.length === 0) {
1048
+ report('pass', 'Secrets Isolation', 'No raw .env files detected in root directory');
1049
+ } else {
1050
+ report('warn', 'Secrets Isolation', `Active env files: ${foundEnv.join(', ')} (Excluded during packaging)`);
1051
+ }
1052
+
1053
+ const previewExists = fs.existsSync(path.join(targetDir, 'preview.png')) ||
1054
+ fs.existsSync(path.join(targetDir, 'thumbnail.png')) ||
1055
+ fs.existsSync(path.join(targetDir, 'public', 'fivora-logo.png'));
1056
+ if (previewExists) {
1057
+ report('pass', 'Storefront Assets', 'Brand/preview graphics verified');
1058
+ } else {
1059
+ report('warn', 'Storefront Assets', 'preview.png not found in template root');
1060
+ }
1061
+
1062
+ // Summary
1063
+ console.log('\n' + createBox([
1064
+ '\x1b[1mDOCTOR DIAGNOSTIC SUMMARY\x1b[0m',
1065
+ `\x1b[32m✔ Passed:\x1b[0m ${passed}`,
1066
+ `\x1b[33m⚠ Warnings:\x1b[0m ${warnings}`,
1067
+ `\x1b[31m✖ Errors:\x1b[0m ${errors}`,
1068
+ errors === 0
1069
+ ? '\x1b[32mStatus: HEALTHY — Ready for Fivora packaging & build!\x1b[0m'
1070
+ : '\x1b[31mStatus: ATTENTION REQUIRED — Fix errors before deployment\x1b[0m'
1071
+ ], 58) + '\n');
1072
+
1073
+ if (errors > 0) {
1074
+ process.exit(1);
1075
+ }
1076
+ }
1077
+
1078
+ // Normalize multi-word "validate and zip" or "validate & zip"
1079
+ let command = args[0];
1080
+ let commandArgs = args.slice(1);
1081
+
1082
+ if (command === 'validate' && (commandArgs[0] === 'and' || commandArgs[0] === '&') && commandArgs[1] === 'zip') {
1083
+ command = 'validate-and-zip';
1084
+ commandArgs = commandArgs.slice(2);
1085
+ }
1086
+
781
1087
  if (command === 'init') {
782
- initProject(args[1]);
1088
+ initProject(commandArgs[0]);
783
1089
  } else if (command === 'create') {
784
- createTemplate(args[1]);
1090
+ createTemplate(commandArgs[0]);
785
1091
  } else if (command === 'add') {
786
- addComponent(args[1], args[2]);
1092
+ addComponent(commandArgs[0], commandArgs[1]);
1093
+ } else if (command === 'doctor' || command === 'check') {
1094
+ runDoctor(commandArgs[0]);
787
1095
  } else if (command === 'lab') {
788
1096
  const script = path.join(toolsDir, 'local-template-lab.cjs');
789
- const res = spawnSync(process.execPath, [script, ...args.slice(1)], { stdio: 'inherit' });
1097
+ const res = spawnSync(process.execPath, [script, ...commandArgs], { stdio: 'inherit' });
790
1098
  process.exit(res.status ?? 0);
791
1099
  } else if (command === 'validate') {
792
- const script = path.join(toolsDir, 'fivora-template-validator.cjs');
793
- const res = spawnSync(process.execPath, [script, 'validate', ...args.slice(1)], { stdio: 'inherit' });
794
- process.exit(res.status ?? 0);
1100
+ if (commandArgs.includes('--zip') || commandArgs.includes('-z')) {
1101
+ const cleanArgs = commandArgs.filter((a) => a !== '--zip' && a !== '-z');
1102
+ runValidateAndZip(cleanArgs[0] || '.', cleanArgs.slice(1));
1103
+ } else {
1104
+ const script = path.join(toolsDir, 'fivora-template-validator.cjs');
1105
+ const res = spawnSync(process.execPath, [script, 'validate', ...commandArgs], { stdio: 'inherit' });
1106
+ process.exit(res.status ?? 0);
1107
+ }
795
1108
  } else if (command === 'pack' || command === 'zip') {
796
- const targetDir = path.resolve(args[1] || '.');
1109
+ const targetDir = path.resolve(commandArgs[0] || '.');
797
1110
  const outputZip = path.resolve(targetDir, 'fivora-template.zip');
798
1111
  packageCleanZip(targetDir, outputZip);
1112
+ } else if (command === 'validate-and-zip' || command === 'validate-zip') {
1113
+ runValidateAndZip(commandArgs[0] || '.', commandArgs.slice(1));
799
1114
  } else if (command === 'update' || command === 'upgrade') {
800
- updateDependencies(args.slice(1));
1115
+ updateDependencies(commandArgs);
801
1116
  } else if (command === 'package') {
802
1117
  const script = path.join(toolsDir, 'fivora-template-validator.cjs');
803
- const res = spawnSync(process.execPath, [script, 'package', ...args.slice(1)], { stdio: 'inherit' });
1118
+ const res = spawnSync(process.execPath, [script, 'package', ...commandArgs], { stdio: 'inherit' });
804
1119
  process.exit(res.status ?? 0);
805
1120
  } else {
806
- console.log(`Usage: deneb <command> [options] (or fivora <command> [options])
1121
+ console.log(`Usage: deneb <command> [options]
807
1122
  DENEB UI Framework — Powered by DENEB-UI Collaborate with FIVORA
808
1123
 
809
- Commands:
810
- add Add a DENEB UI component to src/components/ui/ (e.g. deneb add product-card)
811
- init Configure an existing Next.js project with missing Fivora files & scripts
812
- create Scaffold a new storefront template (e.g. deneb create my-store)
813
- lab Start the local visual editing lab
814
- validate Validate template visual editing contracts and empty states
815
- update Update DENEB & Fivora packages to latest
816
- pack Quick-package a clean, upload-ready ZIP without junk files (node_modules, .next, .git, .env)
817
- zip Alias for pack
818
- package Run full preflight verification and generate upload ZIP`);
1124
+ Core Commands:
1125
+ init Configure an existing Next.js project with missing Fivora files & scripts
1126
+ update Update DENEB packages (@deneb-ui/ui, @deneb-ui/cli) and UI components
1127
+ validate Validate website configuration and visual editing contracts with Fivora platform
1128
+ doctor Run comprehensive environment, manifest & asset diagnostic checks
1129
+ zip Zip the project without unnecessary folders or files (node_modules, .next, .git, .env)
1130
+ validate-and-zip Validate website configuration and immediately package clean upload-ready ZIP
1131
+
1132
+ Development & Scaffolding:
1133
+ add <component> Add or update a DENEB UI component in src/components/ui/ (e.g. deneb add product-card)
1134
+ create <name> Scaffold a new storefront template (e.g. deneb create my-store)
1135
+ lab Start the local visual editing lab simulation
1136
+ pack Alias for zip
1137
+ package Run strict sandbox preflight verification and generate upload ZIP
1138
+
1139
+ Examples:
1140
+ deneb doctor
1141
+ deneb init
1142
+ deneb update
1143
+ deneb validate .
1144
+ deneb validate --zip
1145
+ deneb validate-and-zip
1146
+ deneb zip .
1147
+ deneb add product-card
1148
+ deneb add all`);
819
1149
  process.exit(1);
820
1150
  }
1151
+
package/package.json CHANGED
@@ -1,18 +1,26 @@
1
1
  {
2
2
  "name": "@deneb-ui/cli",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "Official developer CLI for DENEB UI — Validating, scaffolding, testing, and packaging storefront templates. Created by Chamika Gayashan & Induranga Kawishwara.",
5
5
  "bin": {
6
6
  "deneb": "bin/index.js",
7
7
  "denebui": "bin/index.js",
8
8
  "deneb-cli": "bin/index.js",
9
- "fivora": "bin/index.js",
10
9
  "ceeg": "bin/index.js",
11
10
  "ceegui": "bin/index.js"
12
11
  },
13
12
  "publishConfig": {
14
13
  "access": "public"
15
14
  },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/deneb-ui/core.git",
18
+ "directory": "cli/deneb-cli"
19
+ },
20
+ "homepage": "https://github.com/deneb-ui/core#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/deneb-ui/core/issues"
23
+ },
16
24
  "files": [
17
25
  "bin",
18
26
  "src",
@@ -21,7 +29,6 @@
21
29
  "keywords": [
22
30
  "deneb",
23
31
  "deneb-ui",
24
- "fivora",
25
32
  "cli",
26
33
  "validator",
27
34
  "packager"