@wix/web5-core 1.63.43 → 1.63.44

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.
@@ -46,8 +46,25 @@ var _tokenContract = require("../theme/tokenContract");
46
46
  * and set a value has already settled that argument, so aliasing theirs would
47
47
  * let the template overrule the person who chose. They are written last, so a
48
48
  * token both maps carry resolves to the owner's value.
49
+ * - **The active theme scheme sits between the two**: `schemeTokens` is the
50
+ * resolved entry of `Configuration.themeSchemes` (see `theme/themeScheme`).
51
+ * It is written after the store map and before the owner's overrides, under
52
+ * the SAME bucket rule as the store map: a `brand` token (colours, fonts) as
53
+ * the real token, anything else — `--radius`, a key the contract has never
54
+ * heard of — as its `--web5-host-*` alias. Being the later write of either
55
+ * property, the chosen palette beats the flat store map on colours and on
56
+ * radius alike — yet a template that states `--radius` still wins on shape,
57
+ * exactly as it does over an imported radius. A scheme is written like the
58
+ * store map it stands in for, not like an owner override, so it gets no
59
+ * exemption from aliasing; the owner's layer still wins over it. Its entries
60
+ * are filtered exactly like the store map's too: the same key check, the same
61
+ * warning, and no per-type value check — `setProperty` keeps any value inert,
62
+ * and the typed grammar (`isSchemeTokenEntry`) is the panel's to enforce
63
+ * before a write. So the scheme token set is as open as `themeOverrides`: a
64
+ * new scheme token needs no release here. Absent → exactly the pre-scheme
65
+ * behaviour.
49
66
  * - **Idempotent**: one marker element per document, replaced wholesale on
50
- * re-apply; empty/absent input removes it.
67
+ * re-apply; empty/absent input (all three layers) removes it.
51
68
  */
52
69
 
53
70
  /** A themeOverrides key: always a CSS custom property. */
@@ -69,29 +86,37 @@ var _tokenContract = require("../theme/tokenContract");
69
86
  * who wins when a store and a template disagree.
70
87
  */
71
88
  const THEME_OVERRIDE_TOKENS = exports.THEME_OVERRIDE_TOKENS = new Set(Object.keys(_tokenContract.THEME_TOKEN_CONTRACT));
72
-
73
- /** Mirrors the server's Consts.ThemeOverrideKeyPattern. */
74
- const KEY_PATTERN = /^--[a-z][a-z0-9-]{0,62}$/;
75
89
  const MARKER_ATTR = 'data-web5-theme-overrides';
76
90
 
77
- /** Drops keys that are not well-formed custom properties, warning about each. */
91
+ /**
92
+ * Drops keys that are not well-formed custom properties, warning about each.
93
+ * Used for all three layers — store, scheme and owner.
94
+ */
78
95
  function wellFormedEntries(overrides) {
79
96
  return Object.entries(overrides ?? {}).filter(([key]) => {
80
- const wellFormed = KEY_PATTERN.test(key);
97
+ const wellFormed = _tokenContract.THEME_OVERRIDE_KEY_PATTERN.test(key);
81
98
  if (!wellFormed) {
82
99
  console.warn(`[web5-theme] Skipping malformed theme override key: ${key}`);
83
100
  }
84
101
  return wellFormed;
85
102
  });
86
103
  }
