@dreamtree-org/landing-kit 0.8.0 → 0.10.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/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 fill = (fields2) => Object.fromEntries(fields2.map((f) => [f.key, f.kind === "list" ? (f.default || []).map((item) => ({ ...fill(f.of), ...item })) : f.default]));
322
- return fill(def.fields);
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
  });