@tokenoftrust/cli 2.0.22 → 2.0.24

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/validate.mjs +20 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/cli",
3
- "version": "2.0.22",
3
+ "version": "2.0.24",
4
4
  "description": "Token of Trust developer CLI — clone a tenant store, run it locally with save→reload, and submit it for preview. Installs the `tot` command.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Token of Trust",
package/src/validate.mjs CHANGED
@@ -321,7 +321,9 @@ export const BLOCK_PALETTE_SUPPORTED_CONTRACTS = Object.freeze([
321
321
  "block-palette@3",
322
322
  "block-palette@4",
323
323
  "block-palette@5",
324
+ "block-palette@6",
324
325
  ]);
326
+ export const FEATURED_PRODUCT_PRESENTATIONS = Object.freeze(["grid", "carousel"]);
325
327
  const SUPPORTED_BLOCK_CONTRACTS = new Set(BLOCK_PALETTE_SUPPORTED_CONTRACTS);
326
328
  const KNOWN_BLOCKS = new Set([
327
329
  "hero",
@@ -494,6 +496,19 @@ function validatePromoTiles(block, file, at, out, tenantId) {
494
496
  }
495
497
  }
496
498
 
499
+ function validateHeroSlides(slides, file, at, out, tenantId) {
500
+ if (!Array.isArray(slides) || slides.length === 0) { out.push(mk(ERROR, "block-prop", file, `${at}.slides must be a non-empty array`)); return; }
501
+ slides.forEach((slide, index) => {
502
+ const slideAt = `${at}.slides[${index}]`;
503
+ if (slide == null || typeof slide !== "object" || Array.isArray(slide)) { out.push(mk(ERROR, "block-prop", file, `${slideAt} must be an object`)); return; }
504
+ for (const prop of ["image", "imageAlt"]) if (typeof slide[prop] !== "string" || !slide[prop].trim()) out.push(mk(ERROR, "block-required", file, `${slideAt}.${prop} must be a non-empty string`));
505
+ validateVisualAsset(slide.image, file, `${slideAt}.image`, out, tenantId);
506
+ for (const prop of ["headline", "ctaLabel", "ctaHref"]) if (slide[prop] !== undefined && (typeof slide[prop] !== "string" || !slide[prop].trim())) out.push(mk(ERROR, "block-prop", file, `${slideAt}.${prop} must be a non-empty string`));
507
+ if ((slide.ctaLabel === undefined) !== (slide.ctaHref === undefined)) out.push(mk(ERROR, "block-required", file, `${slideAt}.ctaLabel and ${slideAt}.ctaHref must be supplied together`));
508
+ if (typeof slide.ctaHref === "string" && !isSafeStorefrontHref(slide.ctaHref)) out.push(mk(ERROR, "navigation-href", file, `${slideAt}.ctaHref must be a same-storefront root-relative path`));
509
+ });
510
+ }
511
+
497
512
  function validateTrustStrip(block, file, at, out, tenantId) {
498
513
  if (!Array.isArray(block.items)) {
499
514
  out.push(mk(ERROR, "block-prop", file, `${at}.items must be an array`));
@@ -523,6 +538,9 @@ function validateTrustStrip(block, file, at, out, tenantId) {
523
538
  }
524
539
 
525
540
  function validateFaq(block, file, at, out) {
541
+ if (block.lede !== undefined && (typeof block.lede !== "string" || !block.lede.trim())) {
542
+ out.push(mk(ERROR, "block-prop", file, `${at}.lede must be a non-empty plain-text string`));
543
+ }
526
544
  if (!Array.isArray(block.items)) {
527
545
  out.push(mk(ERROR, "block-prop", file, `${at}.items must be an array`));
528
546
  return;
@@ -582,10 +600,12 @@ function validateHomeDoc(doc, file, tenantId) {
582
600
  if (block.limit !== undefined && typeof block.limit !== "number") {
583
601
  out.push(mk(ERROR, "block-prop", file, `${at}.limit must be a number`));
584
602
  }
603
+ if (block.presentation !== undefined && !FEATURED_PRODUCT_PRESENTATIONS.includes(block.presentation)) out.push(mk(ERROR, "block-prop", file, `${at}.presentation must be grid or carousel`));
585
604
  }
586
605
  if (component === "hero" && block.image !== undefined) {
587
606
  validateVisualAsset(block.image, file, `${at}.image`, out, tenantId);
588
607
  }
608
+ if (component === "hero" && block.slides !== undefined) validateHeroSlides(block.slides, file, at, out, tenantId);
589
609
  if (component === "editorial" && block.image !== undefined) {
590
610
  validateVisualAsset(block.image, file, `${at}.image`, out, tenantId);
591
611
  if (typeof block.imageAlt !== "string") {