87
- function applyThemeOverrides(overrides, userOverrides) {
104
+
105
+ /**
106
+ * Where a store-side token lands: a `brand` token as itself, so the store's
107
+ * identity beats the template's; everything else as its `--web5-host-*` alias,
108
+ * which core consumes only as a fallback the template can beat.
109
+ */
110
+ const bucketed = key => (0, _tokenContract.bucketOf)(key) === 'brand' ? key : (0, _tokenContract.hostAliasFor)(key);
111
+ function applyThemeOverrides(overrides, userOverrides, schemeTokens) {
88
112
  if (typeof document === 'undefined') {
89
113
  return;
90
114
  }
91
115
  const existing = document.head.querySelector(`style[${MARKER_ATTR}]`);
92
116
  const entries = wellFormedEntries(overrides);
93
117
  const userEntries = wellFormedEntries(userOverrides);
94
- if (entries.length === 0 && userEntries.length === 0) {
118
+ const activeSchemeEntries = wellFormedEntries(schemeTokens);
119
+ if (entries.length === 0 && activeSchemeEntries.length === 0 && userEntries.length === 0) {
95
120
  existing == null || existing.remove();
96
121
  // Nothing to apply is itself a traceable answer: it means every token on
97
122
  // the page is the template's, which is otherwise indistinguishable from
@@ -128,7 +153,14 @@ function applyThemeOverrides(overrides, userOverrides) {
128
153
  // Brand wins over the template, so it is written as the token the
129
154
  // stylesheets actually read. Everything else lands in the host namespace,
130
155
  // where core consumes it only as a fallback the template can beat.
131
- write(key, value, (0, _tokenContract.bucketOf)(key) === 'brand' ? key : (0, _tokenContract.hostAliasFor)(key), 'store');
156
+ write(key, value, bucketed(key), 'store');
157
+ }
158
+ // The active scheme over the store map, bucketed the same way: a brand token
159
+ // as itself, `--radius` and unknown keys as their alias. Either way this is
160
+ // the later write of that property, so it beats the store's — and a
161
+ // template's `--radius` still beats the alias.
162
+ for (const [key, value] of activeSchemeEntries) {
163
+ write(key, value, bucketed(key), 'scheme');
132
164
  }
133
165
  // Owner choices last and never aliased: within one declaration block the last
134
166
  // write of a property wins, so a token both maps carry ends up the owner's.
@@ -1 +1 @@
1
- {"version":3,"names":["_hostScope","require","_themeDebug","_tokenContract","THEME_OVERRIDE_TOKENS","exports","Set","Object","keys","THEME_TOKEN_CONTRACT","KEY_PATTERN","MARKER_ATTR","wellFormedEntries","overrides","entries","filter","key","wellFormed","test","console","warn","applyThemeOverrides","userOverrides","document","existing","head","querySelector","userEntries","length","remove","traceThemeOverrides","style","createElement","setAttribute","appendChild","sheet","insertRule","WEB5_SCOPE","rule","cssRules","applied","write","value","target","source","setProperty","push","token","writtenAs","bucketOf","hostAliasFor"],"sources":["../../../src/client/applyThemeOverrides.ts"],"sourcesContent":["/**\n * Runtime theme token injection (DL #131).\n *\n * `Configuration.themeOverrides` carries per-store CSS custom-property\n * overrides (`--primary`, `--radius`, `--heading`, ...) computed from the\n * store's own theme (e.g. the Shopify `settings_data.json` extractor). This\n * applies them to the page so they win over the bundle's baked `theme.css`:\n *\n * - **Scoped, not `:root`**: the rule targets `WEB5_SCOPE`, the same\n * `:is(#root, .w5-root, [data-web5-placement], .debug-view)` selector every\n * compiled bundle stylesheet uses — host-page styling is never touched, and\n * equal specificity + later document order makes the override win. Callers\n * must therefore apply AFTER the client bundle's CSS is in the document.\n * - **Bucketed, since DL #193**: a `brand` token (colour roles, font families)\n * is written as itself, so the store's identity beats the template's. Every\n * other key — `host` tokens and anything the contract has never heard of —\n * is written as its `--web5-host-*` alias, which core's stylesheets consume\n * as a `var()` fallback. A template stating the real token therefore wins on\n * shape, and an unknown key is inert rather than dangerous: a custom property\n * nothing references renders nothing, so an adapter that learns to read a new\n * token cannot silently override a template.\n * - **The token set stays open, but the destination changed.** Before DL #193\n * an unrecognised key was applied verbatim, so a template could consume\n * `var(--foo)` and grow a token with no release. It now arrives as\n * `--web5-host-foo`, so the template consumes `var(--web5-host-foo, <default>)`\n * instead. One line different, and the provenance is explicit — the wiring\n * line is what activates a host token, not the contract entry.\n * - The mandatory `--` prefix means an override can only define custom\n * properties — it can never set a real CSS property inside the scope. The\n * server enforces the same shape (plus value sanitation) at write time.\n * - **Values are inert**: entries are written with\n * `CSSStyleDeclaration.setProperty`, never string-concatenated into CSS, so\n * a hostile value cannot terminate the declaration or open a new rule.\n * - **The shop owner is the last word (DL #193)**: `userOverrides` carries the\n * choices a human made in the owner panel, and every one of them is written\n * as the real token whatever its bucket. Bucketing exists to arbitrate a\n * disagreement between a store and a template; an owner who opened a panel\n * and set a value has already settled that argument, so aliasing theirs would\n * let the template overrule the person who chose. They are written last, so a\n * token both maps carry resolves to the owner's value.\n * - **Idempotent**: one marker element per document, replaced wholesale on\n * re-apply; empty/absent input removes it.\n */\nimport { WEB5_SCOPE } from '../hostScope';\nimport {\n traceThemeOverrides,\n type AppliedToken,\n type ThemeOverrideSource,\n} from './themeDebug';\nimport {\n THEME_TOKEN_CONTRACT,\n bucketOf,\n hostAliasFor,\n} from '../theme/tokenContract';\n\n/** A themeOverrides key: always a CSS custom property. */\nexport type ThemeOverrideKey = `--${string}`;\n\n/**\n * Writer-side shape for a theme override map. The catalog below documents\n * the tokens the seed consumes today; the type stays open so new tokens\n * flow end-to-end with no release anywhere but the seed.\n */\nexport type ThemeOverrides = Record<ThemeOverrideKey, string>;\n\n/**\n * The known token catalog. GUIDANCE, NOT A GATE — `applyThemeOverrides` still\n * applies any well-formed custom property, so the vocabulary can grow without a\n * release here.\n *\n * Derived from `@wix/web5-token-contract` rather than restated, because two\n * hand-maintained copies of one list is how they drift. The contract also\n * carries what this Set cannot: which bucket each token is in, and therefore\n * who wins when a store and a template disagree.\n */\nexport const THEME_OVERRIDE_TOKENS: ReadonlySet<string> = new Set(\n Object.keys(THEME_TOKEN_CONTRACT),\n);\n\n/** Mirrors the server's Consts.ThemeOverrideKeyPattern. */\nconst KEY_PATTERN = /^--[a-z][a-z0-9-]{0,62}$/;\n\nconst MARKER_ATTR = 'data-web5-theme-overrides';\n\n/** Drops keys that are not well-formed custom properties, warning about each. */\nfunction wellFormedEntries(\n overrides?: Record<string, string> | null,\n): [string, string][] {\n return Object.entries(overrides ?? {}).filter(([key]) => {\n const wellFormed = KEY_PATTERN.test(key);\n if (!wellFormed) {\n console.warn(\n `[web5-theme] Skipping malformed theme override key: ${key}`,\n );\n }\n return wellFormed;\n });\n}\n\nexport function applyThemeOverrides(\n overrides?: Record<string, string> | null,\n userOverrides?: Record<string, string> | null,\n): void {\n if (typeof document === 'undefined') {\n return;\n }\n\n const existing = document.head.querySelector(`style[${MARKER_ATTR}]`);\n const entries = wellFormedEntries(overrides);\n const userEntries = wellFormedEntries(userOverrides);\n\n if (entries.length === 0 && userEntries.length === 0) {\n existing?.remove();\n // Nothing to apply is itself a traceable answer: it means every token on\n // the page is the template's, which is otherwise indistinguishable from\n // tracing having failed.\n traceThemeOverrides([]);\n return;\n }\n\n const style = document.createElement('style');\n style.setAttribute(MARKER_ATTR, '');\n document.head.appendChild(style);\n const sheet = style.sheet;\n if (!sheet) {\n style.remove();\n return;\n }\n sheet.insertRule(`${WEB5_SCOPE} {}`, 0);\n const rule = sheet.cssRules[0] as CSSStyleRule;\n const applied: AppliedToken[] = [];\n const write = (\n key: string,\n value: string,\n target: string,\n source: ThemeOverrideSource,\n ): void => {\n try {\n rule.style.setProperty(target, value);\n applied.push({ token: key, value, writtenAs: target, source });\n } catch {\n // An engine that rejects the value leaves the token at its baked\n // default — degraded theming, never broken CSS.\n }\n };\n\n for (const [key, value] of entries) {\n // Brand wins over the template, so it is written as the token the\n // stylesheets actually read. Everything else lands in the host namespace,\n // where core consumes it only as a fallback the template can beat.\n write(\n key,\n value,\n bucketOf(key) === 'brand' ? key : hostAliasFor(key),\n 'store',\n );\n }\n // Owner choices last and never aliased: within one declaration block the last\n // write of a property wins, so a token both maps carry ends up the owner's.\n for (const [key, value] of userEntries) {\n write(key, value, key, 'user');\n }\n // Replace-on-reapply: the fresh element is appended (so it stays last in\n // document order) before the stale one is dropped.\n existing?.remove();\n // AFTER the stale element is gone, so the resolved values traced below are\n // the ones the page will actually render. Off unless explicitly enabled.\n traceThemeOverrides(applied);\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_hostScope","require","_themeDebug","_tokenContract","THEME_OVERRIDE_TOKENS","exports","Set","Object","keys","THEME_TOKEN_CONTRACT","MARKER_ATTR","wellFormedEntries","overrides","entries","filter","key","wellFormed","THEME_OVERRIDE_KEY_PATTERN","test","console","warn","bucketed","bucketOf","hostAliasFor","applyThemeOverrides","userOverrides","schemeTokens","document","existing","head","querySelector","userEntries","activeSchemeEntries","length","remove","traceThemeOverrides","style","createElement","setAttribute","appendChild","sheet","insertRule","WEB5_SCOPE","rule","cssRules","applied","write","value","target","source","setProperty","push","token","writtenAs"],"sources":["../../../src/client/applyThemeOverrides.ts"],"sourcesContent":["/**\n * Runtime theme token injection (DL #131).\n *\n * `Configuration.themeOverrides` carries per-store CSS custom-property\n * overrides (`--primary`, `--radius`, `--heading`, ...) computed from the\n * store's own theme (e.g. the Shopify `settings_data.json` extractor). This\n * applies them to the page so they win over the bundle's baked `theme.css`:\n *\n * - **Scoped, not `:root`**: the rule targets `WEB5_SCOPE`, the same\n * `:is(#root, .w5-root, [data-web5-placement], .debug-view)` selector every\n * compiled bundle stylesheet uses — host-page styling is never touched, and\n * equal specificity + later document order makes the override win. Callers\n * must therefore apply AFTER the client bundle's CSS is in the document.\n * - **Bucketed, since DL #193**: a `brand` token (colour roles, font families)\n * is written as itself, so the store's identity beats the template's. Every\n * other key — `host` tokens and anything the contract has never heard of —\n * is written as its `--web5-host-*` alias, which core's stylesheets consume\n * as a `var()` fallback. A template stating the real token therefore wins on\n * shape, and an unknown key is inert rather than dangerous: a custom property\n * nothing references renders nothing, so an adapter that learns to read a new\n * token cannot silently override a template.\n * - **The token set stays open, but the destination changed.** Before DL #193\n * an unrecognised key was applied verbatim, so a template could consume\n * `var(--foo)` and grow a token with no release. It now arrives as\n * `--web5-host-foo`, so the template consumes `var(--web5-host-foo, <default>)`\n * instead. One line different, and the provenance is explicit — the wiring\n * line is what activates a host token, not the contract entry.\n * - The mandatory `--` prefix means an override can only define custom\n * properties — it can never set a real CSS property inside the scope. The\n * server enforces the same shape (plus value sanitation) at write time.\n * - **Values are inert**: entries are written with\n * `CSSStyleDeclaration.setProperty`, never string-concatenated into CSS, so\n * a hostile value cannot terminate the declaration or open a new rule.\n * - **The shop owner is the last word (DL #193)**: `userOverrides` carries the\n * choices a human made in the owner panel, and every one of them is written\n * as the real token whatever its bucket. Bucketing exists to arbitrate a\n * disagreement between a store and a template; an owner who opened a panel\n * and set a value has already settled that argument, so aliasing theirs would\n * let the template overrule the person who chose. They are written last, so a\n * token both maps carry resolves to the owner's value.\n * - **The active theme scheme sits between the two**: `schemeTokens` is the\n * resolved entry of `Configuration.themeSchemes` (see `theme/themeScheme`).\n * It is written after the store map and before the owner's overrides, under\n * the SAME bucket rule as the store map: a `brand` token (colours, fonts) as\n * the real token, anything else — `--radius`, a key the contract has never\n * heard of — as its `--web5-host-*` alias. Being the later write of either\n * property, the chosen palette beats the flat store map on colours and on\n * radius alike — yet a template that states `--radius` still wins on shape,\n * exactly as it does over an imported radius. A scheme is written like the\n * store map it stands in for, not like an owner override, so it gets no\n * exemption from aliasing; the owner's layer still wins over it. Its entries\n * are filtered exactly like the store map's too: the same key check, the same\n * warning, and no per-type value check — `setProperty` keeps any value inert,\n * and the typed grammar (`isSchemeTokenEntry`) is the panel's to enforce\n * before a write. So the scheme token set is as open as `themeOverrides`: a\n * new scheme token needs no release here. Absent → exactly the pre-scheme\n * behaviour.\n * - **Idempotent**: one marker element per document, replaced wholesale on\n * re-apply; empty/absent input (all three layers) removes it.\n */\nimport { WEB5_SCOPE } from '../hostScope';\nimport {\n traceThemeOverrides,\n type AppliedToken,\n type ThemeOverrideSource,\n} from './themeDebug';\nimport {\n THEME_OVERRIDE_KEY_PATTERN,\n THEME_TOKEN_CONTRACT,\n bucketOf,\n hostAliasFor,\n} from '../theme/tokenContract';\n\n/** A themeOverrides key: always a CSS custom property. */\nexport type ThemeOverrideKey = `--${string}`;\n\n/**\n * Writer-side shape for a theme override map. The catalog below documents\n * the tokens the seed consumes today; the type stays open so new tokens\n * flow end-to-end with no release anywhere but the seed.\n */\nexport type ThemeOverrides = Record<ThemeOverrideKey, string>;\n\n/**\n * The known token catalog. GUIDANCE, NOT A GATE — `applyThemeOverrides` still\n * applies any well-formed custom property, so the vocabulary can grow without a\n * release here.\n *\n * Derived from `@wix/web5-token-contract` rather than restated, because two\n * hand-maintained copies of one list is how they drift. The contract also\n * carries what this Set cannot: which bucket each token is in, and therefore\n * who wins when a store and a template disagree.\n */\nexport const THEME_OVERRIDE_TOKENS: ReadonlySet<string> = new Set(\n Object.keys(THEME_TOKEN_CONTRACT),\n);\n\nconst MARKER_ATTR = 'data-web5-theme-overrides';\n\n/**\n * Drops keys that are not well-formed custom properties, warning about each.\n * Used for all three layers — store, scheme and owner.\n */\nfunction wellFormedEntries(\n overrides?: Record<string, string> | null,\n): [string, string][] {\n return Object.entries(overrides ?? {}).filter(([key]) => {\n const wellFormed = THEME_OVERRIDE_KEY_PATTERN.test(key);\n if (!wellFormed) {\n console.warn(\n `[web5-theme] Skipping malformed theme override key: ${key}`,\n );\n }\n return wellFormed;\n });\n}\n\n/**\n * Where a store-side token lands: a `brand` token as itself, so the store's\n * identity beats the template's; everything else as its `--web5-host-*` alias,\n * which core consumes only as a fallback the template can beat.\n */\nconst bucketed = (key: string): string =>\n bucketOf(key) === 'brand' ? key : hostAliasFor(key);\n\nexport function applyThemeOverrides(\n overrides?: Record<string, string> | null,\n userOverrides?: Record<string, string> | null,\n schemeTokens?: Record<string, string> | null,\n): void {\n if (typeof document === 'undefined') {\n return;\n }\n\n const existing = document.head.querySelector(`style[${MARKER_ATTR}]`);\n const entries = wellFormedEntries(overrides);\n const userEntries = wellFormedEntries(userOverrides);\n const activeSchemeEntries = wellFormedEntries(schemeTokens);\n\n if (\n entries.length === 0 &&\n activeSchemeEntries.length === 0 &&\n userEntries.length === 0\n ) {\n existing?.remove();\n // Nothing to apply is itself a traceable answer: it means every token on\n // the page is the template's, which is otherwise indistinguishable from\n // tracing having failed.\n traceThemeOverrides([]);\n return;\n }\n\n const style = document.createElement('style');\n style.setAttribute(MARKER_ATTR, '');\n document.head.appendChild(style);\n const sheet = style.sheet;\n if (!sheet) {\n style.remove();\n return;\n }\n sheet.insertRule(`${WEB5_SCOPE} {}`, 0);\n const rule = sheet.cssRules[0] as CSSStyleRule;\n const applied: AppliedToken[] = [];\n const write = (\n key: string,\n value: string,\n target: string,\n source: ThemeOverrideSource,\n ): void => {\n try {\n rule.style.setProperty(target, value);\n applied.push({ token: key, value, writtenAs: target, source });\n } catch {\n // An engine that rejects the value leaves the token at its baked\n // default — degraded theming, never broken CSS.\n }\n };\n\n for (const [key, value] of entries) {\n // Brand wins over the template, so it is written as the token the\n // stylesheets actually read. Everything else lands in the host namespace,\n // where core consumes it only as a fallback the template can beat.\n write(key, value, bucketed(key), 'store');\n }\n // The active scheme over the store map, bucketed the same way: a brand token\n // as itself, `--radius` and unknown keys as their alias. Either way this is\n // the later write of that property, so it beats the store's — and a\n // template's `--radius` still beats the alias.\n for (const [key, value] of activeSchemeEntries) {\n write(key, value, bucketed(key), 'scheme');\n }\n // Owner choices last and never aliased: within one declaration block the last\n // write of a property wins, so a token both maps carry ends up the owner's.\n for (const [key, value] of userEntries) {\n write(key, value, key, 'user');\n }\n // Replace-on-reapply: the fresh element is appended (so it stays last in\n // document order) before the stale one is dropped.\n existing?.remove();\n // AFTER the stale element is gone, so the resolved values traced below are\n // the ones the page will actually render. Off unless explicitly enabled.\n traceThemeOverrides(applied);\n}\n"],"mappings":";;;;;AA4DA,IAAAA,UAAA,GAAAC,OAAA;AACA,IAAAC,WAAA,GAAAD,OAAA;AAKA,IAAAE,cAAA,GAAAF,OAAA;AAlEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAcA;;AAGA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMG,qBAA0C,GAAAC,OAAA,CAAAD,qBAAA,GAAG,IAAIE,GAAG,CAC/DC,MAAM,CAACC,IAAI,CAACC,mCAAoB,CAClC,CAAC;AAED,MAAMC,WAAW,GAAG,2BAA2B;;AAE/C;AACA;AACA;AACA;AACA,SAASC,iBAAiBA,CACxBC,SAAyC,EACrB;EACpB,OAAOL,MAAM,CAACM,OAAO,CAACD,SAAS,IAAI,CAAC,CAAC,CAAC,CAACE,MAAM,CAAC,CAAC,CAACC,GAAG,CAAC,KAAK;IACvD,MAAMC,UAAU,GAAGC,yCAA0B,CAACC,IAAI,CAACH,GAAG,CAAC;IACvD,IAAI,CAACC,UAAU,EAAE;MACfG,OAAO,CAACC,IAAI,CACV,uDAAuDL,GAAG,EAC5D,CAAC;IACH;IACA,OAAOC,UAAU;EACnB,CAAC,CAAC;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA,MAAMK,QAAQ,GAAIN,GAAW,IAC3B,IAAAO,uBAAQ,EAACP,GAAG,CAAC,KAAK,OAAO,GAAGA,GAAG,GAAG,IAAAQ,2BAAY,EAACR,GAAG,CAAC;AAE9C,SAASS,mBAAmBA,CACjCZ,SAAyC,EACzCa,aAA6C,EAC7CC,YAA4C,EACtC;EACN,IAAI,OAAOC,QAAQ,KAAK,WAAW,EAAE;IACnC;EACF;EAEA,MAAMC,QAAQ,GAAGD,QAAQ,CAACE,IAAI,CAACC,aAAa,CAAC,SAASpB,WAAW,GAAG,CAAC;EACrE,MAAMG,OAAO,GAAGF,iBAAiB,CAACC,SAAS,CAAC;EAC5C,MAAMmB,WAAW,GAAGpB,iBAAiB,CAACc,aAAa,CAAC;EACpD,MAAMO,mBAAmB,GAAGrB,iBAAiB,CAACe,YAAY,CAAC;EAE3D,IACEb,OAAO,CAACoB,MAAM,KAAK,CAAC,IACpBD,mBAAmB,CAACC,MAAM,KAAK,CAAC,IAChCF,WAAW,CAACE,MAAM,KAAK,CAAC,EACxB;IACAL,QAAQ,YAARA,QAAQ,CAAEM,MAAM,CAAC,CAAC;IAClB;IACA;IACA;IACA,IAAAC,+BAAmB,EAAC,EAAE,CAAC;IACvB;EACF;EAEA,MAAMC,KAAK,GAAGT,QAAQ,CAACU,aAAa,CAAC,OAAO,CAAC;EAC7CD,KAAK,CAACE,YAAY,CAAC5B,WAAW,EAAE,EAAE,CAAC;EACnCiB,QAAQ,CAACE,IAAI,CAACU,WAAW,CAACH,KAAK,CAAC;EAChC,MAAMI,KAAK,GAAGJ,KAAK,CAACI,KAAK;EACzB,IAAI,CAACA,KAAK,EAAE;IACVJ,KAAK,CAACF,MAAM,CAAC,CAAC;IACd;EACF;EACAM,KAAK,CAACC,UAAU,CAAC,GAAGC,qBAAU,KAAK,EAAE,CAAC,CAAC;EACvC,MAAMC,IAAI,GAAGH,KAAK,CAACI,QAAQ,CAAC,CAAC,CAAiB;EAC9C,MAAMC,OAAuB,GAAG,EAAE;EAClC,MAAMC,KAAK,GAAGA,CACZ/B,GAAW,EACXgC,KAAa,EACbC,MAAc,EACdC,MAA2B,KAClB;IACT,IAAI;MACFN,IAAI,CAACP,KAAK,CAACc,WAAW,CAACF,MAAM,EAAED,KAAK,CAAC;MACrCF,OAAO,CAACM,IAAI,CAAC;QAAEC,KAAK,EAAErC,GAAG;QAAEgC,KAAK;QAAEM,SAAS,EAAEL,MAAM;QAAEC;MAAO,CAAC,CAAC;IAChE,CAAC,CAAC,MAAM;MACN;MACA;IAAA;EAEJ,CAAC;EAED,KAAK,MAAM,CAAClC,GAAG,EAAEgC,KAAK,CAAC,IAAIlC,OAAO,EAAE;IAClC;IACA;IACA;IACAiC,KAAK,CAAC/B,GAAG,EAAEgC,KAAK,EAAE1B,QAAQ,CAACN,GAAG,CAAC,EAAE,OAAO,CAAC;EAC3C;EACA;EACA;EACA;EACA;EACA,KAAK,MAAM,CAACA,GAAG,EAAEgC,KAAK,CAAC,IAAIf,mBAAmB,EAAE;IAC9Cc,KAAK,CAAC/B,GAAG,EAAEgC,KAAK,EAAE1B,QAAQ,CAACN,GAAG,CAAC,EAAE,QAAQ,CAAC;EAC5C;EACA;EACA;EACA,KAAK,MAAM,CAACA,GAAG,EAAEgC,KAAK,CAAC,IAAIhB,WAAW,EAAE;IACtCe,KAAK,CAAC/B,GAAG,EAAEgC,KAAK,EAAEhC,GAAG,EAAE,MAAM,CAAC;EAChC;EACA;EACA;EACAa,QAAQ,YAARA,QAAQ,CAAEM,MAAM,CAAC,CAAC;EAClB;EACA;EACA,IAAAC,+BAAmB,EAACU,OAAO,CAAC;AAC9B","ignoreList":[]}
@@ -27,8 +27,12 @@ var _tokenContract = require("../theme/tokenContract");
27
27
  *
28
28
  * [web5:theme] --radius host wrote --web5-host-radius=0rem → 1.25rem TEMPLATE
29
29
  * [web5:theme] --primary brand wrote --primary=0 0% 9% → 0 0% 9% STORE
30
+ * [web5:theme] --radius scheme wrote --web5-host-radius=1rem → 1rem SCHEME
30
31
  * [web5:theme] --radius owner wrote --radius=2rem → 2rem OWNER
31
32
  *
33
+ * A scheme is bucketed like the store map, so its `--radius` is written as the
34
+ * alias too — and can lose to a template the same way.
35
+ *
32
36
  * Off by default. Enable with `?web5DebugTheme=1` (one-shot, wins over storage)
33
37
  * or `localStorage["web5_debug_theme"] = "1"` (sticky) — the same shape as
34
38
  * `matchDebug`, so there is one convention to learn rather than two.
@@ -74,7 +78,7 @@ const resetThemeDebugCache = () => {
74
78
  cached = null;
75
79
  };
76
80
 
77
- /** Which layer asked for a value: the platform import, or a human. */
81
+ /** Which layer asked for a value: the platform import, the active theme scheme, or a human. */
78
82
 
79
83
  /** One token as it was actually written to the mount rule. */
80
84
  exports.resetThemeDebugCache = resetThemeDebugCache;
@@ -113,13 +117,18 @@ function traceThemeOverrides(applied) {
113
117
  // One computed-style read for the whole batch: the expensive part is the style
114
118
  // recalculation it forces, not the per-property lookups off the result.
115
119
  const computed = getComputedStyle(mount);
116
- // An owner's write is the last one into the rule, so when both layers name a
117
- // token the resolved value is theirs check the owner first or a store value
120
+ // Layers are written store scheme owner, and the last write of a token is
121
+ // the one that resolvesso the latest layer is credited, or a store value
118
122
  // that happens to match would be credited with a win it did not have.
123
+ const rank = {
124
+ store: 0,
125
+ scheme: 1,
126
+ user: 2
127
+ };
119
128
  const byToken = new Map();
120
129
  for (const entry of applied) {
121
130
  const held = byToken.get(entry.token);
122
- if (!held || entry.source === 'user') {
131
+ if (!held || rank[entry.source] >= rank[held.source]) {
123
132
  byToken.set(entry.token, entry);
124
133
  }
125
134
  }
@@ -134,11 +143,11 @@ function traceThemeOverrides(applied) {
134
143
  const wanted = value.trim();
135
144
  return {
136
145
  token,
137
- bucket: source === 'user' ? 'owner' : (0, _tokenContract.bucketOf)(token),
146
+ bucket: source === 'user' ? 'owner' : source === 'scheme' ? 'scheme' : (0, _tokenContract.bucketOf)(token),
138
147
  'written as': writtenAs,
139
148
  'asked for': wanted,
140
149
  'resolves to': resolved || '(nothing reads it)',
141
- winner: !resolved ? 'NOBODY — no stylesheet consumes this token' : resolved !== wanted ? 'TEMPLATE' : source === 'user' ? 'OWNER' : 'STORE'
150
+ winner: !resolved ? 'NOBODY — no stylesheet consumes this token' : resolved !== wanted ? 'TEMPLATE' : source === 'user' ? 'OWNER' : source === 'scheme' ? 'SCHEME' : 'STORE'
142
151
  };
143
152
  });
144
153
  const overridden = rows.filter(r => r.winner === 'TEMPLATE').length;
@@ -1 +1 @@
1
- {"version":3,"names":["_hostScope","require","_tokenContract","THEME_DEBUG_KEY","exports","THEME_DEBUG_QUERY_PARAM","LOG_PREFIX","isTruthy","value","cached","isThemeDebugEnabled","URLSearchParams","window","location","search","get","localStorage","getItem","resetThemeDebugCache","traceThemeOverrides","applied","length","console","info","mount","document","querySelector","WEB5_SCOPES","join","warn","computed","getComputedStyle","byToken","Map","entry","held","token","source","set","rows","values","map","writtenAs","resolved","getPropertyValue","trim","wanted","bucket","bucketOf","winner","overridden","filter","r","inert","startsWith","groupCollapsed","table","groupEnd"],"sources":["../../../src/client/themeDebug.ts"],"sourcesContent":["/**\n * Theme-override tracing (debug-only).\n *\n * Answers the one question the token pipeline cannot otherwise be asked:\n * **which layer actually won?**\n *\n * Since DL #193 a `brand` token is written as itself and everything else as its\n * `--web5-host-*` alias, which core's stylesheets consume as a `var()` fallback\n * a template can beat. That makes the outcome a cascade decision, and a cascade\n * decision is invisible — there is no callback, no return value, and nothing in\n * the DOM to read. Worse, `applyThemeOverrides` writes through the CSSOM\n * (`insertRule` + `setProperty`, never `textContent`) so that a hostile value\n * can never terminate a declaration, which means DevTools renders the marker as\n * `<style data-web5-theme-overrides=\"\">` — an empty tag, with the rules real but\n * nowhere in the DOM tree. Every part of that is deliberate and every part of it\n * looks broken.\n *\n * So this reads the resolved value back off the mount **after** the rule is in,\n * and reports what the browser actually decided:\n *\n * [web5:theme] --radius host wrote --web5-host-radius=0rem → 1.25rem TEMPLATE\n * [web5:theme] --primary brand wrote --primary=0 0% 9% → 0 0% 9% STORE\n * [web5:theme] --radius owner wrote --radius=2rem → 2rem OWNER\n *\n * Off by default. Enable with `?web5DebugTheme=1` (one-shot, wins over storage)\n * or `localStorage[\"web5_debug_theme\"] = \"1\"` (sticky) — the same shape as\n * `matchDebug`, so there is one convention to learn rather than two.\n *\n * It is gated rather than always-on because reading a computed value forces a\n * style recalculation, and this runs during page setup. One read is taken for\n * the whole batch, not one per token.\n */\nimport { WEB5_SCOPES } from '../hostScope';\nimport { bucketOf } from '../theme/tokenContract';\n\nexport const THEME_DEBUG_KEY = 'web5_debug_theme';\n\n/** Query param that forces theme tracing on, overriding localStorage. */\nexport const THEME_DEBUG_QUERY_PARAM = 'web5DebugTheme';\n\nconst LOG_PREFIX = '[web5:theme]';\n\nconst isTruthy = (value: unknown): boolean => value === '1' || value === 'true';\n\nlet cached: boolean | null = null;\n\n/** Whether theme tracing is enabled. Memoized — resolved once per page load. */\nexport const isThemeDebugEnabled = (): boolean => {\n if (cached !== null) {\n return cached;\n }\n cached = false;\n try {\n if (\n isTruthy(\n new URLSearchParams(window.location.search).get(\n THEME_DEBUG_QUERY_PARAM,\n ),\n )\n ) {\n cached = true;\n return cached;\n }\n } catch {\n // window / URLSearchParams unavailable (SSR, non-DOM environments).\n }\n try {\n cached = isTruthy(localStorage.getItem(THEME_DEBUG_KEY));\n } catch {\n // localStorage can throw in privacy mode / non-DOM environments.\n }\n return cached;\n};\n\n/** Test seam — the flag is memoized for the page, so tests must be able to clear it. */\nexport const resetThemeDebugCache = (): void => {\n cached = null;\n};\n\n/** Which layer asked for a value: the platform import, or a human. */\nexport type ThemeOverrideSource = 'store' | 'user';\n\n/** One token as it was actually written to the mount rule. */\nexport interface AppliedToken {\n token: string;\n value: string;\n writtenAs: string;\n source: ThemeOverrideSource;\n}\n\ninterface TraceRow {\n token: string;\n bucket: string;\n 'written as': string;\n 'asked for': string;\n 'resolves to': string;\n winner: string;\n}\n\n/**\n * Report what each override did, after the rule is live.\n *\n * `applied` is what `applyThemeOverrides` actually wrote — already filtered for\n * key shape, so anything malformed has been dropped and warned about before\n * reaching here.\n */\nexport function traceThemeOverrides(applied: AppliedToken[]): void {\n if (!isThemeDebugEnabled()) {\n return;\n }\n if (applied.length === 0) {\n // Silence here would be indistinguishable from tracing being broken, and\n // \"no overrides reached the page\" is itself the answer often enough — a\n // store with nothing imported means every token is the template's.\n // eslint-disable-next-line no-console\n console.info(\n `${LOG_PREFIX} no overrides applied — nothing usable arrived for this store, so every token is whatever the template and core defaults say`,\n );\n return;\n }\n let mount: Element | null = null;\n try {\n mount = document.querySelector(WEB5_SCOPES.join(','));\n } catch {\n // Malformed selector cannot happen with the constant, but querySelector is\n // the one call here that can throw, and a debug aid must never be the thing\n // that breaks a page.\n }\n if (!mount) {\n console.warn(\n `${LOG_PREFIX} no mount found (${WEB5_SCOPES.join(\n ', ',\n )}) — tokens were written, but their resolved values cannot be read`,\n );\n return;\n }\n\n // One computed-style read for the whole batch: the expensive part is the style\n // recalculation it forces, not the per-property lookups off the result.\n const computed = getComputedStyle(mount);\n // An owner's write is the last one into the rule, so when both layers name a\n // token the resolved value is theirs — check the owner first or a store value\n // that happens to match would be credited with a win it did not have.\n const byToken = new Map<string, AppliedToken>();\n for (const entry of applied) {\n const held = byToken.get(entry.token);\n if (!held || entry.source === 'user') {\n byToken.set(entry.token, entry);\n }\n }\n const rows: TraceRow[] = [...byToken.values()].map((entry) => {\n const { token, value, writtenAs, source } = entry;\n const resolved = computed.getPropertyValue(token).trim();\n const wanted = value.trim();\n return {\n token,\n bucket: source === 'user' ? 'owner' : bucketOf(token),\n 'written as': writtenAs,\n 'asked for': wanted,\n 'resolves to': resolved || '(nothing reads it)',\n winner: !resolved\n ? 'NOBODY — no stylesheet consumes this token'\n : resolved !== wanted\n ? 'TEMPLATE'\n : source === 'user'\n ? 'OWNER'\n : 'STORE',\n };\n });\n\n const overridden = rows.filter((r) => r.winner === 'TEMPLATE').length;\n const inert = rows.filter((r) => r.winner.startsWith('NOBODY')).length;\n\n /* eslint-disable no-console */\n console.groupCollapsed(\n `${LOG_PREFIX} ${rows.length} override(s) applied · ${overridden} kept by the template · ${inert} inert`,\n );\n console.table(rows);\n if (inert > 0) {\n console.info(\n `${LOG_PREFIX} \"inert\" means the value was written but no stylesheet reads that token — ` +\n `for a host token that means core has no \\`var(--web5-host-…)\\` wiring for it yet.`,\n );\n }\n console.info(\n `${LOG_PREFIX} the marker <style data-web5-theme-overrides> looks empty in DevTools on purpose: ` +\n `rules are inserted through the CSSOM, never as text. Read them with ` +\n `document.querySelector('style[data-web5-theme-overrides]').sheet.cssRules[0].cssText`,\n );\n console.groupEnd();\n /* eslint-enable no-console */\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_hostScope","require","_tokenContract","THEME_DEBUG_KEY","exports","THEME_DEBUG_QUERY_PARAM","LOG_PREFIX","isTruthy","value","cached","isThemeDebugEnabled","URLSearchParams","window","location","search","get","localStorage","getItem","resetThemeDebugCache","traceThemeOverrides","applied","length","console","info","mount","document","querySelector","WEB5_SCOPES","join","warn","computed","getComputedStyle","rank","store","scheme","user","byToken","Map","entry","held","token","source","set","rows","values","map","writtenAs","resolved","getPropertyValue","trim","wanted","bucket","bucketOf","winner","overridden","filter","r","inert","startsWith","groupCollapsed","table","groupEnd"],"sources":["../../../src/client/themeDebug.ts"],"sourcesContent":["/**\n * Theme-override tracing (debug-only).\n *\n * Answers the one question the token pipeline cannot otherwise be asked:\n * **which layer actually won?**\n *\n * Since DL #193 a `brand` token is written as itself and everything else as its\n * `--web5-host-*` alias, which core's stylesheets consume as a `var()` fallback\n * a template can beat. That makes the outcome a cascade decision, and a cascade\n * decision is invisible — there is no callback, no return value, and nothing in\n * the DOM to read. Worse, `applyThemeOverrides` writes through the CSSOM\n * (`insertRule` + `setProperty`, never `textContent`) so that a hostile value\n * can never terminate a declaration, which means DevTools renders the marker as\n * `<style data-web5-theme-overrides=\"\">` — an empty tag, with the rules real but\n * nowhere in the DOM tree. Every part of that is deliberate and every part of it\n * looks broken.\n *\n * So this reads the resolved value back off the mount **after** the rule is in,\n * and reports what the browser actually decided:\n *\n * [web5:theme] --radius host wrote --web5-host-radius=0rem → 1.25rem TEMPLATE\n * [web5:theme] --primary brand wrote --primary=0 0% 9% → 0 0% 9% STORE\n * [web5:theme] --radius scheme wrote --web5-host-radius=1rem → 1rem SCHEME\n * [web5:theme] --radius owner wrote --radius=2rem → 2rem OWNER\n *\n * A scheme is bucketed like the store map, so its `--radius` is written as the\n * alias too — and can lose to a template the same way.\n *\n * Off by default. Enable with `?web5DebugTheme=1` (one-shot, wins over storage)\n * or `localStorage[\"web5_debug_theme\"] = \"1\"` (sticky) — the same shape as\n * `matchDebug`, so there is one convention to learn rather than two.\n *\n * It is gated rather than always-on because reading a computed value forces a\n * style recalculation, and this runs during page setup. One read is taken for\n * the whole batch, not one per token.\n */\nimport { WEB5_SCOPES } from '../hostScope';\nimport { bucketOf } from '../theme/tokenContract';\n\nexport const THEME_DEBUG_KEY = 'web5_debug_theme';\n\n/** Query param that forces theme tracing on, overriding localStorage. */\nexport const THEME_DEBUG_QUERY_PARAM = 'web5DebugTheme';\n\nconst LOG_PREFIX = '[web5:theme]';\n\nconst isTruthy = (value: unknown): boolean => value === '1' || value === 'true';\n\nlet cached: boolean | null = null;\n\n/** Whether theme tracing is enabled. Memoized — resolved once per page load. */\nexport const isThemeDebugEnabled = (): boolean => {\n if (cached !== null) {\n return cached;\n }\n cached = false;\n try {\n if (\n isTruthy(\n new URLSearchParams(window.location.search).get(\n THEME_DEBUG_QUERY_PARAM,\n ),\n )\n ) {\n cached = true;\n return cached;\n }\n } catch {\n // window / URLSearchParams unavailable (SSR, non-DOM environments).\n }\n try {\n cached = isTruthy(localStorage.getItem(THEME_DEBUG_KEY));\n } catch {\n // localStorage can throw in privacy mode / non-DOM environments.\n }\n return cached;\n};\n\n/** Test seam — the flag is memoized for the page, so tests must be able to clear it. */\nexport const resetThemeDebugCache = (): void => {\n cached = null;\n};\n\n/** Which layer asked for a value: the platform import, the active theme scheme, or a human. */\nexport type ThemeOverrideSource = 'store' | 'scheme' | 'user';\n\n/** One token as it was actually written to the mount rule. */\nexport interface AppliedToken {\n token: string;\n value: string;\n writtenAs: string;\n source: ThemeOverrideSource;\n}\n\ninterface TraceRow {\n token: string;\n bucket: string;\n 'written as': string;\n 'asked for': string;\n 'resolves to': string;\n winner: string;\n}\n\n/**\n * Report what each override did, after the rule is live.\n *\n * `applied` is what `applyThemeOverrides` actually wrote — already filtered for\n * key shape, so anything malformed has been dropped and warned about before\n * reaching here.\n */\nexport function traceThemeOverrides(applied: AppliedToken[]): void {\n if (!isThemeDebugEnabled()) {\n return;\n }\n if (applied.length === 0) {\n // Silence here would be indistinguishable from tracing being broken, and\n // \"no overrides reached the page\" is itself the answer often enough — a\n // store with nothing imported means every token is the template's.\n // eslint-disable-next-line no-console\n console.info(\n `${LOG_PREFIX} no overrides applied — nothing usable arrived for this store, so every token is whatever the template and core defaults say`,\n );\n return;\n }\n let mount: Element | null = null;\n try {\n mount = document.querySelector(WEB5_SCOPES.join(','));\n } catch {\n // Malformed selector cannot happen with the constant, but querySelector is\n // the one call here that can throw, and a debug aid must never be the thing\n // that breaks a page.\n }\n if (!mount) {\n console.warn(\n `${LOG_PREFIX} no mount found (${WEB5_SCOPES.join(\n ', ',\n )}) — tokens were written, but their resolved values cannot be read`,\n );\n return;\n }\n\n // One computed-style read for the whole batch: the expensive part is the style\n // recalculation it forces, not the per-property lookups off the result.\n const computed = getComputedStyle(mount);\n // Layers are written store → scheme → owner, and the last write of a token is\n // the one that resolves — so the latest layer is credited, or a store value\n // that happens to match would be credited with a win it did not have.\n const rank: Record<ThemeOverrideSource, number> = {\n store: 0,\n scheme: 1,\n user: 2,\n };\n const byToken = new Map<string, AppliedToken>();\n for (const entry of applied) {\n const held = byToken.get(entry.token);\n if (!held || rank[entry.source] >= rank[held.source]) {\n byToken.set(entry.token, entry);\n }\n }\n const rows: TraceRow[] = [...byToken.values()].map((entry) => {\n const { token, value, writtenAs, source } = entry;\n const resolved = computed.getPropertyValue(token).trim();\n const wanted = value.trim();\n return {\n token,\n bucket:\n source === 'user'\n ? 'owner'\n : source === 'scheme'\n ? 'scheme'\n : bucketOf(token),\n 'written as': writtenAs,\n 'asked for': wanted,\n 'resolves to': resolved || '(nothing reads it)',\n winner: !resolved\n ? 'NOBODY — no stylesheet consumes this token'\n : resolved !== wanted\n ? 'TEMPLATE'\n : source === 'user'\n ? 'OWNER'\n : source === 'scheme'\n ? 'SCHEME'\n : 'STORE',\n };\n });\n\n const overridden = rows.filter((r) => r.winner === 'TEMPLATE').length;\n const inert = rows.filter((r) => r.winner.startsWith('NOBODY')).length;\n\n /* eslint-disable no-console */\n console.groupCollapsed(\n `${LOG_PREFIX} ${rows.length} override(s) applied · ${overridden} kept by the template · ${inert} inert`,\n );\n console.table(rows);\n if (inert > 0) {\n console.info(\n `${LOG_PREFIX} \"inert\" means the value was written but no stylesheet reads that token — ` +\n `for a host token that means core has no \\`var(--web5-host-…)\\` wiring for it yet.`,\n );\n }\n console.info(\n `${LOG_PREFIX} the marker <style data-web5-theme-overrides> looks empty in DevTools on purpose: ` +\n `rules are inserted through the CSSOM, never as text. Read them with ` +\n `document.querySelector('style[data-web5-theme-overrides]').sheet.cssRules[0].cssText`,\n );\n console.groupEnd();\n /* eslint-enable no-console */\n}\n"],"mappings":"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","ignoreList":[]}
package/dist/cjs/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.UserQueryProvider = exports.UserQuery = exports.UnifiedMarkdownParser = exports.UnifiedLink = exports.UNKNOWN_CONSENT = exports.UNBRANDED_ASSISTANT_NAME = exports.TextBlockSectionDefinition = exports.TableRow = exports.TableHeader = exports.TableHead = exports.TableFooter = exports.TableCell = exports.TableCaption = exports.TableBody = exports.Table = exports.TOKEN_NAME_PATTERN = exports.THEME_TOKEN_CONTRACT = exports.THEME_OVERRIDE_TOKENS = exports.THEME_DEBUG_QUERY_PARAM = exports.THEME_DEBUG_KEY = exports.TEMPLATES_MANIFEST_URL = exports.TEMPLATES_CDN_BASE = exports.SmartIcon = exports.SkipNodesSectionDefinition = exports.ShopifyStorefrontClient = exports.SectionsRuntimeProvider = exports.SectionSkeleton = exports.SectionRuntimeProvider = exports.SearchSectionDefinition = exports.SearchSection = exports.STREAMING_TIMEOUT_MS = exports.REFRESH_PROMPTS_WINDOW_MS = exports.REFRESH_PROMPTS_UNTIL_KEY = exports.PromptEntryEmptyState = exports.PoweredByBadge = exports.PlacementSmoothHeight = exports.PlacementResponseRenderer = exports.PlacementPayloadProvider = exports.PlacementLoader = exports.PRODUCT_BY_HANDLE_QUERY = exports.PRODUCT_BACK_SESSION_KEY = exports.PLATFORM_ENTITY_TYPES = exports.OptimizedImage = exports.NextStepsSectionDefinition = exports.MetricsSectionDefinition = exports.MarkdownText = exports.MATCH_DEBUG_QUERY_PARAM = exports.MATCH_DEBUG_KEY = exports.Loader = exports.ListItemsSectionDefinition = exports.LinkType = exports.LIST_ITEMS_ENDPOINT = exports.KpiSectionDefinition = exports.ImageSlotProvider = exports.ImageSearchFilterToken = exports.HtmlCommentSectionDefinition = exports.HeroSectionDefinition = exports.HeroEntitySectionDefinition = exports.HOST_CONSENT_GLOBAL = exports.FeedbackBar = exports.FeatureToggleProvider = exports.FeatureSection9PlusDefinition = exports.FeatureCardsSectionDefinition = exports.FallbackSectionDefinition = exports.ErrorSectionDefinition = exports.EntitySectionDefinition = exports.EntityCollectionSectionDefinition = exports.EXPERIMENT_IDS = exports.ERROR_MARKDOWN = exports.EDITABLE_TOKENS = exports.DiagnosticsCollector = exports.DROP_SECTION = exports.DIAGNOSTIC_TYPES = exports.DEFAULT_PROMPT_PLACEHOLDER = exports.DEFAULT_POWERED_BY_HREF = exports.DEFAULT_ERROR_TEMPLATES = exports.DEFAULT_BACKEND_ENVIRONMENT = exports.DEFAULT_AI_DISCLOSURE_TEXT = exports.CtaBannerSectionDefinition = exports.ComponentTracking = exports.ComponentRegistry = exports.ComponentDependenciesProvider = exports.ComparisonSectionDefinition = exports.ChipsProvider = exports.CalloutSectionDefinition = exports.CalloutBlock = exports.CONSENT_OVERRIDE_QUERY_PARAM = exports.CONSENT_OVERRIDE_KEY = exports.COLLECTION_BY_HANDLE_QUERY = exports.CLIENT_IDS = exports.CALLOUT_SEMANTICS = exports.CALLOUT_KINDS = exports.BottomContainer = exports.BRAND_TOKENS = exports.BACKEND_ENVIRONMENT_QUERY_PARAM = exports.BACKEND_ENVIRONMENT_KEY = exports.AiIcon = exports.AiDisclosure = exports.ASSISTANT_TOKEN = exports.ARTICLE_BY_HANDLE_QUERY = void 0;
5
- exports.isDocumentFamilyEntityType = exports.installConsentProvider = exports.initConsentGate = exports.hslTripletToHex = exports.hslToRgb = exports.hostAliasFor = exports.hexToHslTriplet = exports.hasImage = exports.hasHostSuppliedConsent = exports.hasAnalyticsConsent = exports.getTemplateOverride = exports.getSessionId = exports.getResizedImageUrl = exports.getRefreshPromptsExpiry = exports.getPartsByType = exports.getPartByRole = exports.getOrCreateSessionId = exports.getLinks = exports.getIntentFromMarkdown = exports.getInstalledProviderName = exports.getImages = exports.getHeading = exports.getGateView = exports.getForwardStack = exports.getErrorTypeFromStatus = exports.getEntityExtractor = exports.getContextualImageFilename = exports.getConsentSnapshot = exports.getConsentOverride = exports.getConsentGateStats = exports.getClientBundleOverride = exports.getChatId = exports.getBackendEnvironment = exports.getAllByRole = exports.generateSectionId = exports.generateId = exports.formatProductPriceLabel = exports.formatPriceField = exports.formatMoney = exports.fixMalformedLinks = exports.findKeywordsInContent = exports.findInvalidWeb5Links = exports.findImageInNode = exports.findImageInChildren = exports.fetchProductsByHandlesMap = exports.fetchProductsByHandles = exports.fetchEntityListData = exports.extractProtocol = exports.extractLinkMetadata = exports.extractIntentFromMarkdown = exports.extractContentMarkdown = exports.extractContent = exports.escapeWeb5Links = exports.entityPayloadFromItems = exports.entityHref = exports.ensureMinLightness = exports.enrichEntitiesFromPayload = exports.enableRefreshPrompts = exports.disableRefreshPrompts = exports.detectLinkType = exports.detectConsentProvider = exports.deriveRole = exports.deriveLightColor = exports.deriveDarkGradient = exports.deriveDarkColor = exports.defaultExtractor = exports.decodeLinkText = exports.createWixAuthFetch = exports.createShopifyConsentProvider = exports.createSdkRegistry = exports.createPageSection = exports.createOneTrustConsentProvider = exports.createHostSuppliedConsentProvider = exports.createFeatureToggleReader = exports.createErrorMarkdown = exports.convertToBlockElementsWithMapping = exports.convertToBlockElements = exports.computeContentBBox = exports.composeSemantic = exports.cn = exports.clearForwardStack = exports.buildProbeUrl = exports.buildPlacementDependencies = exports.buildImageSearchFilter = exports.bucketOf = exports.backgroundFilter = exports.assistantName = exports.applyThemeOverrides = exports.analyzeBackdrop = exports.addToCart = exports.Web5UrlType = exports.WEB5_USER_QUERY_EVENT = exports.WEB5_SCOPES = exports.WEB5_SCOPE = exports.WEB5_ROOT_ID = exports.WEB5_ROOT_CLASS = exports.WEB5_REDIRECT_EVENT = exports.WEB5_GLOBAL_TOKENS = exports.WEB5_ANSWER_UPDATED_EVENT = exports.WEB5_ANSWER_SETTLED_EVENT = void 0;
6
- exports.useEntityTransforms = exports.useDebugImageContext = exports.useCurrentSectionOptions = exports.useCurrentSectionId = exports.useConversation = exports.useComponentDependencies = exports.useChips = exports.unlockOnUserAction = exports.tryParseComponent = exports.trimTrailingWhitespace = exports.transmit = exports.transformToSolutionEntityData = exports.transformToGenericEntityData = exports.transformToBlogPostEntityData = exports.transformShopifyProduct = exports.transformShopifyEntityToItemData = exports.transformShopifyCollection = exports.transformShopifyArticle = exports.transformSSProductToEntityItemData = exports.toRgb = exports.toMatchedOptions = exports.toCatalogPath = exports.subscribeToConsent = exports.stripMarkdown = exports.startNewChatId = exports.shouldRefreshPrompts = exports.setMatchDebug = exports.setForwardStack = exports.setConsentOverride = exports.setConsentBufferLimit = exports.setChatId = exports.setBackendEnvironment = exports.rgbToHsl = exports.resolveShopifyEntity = exports.resolveShopifyConfig = exports.resolveErrorTemplate = exports.resolveEntityTypeConfig = exports.resolveClientBundleUrl = exports.resolveAssistantPlaceholder = exports.resetMatchDebugCache = exports.resetConsentGateForTests = exports.resetChatIdForTests = exports.registerEntityExtractor = exports.readProductBackHandoff = exports.readCurrencyCode = exports.pushToForwardStack = exports.pushPromptSubmit = exports.pushLinkClick = exports.pushExit = exports.pushEvent = exports.pushError = exports.pushEntityFiltered = exports.publishHostConsent = exports.preprocessMarkdown = exports.popFromForwardStack = exports.parseWeb5Url = exports.parseMarkdownToComponents = exports.parseMarkdownToAst = exports.parseEntityLink = exports.parseAstToMarkdown = exports.normalizeImageUrl = exports.normalizeIconUrls = exports.normalizeEntityItem = exports.nodesToParts = exports.mergeSectionsWithStableReferences = exports.mergeEntityData = exports.mergeClientConfig = exports.mayTransmit = exports.mayPersistIdentity = exports.matchMarkdown = exports.matchAllSections = exports.logMatchDebug = exports.loadImagePixels = exports.loadClientBundle = exports.loadBackdropAnalysis = exports.listEntityItems = exports.isWeb5Url = exports.isWeb5SearchUrl = exports.isWeb5ImageUrl = exports.isWeb5IconUrl = exports.isWeb5EntityUrl = exports.isWeb5AskUrl = exports.isWeb5ActionUrl = exports.isValidWeb5Url = exports.isValidTemplateId = exports.isValidLinkUrl = exports.isUserEngaged = exports.isTrustedBundleHost = exports.isThemeDebugEnabled = exports.isTemplatePickerRequested = exports.isSimulationTraffic = exports.isShopifyHost = exports.isProductFamilyEntityType = exports.isOneTrustHost = exports.isNavigableUrl = exports.isMatchDebugEnabled = exports.isLegacyUrl = exports.isHtmlComment = exports.isHslTriplet = exports.isEntityLink = void 0;
7
- exports.writeProductBackHandoff = exports.validatePatternWithBlocks = exports.validatePatternSyntax = exports.validatePattern = exports.validateLinkUrl = exports.usesStagingBackend = exports.useWeb5Link = exports.useUserQuery = exports.useResolvedImageSources = exports.useResolveShopifyEntityData = exports.useResolveSearchSpringEntityData = exports.useResolveGenericEntityData = exports.usePlacementPayload = exports.useMarkdownUtils = exports.useImageSlotCollector = exports.useImageSlot = exports.useFeatureToggles = exports.useFeatureToggle = void 0;
4
+ exports.UNKNOWN_CONSENT = exports.UNBRANDED_ASSISTANT_NAME = exports.TextBlockSectionDefinition = exports.TableRow = exports.TableHeader = exports.TableHead = exports.TableFooter = exports.TableCell = exports.TableCaption = exports.TableBody = exports.Table = exports.TOKEN_NAME_PATTERN = exports.THEME_TOKEN_CONTRACT = exports.THEME_OVERRIDE_TOKENS = exports.THEME_OVERRIDE_KEY_PATTERN = exports.THEME_DEBUG_QUERY_PARAM = exports.THEME_DEBUG_KEY = exports.TEMPLATES_MANIFEST_URL = exports.TEMPLATES_CDN_BASE = exports.SmartIcon = exports.SkipNodesSectionDefinition = exports.ShopifyStorefrontClient = exports.SectionsRuntimeProvider = exports.SectionSkeleton = exports.SectionRuntimeProvider = exports.SearchSectionDefinition = exports.SearchSection = exports.STREAMING_TIMEOUT_MS = exports.SCHEME_COLOR_TOKENS = exports.SCHEME_ANCHOR_TOKENS = exports.REFRESH_PROMPTS_WINDOW_MS = exports.REFRESH_PROMPTS_UNTIL_KEY = exports.PromptEntryEmptyState = exports.PoweredByBadge = exports.PlacementSmoothHeight = exports.PlacementResponseRenderer = exports.PlacementPayloadProvider = exports.PlacementLoader = exports.PRODUCT_BY_HANDLE_QUERY = exports.PRODUCT_BACK_SESSION_KEY = exports.PLATFORM_ENTITY_TYPES = exports.OptimizedImage = exports.NextStepsSectionDefinition = exports.MetricsSectionDefinition = exports.MarkdownText = exports.MAX_THEME_SCHEMES = exports.MATCH_DEBUG_QUERY_PARAM = exports.MATCH_DEBUG_KEY = exports.Loader = exports.ListItemsSectionDefinition = exports.LinkType = exports.LIST_ITEMS_ENDPOINT = exports.KpiSectionDefinition = exports.ImageSlotProvider = exports.ImageSearchFilterToken = exports.HtmlCommentSectionDefinition = exports.HeroSectionDefinition = exports.HeroEntitySectionDefinition = exports.HOST_CONSENT_GLOBAL = exports.FeedbackBar = exports.FeatureToggleProvider = exports.FeatureSection9PlusDefinition = exports.FeatureCardsSectionDefinition = exports.FallbackSectionDefinition = exports.ErrorSectionDefinition = exports.EntitySectionDefinition = exports.EntityCollectionSectionDefinition = exports.EXPERIMENT_IDS = exports.ERROR_MARKDOWN = exports.EDITABLE_TOKENS = exports.DiagnosticsCollector = exports.DROP_SECTION = exports.DIAGNOSTIC_TYPES = exports.DEFAULT_PROMPT_PLACEHOLDER = exports.DEFAULT_POWERED_BY_HREF = exports.DEFAULT_ERROR_TEMPLATES = exports.DEFAULT_BACKEND_ENVIRONMENT = exports.DEFAULT_AI_DISCLOSURE_TEXT = exports.CtaBannerSectionDefinition = exports.ComponentTracking = exports.ComponentRegistry = exports.ComponentDependenciesProvider = exports.ComparisonSectionDefinition = exports.ChipsProvider = exports.CalloutSectionDefinition = exports.CalloutBlock = exports.CONSENT_OVERRIDE_QUERY_PARAM = exports.CONSENT_OVERRIDE_KEY = exports.COLLECTION_BY_HANDLE_QUERY = exports.CLIENT_IDS = exports.CALLOUT_SEMANTICS = exports.CALLOUT_KINDS = exports.BottomContainer = exports.BRAND_TOKENS = exports.BACKEND_ENVIRONMENT_QUERY_PARAM = exports.BACKEND_ENVIRONMENT_KEY = exports.AiIcon = exports.AiDisclosure = exports.ASSISTANT_TOKEN = exports.ARTICLE_BY_HANDLE_QUERY = void 0;
5
+ exports.hostAliasFor = exports.hexToHslTriplet = exports.hasImage = exports.hasHostSuppliedConsent = exports.hasAnalyticsConsent = exports.getTemplateOverride = exports.getSessionId = exports.getResizedImageUrl = exports.getRefreshPromptsExpiry = exports.getPartsByType = exports.getPartByRole = exports.getOrCreateSessionId = exports.getLinks = exports.getIntentFromMarkdown = exports.getInstalledProviderName = exports.getImages = exports.getHeading = exports.getGateView = exports.getForwardStack = exports.getErrorTypeFromStatus = exports.getEntityExtractor = exports.getContextualImageFilename = exports.getConsentSnapshot = exports.getConsentOverride = exports.getConsentGateStats = exports.getClientBundleOverride = exports.getChatId = exports.getBackendEnvironment = exports.getAllByRole = exports.generateSectionId = exports.generateId = exports.formatProductPriceLabel = exports.formatPriceField = exports.formatMoney = exports.fixMalformedLinks = exports.findKeywordsInContent = exports.findInvalidWeb5Links = exports.findImageInNode = exports.findImageInChildren = exports.fetchProductsByHandlesMap = exports.fetchProductsByHandles = exports.fetchEntityListData = exports.extractProtocol = exports.extractLinkMetadata = exports.extractIntentFromMarkdown = exports.extractContentMarkdown = exports.extractContent = exports.escapeWeb5Links = exports.entityPayloadFromItems = exports.entityHref = exports.ensureMinLightness = exports.enrichEntitiesFromPayload = exports.enableRefreshPrompts = exports.disableRefreshPrompts = exports.detectLinkType = exports.detectConsentProvider = exports.deriveRole = exports.deriveLightColor = exports.deriveDarkGradient = exports.deriveDarkColor = exports.defaultExtractor = exports.decodeLinkText = exports.createWixAuthFetch = exports.createShopifyConsentProvider = exports.createSdkRegistry = exports.createPageSection = exports.createOneTrustConsentProvider = exports.createHostSuppliedConsentProvider = exports.createFeatureToggleReader = exports.createErrorMarkdown = exports.convertToBlockElementsWithMapping = exports.convertToBlockElements = exports.computeContentBBox = exports.composeSemantic = exports.completeScheme = exports.cn = exports.clearForwardStack = exports.buildProbeUrl = exports.buildPlacementDependencies = exports.buildImageSearchFilter = exports.bucketOf = exports.backgroundFilter = exports.assistantName = exports.applyThemeOverrides = exports.analyzeBackdrop = exports.addToCart = exports.Web5UrlType = exports.WEB5_USER_QUERY_EVENT = exports.WEB5_SCOPES = exports.WEB5_SCOPE = exports.WEB5_ROOT_ID = exports.WEB5_ROOT_CLASS = exports.WEB5_REDIRECT_EVENT = exports.WEB5_GLOBAL_TOKENS = exports.WEB5_ANSWER_UPDATED_EVENT = exports.WEB5_ANSWER_SETTLED_EVENT = exports.UserQueryProvider = exports.UserQuery = exports.UnifiedMarkdownParser = exports.UnifiedLink = void 0;
6
+ exports.transmit = exports.transformToSolutionEntityData = exports.transformToGenericEntityData = exports.transformToBlogPostEntityData = exports.transformShopifyProduct = exports.transformShopifyEntityToItemData = exports.transformShopifyCollection = exports.transformShopifyArticle = exports.transformSSProductToEntityItemData = exports.toRgb = exports.toMatchedOptions = exports.toCatalogPath = exports.subscribeToConsent = exports.stripMarkdown = exports.startNewChatId = exports.shouldRefreshPrompts = exports.setMatchDebug = exports.setForwardStack = exports.setConsentOverride = exports.setConsentBufferLimit = exports.setChatId = exports.setBackendEnvironment = exports.schemeDisplayName = exports.rgbToHsl = exports.resolveShopifyEntity = exports.resolveShopifyConfig = exports.resolveErrorTemplate = exports.resolveEntityTypeConfig = exports.resolveClientBundleUrl = exports.resolveAssistantPlaceholder = exports.resolveActiveScheme = exports.resetMatchDebugCache = exports.resetConsentGateForTests = exports.resetChatIdForTests = exports.registerEntityExtractor = exports.readProductBackHandoff = exports.readCurrencyCode = exports.pushToForwardStack = exports.pushPromptSubmit = exports.pushLinkClick = exports.pushExit = exports.pushEvent = exports.pushError = exports.pushEntityFiltered = exports.publishHostConsent = exports.preprocessMarkdown = exports.popFromForwardStack = exports.parseWeb5Url = exports.parseMarkdownToComponents = exports.parseMarkdownToAst = exports.parseEntityLink = exports.parseAstToMarkdown = exports.normalizeImageUrl = exports.normalizeIconUrls = exports.normalizeEntityItem = exports.nodesToParts = exports.nextOwnerSchemeId = exports.mergeSectionsWithStableReferences = exports.mergeEntityData = exports.mergeClientConfig = exports.mayTransmit = exports.mayPersistIdentity = exports.matchMarkdown = exports.matchAllSections = exports.logMatchDebug = exports.loadImagePixels = exports.loadClientBundle = exports.loadBackdropAnalysis = exports.listEntityItems = exports.isWeb5Url = exports.isWeb5SearchUrl = exports.isWeb5ImageUrl = exports.isWeb5IconUrl = exports.isWeb5EntityUrl = exports.isWeb5AskUrl = exports.isWeb5ActionUrl = exports.isValidWeb5Url = exports.isValidTemplateId = exports.isValidLinkUrl = exports.isUserEngaged = exports.isTrustedBundleHost = exports.isThemeScheme = exports.isThemeDebugEnabled = exports.isTemplatePickerRequested = exports.isSimulationTraffic = exports.isShopifyHost = exports.isSchemeTokenEntry = exports.isProductFamilyEntityType = exports.isOneTrustHost = exports.isNavigableUrl = exports.isMatchDebugEnabled = exports.isLegacyUrl = exports.isHtmlComment = exports.isHslTriplet = exports.isEntityLink = exports.isDocumentFamilyEntityType = exports.installConsentProvider = exports.initConsentGate = exports.hslTripletToHex = exports.hslToRgb = void 0;
7
+ exports.writeProductBackHandoff = exports.validatePatternWithBlocks = exports.validatePatternSyntax = exports.validatePattern = exports.validateLinkUrl = exports.usesStagingBackend = exports.useWeb5Link = exports.useUserQuery = exports.useResolvedImageSources = exports.useResolveShopifyEntityData = exports.useResolveSearchSpringEntityData = exports.useResolveGenericEntityData = exports.usePlacementPayload = exports.useMarkdownUtils = exports.useImageSlotCollector = exports.useImageSlot = exports.useFeatureToggles = exports.useFeatureToggle = exports.useEntityTransforms = exports.useDebugImageContext = exports.useCurrentSectionOptions = exports.useCurrentSectionId = exports.useConversation = exports.useComponentDependencies = exports.useChips = exports.unlockOnUserAction = exports.tryParseComponent = exports.trimTrailingWhitespace = void 0;
8
8
  var _clients = require("./clients");
9
9
  exports.CLIENT_IDS = _clients.CLIENT_IDS;
10
10
  exports.EXPERIMENT_IDS = _clients.EXPERIMENT_IDS;
@@ -331,8 +331,19 @@ exports.THEME_TOKEN_CONTRACT = _tokenContract.THEME_TOKEN_CONTRACT;
331
331
  exports.BRAND_TOKENS = _tokenContract.BRAND_TOKENS;
332
332
  exports.EDITABLE_TOKENS = _tokenContract.EDITABLE_TOKENS;
333
333
  exports.TOKEN_NAME_PATTERN = _tokenContract.TOKEN_NAME_PATTERN;
334
+ exports.THEME_OVERRIDE_KEY_PATTERN = _tokenContract.THEME_OVERRIDE_KEY_PATTERN;
334
335
  exports.bucketOf = _tokenContract.bucketOf;
335
336
  exports.hostAliasFor = _tokenContract.hostAliasFor;
337
+ var _themeScheme = require("./theme/themeScheme");
338
+ exports.SCHEME_COLOR_TOKENS = _themeScheme.SCHEME_COLOR_TOKENS;
339
+ exports.MAX_THEME_SCHEMES = _themeScheme.MAX_THEME_SCHEMES;
340
+ exports.SCHEME_ANCHOR_TOKENS = _themeScheme.SCHEME_ANCHOR_TOKENS;
341
+ exports.completeScheme = _themeScheme.completeScheme;
342
+ exports.isThemeScheme = _themeScheme.isThemeScheme;
343
+ exports.isSchemeTokenEntry = _themeScheme.isSchemeTokenEntry;
344
+ exports.nextOwnerSchemeId = _themeScheme.nextOwnerSchemeId;
345
+ exports.resolveActiveScheme = _themeScheme.resolveActiveScheme;
346
+ exports.schemeDisplayName = _themeScheme.schemeDisplayName;
336
347
  var _themeDebug = require("./client/themeDebug");
337
348
  exports.isThemeDebugEnabled = _themeDebug.isThemeDebugEnabled;
338
349
  exports.THEME_DEBUG_KEY = _themeDebug.THEME_DEBUG_KEY;