@dreamtree-org/landing-kit 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +14 -0
- package/README.md +26 -0
- package/dist/index.cjs +323 -15
- package/dist/index.js +323 -15
- package/dist/schema.cjs +324 -3
- package/dist/schema.js +304 -3
- package/index.d.ts +15 -1
- package/package.json +1 -1
- package/schema.d.ts +50 -0
package/dist/schema.cjs
CHANGED
|
@@ -19,23 +19,39 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
// src/schema/index.js
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
|
+
CARD_FIELDS: () => CARD_FIELDS,
|
|
23
|
+
CHROME_FIELDS: () => CHROME_FIELDS,
|
|
24
|
+
CONTENT_BLOCKS: () => CONTENT_BLOCKS,
|
|
22
25
|
DEFAULT_THEME: () => DEFAULT_THEME,
|
|
23
26
|
FONT_PAIRS: () => FONT_PAIRS,
|
|
24
27
|
ICON_NAMES: () => ICON_NAMES,
|
|
28
|
+
LISTING_SLOTS: () => LISTING_SLOTS,
|
|
29
|
+
MAX_BLOCKS: () => MAX_BLOCKS,
|
|
25
30
|
MAX_SECTIONS: () => MAX_SECTIONS,
|
|
26
31
|
MODES: () => MODES,
|
|
27
32
|
PAGE_VERSION: () => PAGE_VERSION,
|
|
28
33
|
PALETTE_STEPS: () => PALETTE_STEPS,
|
|
34
|
+
PRODUCT_SLOTS: () => PRODUCT_SLOTS,
|
|
29
35
|
RADII: () => RADII,
|
|
30
36
|
SECTION_TYPES: () => SECTION_TYPES,
|
|
31
37
|
SECTION_TYPE_KEYS: () => SECTION_TYPE_KEYS,
|
|
38
|
+
STORE_PAGES: () => STORE_PAGES,
|
|
39
|
+
STORE_PAGE_KEYS: () => STORE_PAGE_KEYS,
|
|
40
|
+
STORE_VERSION: () => STORE_VERSION,
|
|
32
41
|
THEME_PRESETS: () => THEME_PRESETS,
|
|
42
|
+
blockDef: () => blockDef,
|
|
33
43
|
brandPalette: () => brandPalette,
|
|
34
44
|
contrastRatio: () => contrastRatio,
|
|
45
|
+
defaultCard: () => defaultCard,
|
|
46
|
+
defaultChrome: () => defaultChrome,
|
|
35
47
|
defaultPage: () => defaultPage,
|
|
48
|
+
defaultStore: () => defaultStore,
|
|
36
49
|
hexToOklch: () => hexToOklch,
|
|
37
50
|
isHex: () => isHex,
|
|
51
|
+
isLegacyPage: () => isLegacyPage,
|
|
38
52
|
luminance: () => luminance,
|
|
53
|
+
migrateStore: () => migrateStore,
|
|
54
|
+
newBlock: () => newBlock,
|
|
39
55
|
newSection: () => newSection,
|
|
40
56
|
newSectionId: () => newSectionId,
|
|
41
57
|
oklchToHex: () => oklchToHex,
|
|
@@ -43,8 +59,12 @@ __export(index_exports, {
|
|
|
43
59
|
safeHref: () => safeHref,
|
|
44
60
|
sanitizeRichText: () => sanitizeRichText,
|
|
45
61
|
sectionDefaults: () => sectionDefaults,
|
|
62
|
+
storeMediaRefs: () => storeMediaRefs,
|
|
63
|
+
templatePage: () => templatePage,
|
|
46
64
|
themeVars: () => themeVars,
|
|
65
|
+
validateFields: () => validateFields,
|
|
47
66
|
validatePage: () => validatePage,
|
|
67
|
+
validateStore: () => validateStore,
|
|
48
68
|
validateTheme: () => validateTheme
|
|
49
69
|
});
|
|
50
70
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -318,8 +338,8 @@ var MAX_SECTIONS = 20;
|
|
|
318
338
|
function sectionDefaults(type) {
|
|
319
339
|
const def = SECTION_TYPES[type];
|
|
320
340
|
if (!def) throw new Error(`Unknown section type "${type}"`);
|
|
321
|
-
const
|
|
322
|
-
return
|
|
341
|
+
const fill2 = (fields2) => Object.fromEntries(fields2.map((f) => [f.key, f.kind === "list" ? (f.default || []).map((item) => ({ ...fill2(f.of), ...item })) : f.default]));
|
|
342
|
+
return fill2(def.fields);
|
|
323
343
|
}
|
|
324
344
|
var seq = 0;
|
|
325
345
|
function newSectionId(type) {
|
|
@@ -349,7 +369,7 @@ var decode = (s) => s.replace(/&(amp|quot|#39|lt|gt|#x[0-9a-f]+|#\d+);/gi, (m, e
|
|
|
349
369
|
});
|
|
350
370
|
function safeHref(raw) {
|
|
351
371
|
const v = decode(String(raw || "")).replace(/[\u0000- \u007f-\u009f]/g, "");
|
|
352
|
-
if (!v) return null;
|
|
372
|
+
if (!v || v.includes("\\")) return null;
|
|
353
373
|
if (/^(https?:\/\/|mailto:|tel:)/i.test(v)) return v;
|
|
354
374
|
if (/^\/(?!\/)/.test(v) || v.startsWith("#")) return v;
|
|
355
375
|
return null;
|
|
@@ -473,6 +493,9 @@ function field(f, value, path, errors) {
|
|
|
473
493
|
throw new Error(`Unknown field kind "${f.kind}" (${path})`);
|
|
474
494
|
}
|
|
475
495
|
}
|
|
496
|
+
function validateFields(defs, obj, path, errors) {
|
|
497
|
+
return fields(defs, obj, path, errors);
|
|
498
|
+
}
|
|
476
499
|
function fields(defs, obj, path, errors) {
|
|
477
500
|
const src = obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
478
501
|
if (obj != null && src !== obj) errors.push({ path, message: "must be an object" });
|
|
@@ -696,25 +719,319 @@ function themeVars(input) {
|
|
|
696
719
|
}
|
|
697
720
|
};
|
|
698
721
|
}
|
|
722
|
+
|
|
723
|
+
// src/schema/store.js
|
|
724
|
+
var STORE_VERSION = 2;
|
|
725
|
+
var MAX_BLOCKS = 12;
|
|
726
|
+
var ID_RE2 = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
727
|
+
var opt = (...pairs) => pairs.map(([value, label]) => ({ value, label }));
|
|
728
|
+
var sel = (key, label, options, d) => ({ key, kind: "select", label, options, default: d });
|
|
729
|
+
var bool = (key, label, d) => ({ key, kind: "bool", label, default: d });
|
|
730
|
+
var heading = (d, max = 90) => ({ key: "title", kind: "text", label: "Heading", max, default: d });
|
|
731
|
+
var CONTENT_BLOCKS = ["promo_banner", "usp_strip", "trust_badges", "cta_cards", "faq", "rich_text", "testimonials", "brand_strip", "gallery"];
|
|
732
|
+
var LISTING_SLOTS = {
|
|
733
|
+
collection_header: { label: "Collection header", description: "Title, description and quick links for the category or search.", fields: [bool("show_description", "Show the description", true), bool("show_quick_links", "Show quick links", true)] },
|
|
734
|
+
toolbar: { label: "Toolbar and sort", description: "Result count, sort and the filters in use.", fields: [] },
|
|
735
|
+
filters: { label: "Filters", description: "Price, brand and spec filters.", required: true, fields: [] },
|
|
736
|
+
results: { label: "Results grid", description: "The products, with paging.", required: true, fields: [] }
|
|
737
|
+
};
|
|
738
|
+
var PRODUCT_SLOTS = {
|
|
739
|
+
gallery: { label: "Gallery", description: "Photos of the product.", required: true, fields: [] },
|
|
740
|
+
buy_box: { label: "Buy box", description: "Title, price, key specs and the buy buttons.", required: true, fields: [bool("show_compare", "Show Add to compare", true), bool("show_quote", "Show bulk quote link", true)] },
|
|
741
|
+
highlights: { label: "Highlights", description: "The product's headline numbers and selling points.", fields: [heading("Highlights")] },
|
|
742
|
+
delivery: { label: "Delivery check", description: "PIN code delivery estimate.", fields: [] },
|
|
743
|
+
seller: { label: "Seller", description: "Who sells and services the product.", fields: [] },
|
|
744
|
+
inspection: { label: "Inspection report", description: "Checks, grade and condition notes.", fields: [heading("Inspection report"), bool("expanded", "Open by default", false)] },
|
|
745
|
+
battery: { label: "Battery", description: "Battery health, where the product has one.", fields: [] },
|
|
746
|
+
included: { label: "In the box", description: "What ships with the product.", fields: [] },
|
|
747
|
+
specs: { label: "Specifications", description: "The full specification table.", fields: [heading("Specifications")] },
|
|
748
|
+
policies: { label: "Warranty and returns", description: "The store's warranty and return terms.", fields: [heading("Warranty and returns")] },
|
|
749
|
+
similar: { label: "Similar products", description: "More products like this one.", fields: [heading("Similar products"), sel("limit", "How many", opt(["3", "3"], ["4", "4"], ["6", "6"], ["8", "8"]), "4")] }
|
|
750
|
+
};
|
|
751
|
+
var SORTS = opt(["relevance", "Recommended"], ["newest", "Newest first"], ["price_asc", "Price: low to high"], ["price_desc", "Price: high to low"], ["discount", "Biggest discount"]);
|
|
752
|
+
var STORE_PAGES = {
|
|
753
|
+
listing: {
|
|
754
|
+
label: "Listing page",
|
|
755
|
+
slots: LISTING_SLOTS,
|
|
756
|
+
settings: [
|
|
757
|
+
sel("columns", "Columns on desktop", opt(["3", "3"], ["4", "4"]), "3"),
|
|
758
|
+
sel("columns_mobile", "Columns on phones", opt(["1", "1"], ["2", "2"]), "2"),
|
|
759
|
+
sel("page_size", "Products per page", opt(["12", "12"], ["24", "24"], ["36", "36"]), "12"),
|
|
760
|
+
sel("default_sort", "Default sort", SORTS, "relevance"),
|
|
761
|
+
sel("filters", "Filters show as", opt(["sidebar", "Sidebar"], ["topbar", "Bar above the results"], ["drawer", "Button that opens a panel"]), "sidebar"),
|
|
762
|
+
bool("active_chips", "Show the filters in use as chips", true),
|
|
763
|
+
sel("paging", "Paging", opt(["pages", "Page numbers"], ["load_more", "Load more button"]), "pages")
|
|
764
|
+
],
|
|
765
|
+
templates: {
|
|
766
|
+
classic: { label: "Classic", description: "Filters in a sidebar beside a product grid.", settings: {}, blocks: ["collection_header", "toolbar", "filters", "results"] },
|
|
767
|
+
topbar: { label: "Top bar", description: "Filter chips above a wide grid, more room for products.", settings: { columns: "4", filters: "topbar", paging: "load_more" }, blocks: ["collection_header", "filters", "toolbar", "results"] },
|
|
768
|
+
editorial: { label: "Editorial", description: "A story-led collection header, then the grid behind a filter button.", settings: { columns: "4", filters: "drawer", active_chips: false }, blocks: ["collection_header", "rich_text", "toolbar", "filters", "results"] }
|
|
769
|
+
},
|
|
770
|
+
default: "classic"
|
|
771
|
+
},
|
|
772
|
+
product: {
|
|
773
|
+
label: "Product page",
|
|
774
|
+
slots: PRODUCT_SLOTS,
|
|
775
|
+
settings: [
|
|
776
|
+
sel("gallery", "Gallery", opt(["thumbs_side", "Thumbnails beside the photo"], ["thumbs_below", "Thumbnails below the photo"], ["mosaic", "Full-width mosaic"]), "thumbs_side"),
|
|
777
|
+
bool("sticky_buy_bar", "Sticky buy bar on phones", true),
|
|
778
|
+
sel("spec_tiles", "Key spec tiles", opt(["0", "None"], ["4", "4"], ["6", "6"]), "4"),
|
|
779
|
+
bool("breadcrumbs", "Show breadcrumbs", true)
|
|
780
|
+
],
|
|
781
|
+
templates: {
|
|
782
|
+
classic: { label: "Classic", description: "Photos beside the buy box, details below.", settings: { gallery: "thumbs_side" }, blocks: ["gallery", "buy_box", "delivery", "seller", "inspection", "battery", "included", "specs", "policies", "similar"] },
|
|
783
|
+
immersive: { label: "Immersive", description: "A full-width photo mosaic first, a floating buy panel, the story below.", settings: { gallery: "mosaic" }, blocks: ["gallery", "buy_box", "highlights", "inspection", "specs", "policies", "similar"] },
|
|
784
|
+
compact: { label: "Compact", description: "One column, phone first, with a sticky buy bar.", settings: { gallery: "thumbs_below", spec_tiles: "0", breadcrumbs: false }, blocks: ["gallery", "buy_box", "delivery", "inspection", "specs", "policies", "similar"] }
|
|
785
|
+
},
|
|
786
|
+
default: "classic"
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
var STORE_PAGE_KEYS = Object.keys(STORE_PAGES);
|
|
790
|
+
var link = [
|
|
791
|
+
{ key: "label", kind: "text", label: "Label", max: 30, required: true, default: "" },
|
|
792
|
+
{ key: "href", kind: "url", label: "Link", required: true, default: "" }
|
|
793
|
+
];
|
|
794
|
+
var CHROME_FIELDS = [
|
|
795
|
+
{
|
|
796
|
+
key: "announcement",
|
|
797
|
+
kind: "group",
|
|
798
|
+
label: "Announcement bar",
|
|
799
|
+
fields: [
|
|
800
|
+
bool("enabled", "Show the announcement bar", false),
|
|
801
|
+
{ key: "text", kind: "text", label: "Message", max: 120, default: "" },
|
|
802
|
+
{ key: "href", kind: "url", label: "Link", default: "" },
|
|
803
|
+
sel("tone", "Colour", opt(["brand", "Brand"], ["accent", "Accent"], ["dark", "Dark"]), "brand")
|
|
804
|
+
]
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
key: "header",
|
|
808
|
+
kind: "group",
|
|
809
|
+
label: "Header",
|
|
810
|
+
fields: [
|
|
811
|
+
{ key: "utility_text", kind: "text", label: "Strip above the header", max: 80, default: "" },
|
|
812
|
+
bool("show_search", "Search box", true),
|
|
813
|
+
bool("category_nav", "Menu of the categories you sell", true),
|
|
814
|
+
{ key: "nav_links", kind: "list", label: "Extra menu links", max: 8, itemLabel: "label", of: link, default: [] },
|
|
815
|
+
bool("sticky", "Keep the header on screen while scrolling", true)
|
|
816
|
+
]
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
key: "footer",
|
|
820
|
+
kind: "group",
|
|
821
|
+
label: "Footer",
|
|
822
|
+
fields: [
|
|
823
|
+
{ key: "about", kind: "textarea", label: "About the store", max: 240, default: "" },
|
|
824
|
+
{
|
|
825
|
+
key: "columns",
|
|
826
|
+
kind: "list",
|
|
827
|
+
label: "Link columns",
|
|
828
|
+
max: 4,
|
|
829
|
+
itemLabel: "title",
|
|
830
|
+
of: [{ key: "title", kind: "text", label: "Column heading", max: 40, required: true, default: "" }, { key: "links", kind: "list", label: "Links", max: 8, itemLabel: "label", of: link, default: [] }],
|
|
831
|
+
default: []
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
key: "socials",
|
|
835
|
+
kind: "list",
|
|
836
|
+
label: "Social profiles",
|
|
837
|
+
max: 6,
|
|
838
|
+
itemLabel: "network",
|
|
839
|
+
of: [sel("network", "Network", opt(["instagram", "Instagram"], ["facebook", "Facebook"], ["youtube", "YouTube"], ["x", "X"], ["linkedin", "LinkedIn"], ["whatsapp", "WhatsApp"]), "instagram"), { key: "href", kind: "url", label: "Profile link", required: true, default: "" }],
|
|
840
|
+
default: []
|
|
841
|
+
},
|
|
842
|
+
{ key: "note", kind: "text", label: "Small print (GSTIN, address)", max: 160, default: "" }
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
];
|
|
846
|
+
var CARD_FIELDS = [
|
|
847
|
+
sel("image_ratio", "Photo shape", opt(["4:3", "Landscape 4:3"], ["1:1", "Square"], ["3:4", "Portrait 3:4"]), "4:3"),
|
|
848
|
+
bool("badges", "Condition and discount badges", true),
|
|
849
|
+
bool("spec_line", "Key specs line", true),
|
|
850
|
+
bool("compare", "Compare checkbox", true),
|
|
851
|
+
bool("quick_add", "Add to cart button", true),
|
|
852
|
+
bool("mrp", "Show the MRP struck through", true),
|
|
853
|
+
sel("hover", "On hover", opt(["none", "Nothing"], ["zoom", "Zoom the photo"], ["second_image", "Show the second photo"]), "zoom")
|
|
854
|
+
];
|
|
855
|
+
function validateGroups(defs, obj, path, errors) {
|
|
856
|
+
const src = obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
|
|
857
|
+
if (obj != null && src !== obj) errors.push({ path, message: "must be an object" });
|
|
858
|
+
return Object.fromEntries(defs.map((g) => [g.key, g.kind === "group" ? validateFields(g.fields, src[g.key], `${path}.${g.key}`, errors) : validateFields([g], src, path, errors)[g.key]]));
|
|
859
|
+
}
|
|
860
|
+
var fill = (defs) => Object.fromEntries(defs.map((f) => [f.key, f.kind === "group" ? fill(f.fields) : f.kind === "list" ? [...f.default || []] : f.default]));
|
|
861
|
+
var defaultChrome = () => fill(CHROME_FIELDS);
|
|
862
|
+
var defaultCard = () => fill(CARD_FIELDS);
|
|
863
|
+
function blockDef(pageKey, type) {
|
|
864
|
+
const page = own(STORE_PAGES, pageKey) ? STORE_PAGES[pageKey] : null;
|
|
865
|
+
if (!page) return null;
|
|
866
|
+
if (own(page.slots, type)) return { ...page.slots[type], slot: true };
|
|
867
|
+
if (CONTENT_BLOCKS.includes(type) && own(SECTION_TYPES, type)) return { ...SECTION_TYPES[type], slot: false };
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
var own = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
871
|
+
var blockId = (type) => type.replace(/_/g, "-");
|
|
872
|
+
var blockDefaults = (pageKey, type) => {
|
|
873
|
+
const def = blockDef(pageKey, type);
|
|
874
|
+
return def.slot ? fill(def.fields) : sectionDefaults(type);
|
|
875
|
+
};
|
|
876
|
+
function newBlock(pageKey, type) {
|
|
877
|
+
const def = blockDef(pageKey, type);
|
|
878
|
+
if (!def) throw new Error(`Unknown block "${type}" on the ${pageKey} page`);
|
|
879
|
+
return { id: def.slot ? blockId(type) : newSectionId(type), type, enabled: true, props: blockDefaults(pageKey, type) };
|
|
880
|
+
}
|
|
881
|
+
function templatePage(pageKey, template) {
|
|
882
|
+
const page = STORE_PAGES[pageKey];
|
|
883
|
+
const key = own(page.templates, template) ? template : page.default;
|
|
884
|
+
const tpl = page.templates[key];
|
|
885
|
+
const settings = { ...fill(page.settings), ...tpl.settings };
|
|
886
|
+
const blocks = tpl.blocks.map((type) => ({ id: blockId(type), type, enabled: true, props: blockDefaults(pageKey, type) }));
|
|
887
|
+
return { template: key, settings, blocks };
|
|
888
|
+
}
|
|
889
|
+
function validateTemplatePage(pageKey, input, path, errors) {
|
|
890
|
+
var _a, _b;
|
|
891
|
+
const page = STORE_PAGES[pageKey];
|
|
892
|
+
const src = input && typeof input === "object" && !Array.isArray(input) ? input : {};
|
|
893
|
+
let template = (_a = src.template) != null ? _a : page.default;
|
|
894
|
+
if (!own(page.templates, template)) {
|
|
895
|
+
errors.push({ path: `${path}.template`, message: `must be one of ${Object.keys(page.templates).join(", ")}` });
|
|
896
|
+
template = page.default;
|
|
897
|
+
}
|
|
898
|
+
const tpl = page.templates[template];
|
|
899
|
+
const settingDefs = page.settings.map((f) => tpl.settings[f.key] === void 0 ? f : { ...f, default: tpl.settings[f.key] });
|
|
900
|
+
const settings = validateFields(settingDefs, (_b = src.settings) != null ? _b : {}, `${path}.settings`, errors);
|
|
901
|
+
const list = Array.isArray(src.blocks) ? src.blocks : null;
|
|
902
|
+
if (src.blocks != null && !list) errors.push({ path: `${path}.blocks`, message: "must be a list" });
|
|
903
|
+
const raw = list != null ? list : templatePage(pageKey, template).blocks;
|
|
904
|
+
if (raw.length > MAX_BLOCKS) errors.push({ path: `${path}.blocks`, message: `can have at most ${MAX_BLOCKS}` });
|
|
905
|
+
const ids = /* @__PURE__ */ new Set();
|
|
906
|
+
const slotsSeen = /* @__PURE__ */ new Set();
|
|
907
|
+
const blocks = [];
|
|
908
|
+
raw.slice(0, MAX_BLOCKS).forEach((b, i) => {
|
|
909
|
+
var _a2, _b2;
|
|
910
|
+
const at = `${path}.blocks[${i}]`;
|
|
911
|
+
if (!b || typeof b !== "object") {
|
|
912
|
+
errors.push({ path: at, message: "must be an object" });
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
const def = blockDef(pageKey, b.type);
|
|
916
|
+
if (!def) {
|
|
917
|
+
errors.push({ path: `${at}.type`, message: `unknown block "${b.type}" on the ${page.label.toLowerCase()}` });
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const id = String((_a2 = b.id) != null ? _a2 : "");
|
|
921
|
+
if (!ID_RE2.test(id)) errors.push({ path: `${at}.id`, message: "must be 1\u201340 lowercase letters, digits or dashes" });
|
|
922
|
+
else if (ids.has(id)) errors.push({ path: `${at}.id`, message: `duplicate id "${id}"` });
|
|
923
|
+
ids.add(id);
|
|
924
|
+
if (def.slot) {
|
|
925
|
+
if (slotsSeen.has(b.type)) {
|
|
926
|
+
errors.push({ path: `${at}.type`, message: `"${def.label}" can appear only once` });
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
slotsSeen.add(b.type);
|
|
930
|
+
}
|
|
931
|
+
let enabled = b.enabled === void 0 ? true : b.enabled;
|
|
932
|
+
if (typeof enabled !== "boolean") {
|
|
933
|
+
errors.push({ path: `${at}.enabled`, message: "must be true or false" });
|
|
934
|
+
enabled = true;
|
|
935
|
+
}
|
|
936
|
+
if (def.required && !enabled) {
|
|
937
|
+
errors.push({ path: `${at}.enabled`, message: `"${def.label}" is required and cannot be hidden` });
|
|
938
|
+
enabled = true;
|
|
939
|
+
}
|
|
940
|
+
blocks.push({ id, type: b.type, enabled, props: validateFields(def.fields, (_b2 = b.props) != null ? _b2 : {}, `${at}.props`, errors) });
|
|
941
|
+
});
|
|
942
|
+
for (const [type, def] of Object.entries(page.slots)) {
|
|
943
|
+
if (def.required && !slotsSeen.has(type)) {
|
|
944
|
+
errors.push({ path: `${path}.blocks`, message: `"${def.label}" is required` });
|
|
945
|
+
blocks.push({ id: ids.has(blockId(type)) ? `${blockId(type)}-1` : blockId(type), type, enabled: true, props: blockDefaults(pageKey, type) });
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return { template, settings, blocks };
|
|
949
|
+
}
|
|
950
|
+
function migrateStore(input) {
|
|
951
|
+
const home = input && typeof input === "object" && Array.isArray(input.sections) ? input : null;
|
|
952
|
+
return {
|
|
953
|
+
version: STORE_VERSION,
|
|
954
|
+
pages: { home: home ? { version: 1, sections: home.sections } : defaultPage(), listing: templatePage("listing"), product: templatePage("product") },
|
|
955
|
+
chrome: defaultChrome(),
|
|
956
|
+
card: defaultCard()
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
var isLegacyPage = (doc) => !!doc && typeof doc === "object" && doc.version !== STORE_VERSION && Array.isArray(doc.sections);
|
|
960
|
+
function validateStore(input) {
|
|
961
|
+
var _a, _b, _c;
|
|
962
|
+
const errors = [];
|
|
963
|
+
const src0 = isLegacyPage(input) ? migrateStore(input) : input;
|
|
964
|
+
const src = src0 && typeof src0 === "object" && !Array.isArray(src0) ? src0 : null;
|
|
965
|
+
if (!src) {
|
|
966
|
+
const doc2 = migrateStore(null);
|
|
967
|
+
return { ok: false, errors: [{ path: "doc", message: "must be an object" }], doc: doc2 };
|
|
968
|
+
}
|
|
969
|
+
if (src.version != null && src.version !== STORE_VERSION) errors.push({ path: "version", message: `must be ${STORE_VERSION}` });
|
|
970
|
+
const pages = src.pages && typeof src.pages === "object" ? src.pages : {};
|
|
971
|
+
const home = validatePage((_a = pages.home) != null ? _a : defaultPage());
|
|
972
|
+
home.errors.forEach((e) => errors.push({ path: `pages.home.${e.path}`, message: e.message }));
|
|
973
|
+
const doc = {
|
|
974
|
+
version: STORE_VERSION,
|
|
975
|
+
pages: {
|
|
976
|
+
home: home.page,
|
|
977
|
+
...Object.fromEntries(STORE_PAGE_KEYS.map((k) => [k, validateTemplatePage(k, pages[k], `pages.${k}`, errors)]))
|
|
978
|
+
},
|
|
979
|
+
chrome: validateGroups(CHROME_FIELDS, (_b = src.chrome) != null ? _b : {}, "chrome", errors),
|
|
980
|
+
card: validateFields(CARD_FIELDS, (_c = src.card) != null ? _c : {}, "card", errors)
|
|
981
|
+
};
|
|
982
|
+
if (doc.chrome.announcement.enabled && !doc.chrome.announcement.text) errors.push({ path: "chrome.announcement.text", message: "is required when the bar is shown" });
|
|
983
|
+
return { ok: errors.length === 0, errors, doc };
|
|
984
|
+
}
|
|
985
|
+
function defaultStore({ name, tagline } = {}) {
|
|
986
|
+
return { ...migrateStore(defaultPage({ name, tagline })) };
|
|
987
|
+
}
|
|
988
|
+
function storeMediaRefs(doc) {
|
|
989
|
+
const out = [];
|
|
990
|
+
const walk = (v) => {
|
|
991
|
+
if (Array.isArray(v)) v.forEach(walk);
|
|
992
|
+
else if (v && typeof v === "object") {
|
|
993
|
+
if (v.media_id != null && Object.keys(v).every((k) => k === "media_id" || k === "alt")) out.push(v);
|
|
994
|
+
else Object.values(v).forEach(walk);
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
walk(doc);
|
|
998
|
+
return out;
|
|
999
|
+
}
|
|
699
1000
|
// Annotate the CommonJS export names for ESM import in node:
|
|
700
1001
|
0 && (module.exports = {
|
|
1002
|
+
CARD_FIELDS,
|
|
1003
|
+
CHROME_FIELDS,
|
|
1004
|
+
CONTENT_BLOCKS,
|
|
701
1005
|
DEFAULT_THEME,
|
|
702
1006
|
FONT_PAIRS,
|
|
703
1007
|
ICON_NAMES,
|
|
1008
|
+
LISTING_SLOTS,
|
|
1009
|
+
MAX_BLOCKS,
|
|
704
1010
|
MAX_SECTIONS,
|
|
705
1011
|
MODES,
|
|
706
1012
|
PAGE_VERSION,
|
|
707
1013
|
PALETTE_STEPS,
|
|
1014
|
+
PRODUCT_SLOTS,
|
|
708
1015
|
RADII,
|
|
709
1016
|
SECTION_TYPES,
|
|
710
1017
|
SECTION_TYPE_KEYS,
|
|
1018
|
+
STORE_PAGES,
|
|
1019
|
+
STORE_PAGE_KEYS,
|
|
1020
|
+
STORE_VERSION,
|
|
711
1021
|
THEME_PRESETS,
|
|
1022
|
+
blockDef,
|
|
712
1023
|
brandPalette,
|
|
713
1024
|
contrastRatio,
|
|
1025
|
+
defaultCard,
|
|
1026
|
+
defaultChrome,
|
|
714
1027
|
defaultPage,
|
|
1028
|
+
defaultStore,
|
|
715
1029
|
hexToOklch,
|
|
716
1030
|
isHex,
|
|
1031
|
+
isLegacyPage,
|
|
717
1032
|
luminance,
|
|
1033
|
+
migrateStore,
|
|
1034
|
+
newBlock,
|
|
718
1035
|
newSection,
|
|
719
1036
|
newSectionId,
|
|
720
1037
|
oklchToHex,
|
|
@@ -722,7 +1039,11 @@ function themeVars(input) {
|
|
|
722
1039
|
safeHref,
|
|
723
1040
|
sanitizeRichText,
|
|
724
1041
|
sectionDefaults,
|
|
1042
|
+
storeMediaRefs,
|
|
1043
|
+
templatePage,
|
|
725
1044
|
themeVars,
|
|
1045
|
+
validateFields,
|
|
726
1046
|
validatePage,
|
|
1047
|
+
validateStore,
|
|
727
1048
|
validateTheme
|
|
728
1049
|
});
|