@wix/web5-core 1.63.25 → 1.63.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/cjs/components/ui/UserQuery.css +10 -7
  2. package/dist/cjs/components/ui/UserQuery.js +17 -33
  3. package/dist/cjs/components/ui/UserQuery.js.map +1 -1
  4. package/dist/cjs/context/ComponentDependenciesContext.js +15 -1
  5. package/dist/cjs/context/ComponentDependenciesContext.js.map +1 -1
  6. package/dist/cjs/context/ImageSlotContext.js +210 -0
  7. package/dist/cjs/context/ImageSlotContext.js.map +1 -0
  8. package/dist/cjs/hooks/useImageSlot.js +182 -0
  9. package/dist/cjs/hooks/useImageSlot.js.map +1 -0
  10. package/dist/cjs/image/composeSemantic.js +94 -0
  11. package/dist/cjs/image/composeSemantic.js.map +1 -0
  12. package/dist/cjs/image/imageSlotTypes.js +4 -0
  13. package/dist/cjs/image/imageSlotTypes.js.map +1 -0
  14. package/dist/cjs/index.js +11 -3
  15. package/dist/cjs/index.js.map +1 -1
  16. package/dist/cjs/types/dependencies.js.map +1 -1
  17. package/dist/esm/components/ui/UserQuery.css +10 -7
  18. package/dist/esm/components/ui/UserQuery.js +5 -9
  19. package/dist/esm/components/ui/UserQuery.js.map +1 -1
  20. package/dist/esm/context/ComponentDependenciesContext.js +13 -0
  21. package/dist/esm/context/ComponentDependenciesContext.js.map +1 -1
  22. package/dist/esm/context/ImageSlotContext.js +197 -0
  23. package/dist/esm/context/ImageSlotContext.js.map +1 -0
  24. package/dist/esm/hooks/useImageSlot.js +177 -0
  25. package/dist/esm/hooks/useImageSlot.js.map +1 -0
  26. package/dist/esm/image/composeSemantic.js +91 -0
  27. package/dist/esm/image/composeSemantic.js.map +1 -0
  28. package/dist/esm/image/imageSlotTypes.js +2 -0
  29. package/dist/esm/image/imageSlotTypes.js.map +1 -0
  30. package/dist/esm/index.js +7 -0
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/types/dependencies.js.map +1 -1
  33. package/dist/types/components/ui/UserQuery.d.ts.map +1 -1
  34. package/dist/types/context/ComponentDependenciesContext.d.ts +10 -0
  35. package/dist/types/context/ComponentDependenciesContext.d.ts.map +1 -1
  36. package/dist/types/context/ImageSlotContext.d.ts +63 -0
  37. package/dist/types/context/ImageSlotContext.d.ts.map +1 -0
  38. package/dist/types/hooks/useImageSlot.d.ts +35 -0
  39. package/dist/types/hooks/useImageSlot.d.ts.map +1 -0
  40. package/dist/types/image/composeSemantic.d.ts +37 -0
  41. package/dist/types/image/composeSemantic.d.ts.map +1 -0
  42. package/dist/types/image/imageSlotTypes.d.ts +112 -0
  43. package/dist/types/image/imageSlotTypes.d.ts.map +1 -0
  44. package/dist/types/index.d.ts +7 -0
  45. package/dist/types/index.d.ts.map +1 -1
  46. package/dist/types/types/dependencies.d.ts +11 -0
  47. package/dist/types/types/dependencies.d.ts.map +1 -1
  48. package/package.json +2 -2
  49. package/src/components/ui/UserQuery.css +10 -7
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.useImageSlot = useImageSlot;
5
+ var _react = require("react");
6
+ var _ComponentDependenciesContext = require("../context/ComponentDependenciesContext");
7
+ var _ImageSlotContext = require("../context/ImageSlotContext");
8
+ var _composeSemantic = require("../image/composeSemantic");
9
+ /**
10
+ * `useImageSlot` — the whole section-facing surface for images.
11
+ *
12
+ * A section describes the hole in its layout and what the picture should be
13
+ * about. It does not name a request, a batch, or a set boundary; that omission
14
+ * is deliberate and load-bearing, because it is what lets the boundary widen
15
+ * from the section to the page later without touching a single component.
16
+ *
17
+ * Replaces the per-image `useResolvedImageSources` path, whose endpoint was
18
+ * removed and, once restored, answers with an empty list.
19
+ */
20
+
21
+ /** The `web5://image/` prefix an author's token carries. */
22
+ const TOKEN_PREFIX = 'web5://image/';
23
+ const decodeToken = raw => {
24
+ const body = raw.startsWith(TOKEN_PREFIX) ? raw.slice(TOKEN_PREFIX.length) : raw;
25
+ try {
26
+ return decodeURIComponent(body).trim();
27
+ } catch {
28
+ return body.trim();
29
+ }
30
+ };
31
+
32
+ /**
33
+ * Content-derived, never an index and never a React key. A re-render
34
+ * re-declares the same id so it does not re-request, and reordering items
35
+ * cannot remap pictures onto different cards — which an index-based id would
36
+ * do, visibly. Kept short and readable rather than cryptographic: it only has
37
+ * to be stable, and unique within one request.
38
+ */
39
+ const slotIdFor = (role, subjectKey, ratio, width) => {
40
+ let h = 0;
41
+ const material = `${role}|${subjectKey}|${ratio.toFixed(4)}|${width}`;
42
+ for (let i = 0; i < material.length; i++) {
43
+ h = Math.imul(31, h) + material.charCodeAt(i) | 0;
44
+ }
45
+ return `${role}-${(h >>> 0).toString(36)}`;
46
+ };
47
+
48
+ /** Module-level so `getSnapshot` returns a STABLE reference. Returning a fresh
49
+ * `{ status: 'unavailable' }` on every call makes `useSyncExternalStore`
50
+ * believe the store changed on every check, and it re-renders forever. */
51
+ const SETTLED_UNAVAILABLE = {
52
+ status: 'unavailable'
53
+ };
54
+ function useImageSlot(options) {
55
+ var _state$slot$visualMet;
56
+ const {
57
+ role = 'background',
58
+ ratio,
59
+ width = 800,
60
+ subject,
61
+ fallbackUrl,
62
+ enabled = true
63
+ } = options;
64
+
65
+ // Optional for the same reason the provider's is: a section rendered without
66
+ // dependencies configured should lose its picture, not throw.
67
+ const deps = (0, _ComponentDependenciesContext.useOptionalComponentDependencies)();
68
+ const collector = (0, _ImageSlotContext.useImageSlotCollector)();
69
+ const resolve = deps == null ? void 0 : deps.resolveImageSet;
70
+
71
+ /** The declaration, derived purely from props. */
72
+ const request = (0, _react.useMemo)(() => {
73
+ if (!enabled) {
74
+ return null;
75
+ }
76
+ if (subject.from === 'entity') {
77
+ var _subject$entityId;
78
+ const entityId = (_subject$entityId = subject.entityId) == null ? void 0 : _subject$entityId.trim();
79
+ if (!entityId) {
80
+ return null;
81
+ }
82
+ return {
83
+ id: slotIdFor(role, `e:${entityId}`, ratio, width),
84
+ kind: 'IMAGE_SLOT_KIND_ENTITY',
85
+ ratio,
86
+ renderWidthPx: width,
87
+ entityId
88
+ };
89
+ }
90
+ const semantic = subject.from === 'token' ? decodeToken(subject.token ?? '') : (0, _composeSemantic.composeSemantic)({
91
+ title: subject.title,
92
+ lead: subject.lead
93
+ });
94
+
95
+ // Too thin to retrieve on is not a query worth sending. `composeSemantic`
96
+ // returns empty rather than guessing, and a token can be empty too
97
+ // (`![alt](web5://image/)`).
98
+ if (!semantic) {
99
+ return null;
100
+ }
101
+ return {
102
+ id: slotIdFor(role, `s:${semantic}`, ratio, width),
103
+ kind: 'IMAGE_SLOT_KIND_EDITORIAL',
104
+ ratio,
105
+ renderWidthPx: width,
106
+ semantic
107
+ };
108
+ }, [enabled, role, ratio, width, subject]);
109
+
110
+ // Used only when no collector is mounted above — a host that has not adopted
111
+ // the section wrapper's provider. Same answer, no batching with siblings.
112
+ const [soloState, setSoloState] = (0, _react.useState)({
113
+ status: 'pending'
114
+ });
115
+ const soloSentRef = (0, _react.useRef)(null);
116
+ (0, _react.useEffect)(() => {
117
+ if (!request) {
118
+ return;
119
+ }
120
+ if (collector) {
121
+ collector.declare(request);
122
+ return;
123
+ }
124
+ if (soloSentRef.current === request.id) {
125
+ return;
126
+ }
127
+ soloSentRef.current = request.id;
128
+ if (!resolve) {
129
+ setSoloState({
130
+ status: 'unavailable'
131
+ });
132
+ return;
133
+ }
134
+ let alive = true;
135
+ resolve([request]).then(res => {
136
+ if (!alive) {
137
+ return;
138
+ }
139
+ const got = res.slots.find(s => s.slotId === request.id);
140
+ setSoloState(got && got.imageUrl ? {
141
+ status: 'resolved',
142
+ slot: got
143
+ } : {
144
+ status: 'unavailable'
145
+ });
146
+ }).catch(() => {
147
+ if (alive) {
148
+ setSoloState({
149
+ status: 'unavailable'
150
+ });
151
+ }
152
+ });
153
+ return () => {
154
+ alive = false;
155
+ };
156
+ }, [collector, resolve, request]);
157
+
158
+ // Subscribe to THIS slot only. `useSyncExternalStore` is the right shape
159
+ // here: the collector holds slot state outside React so resolving one slot
160
+ // does not re-render its siblings, and this is the supported way to read such
161
+ // a store without tearing. Memoised on the slot id so a re-render does not
162
+ // churn listeners.
163
+ const slotId = request == null ? void 0 : request.id;
164
+ const subscribe = (0, _react.useMemo)(() => onChange => collector && slotId ? collector.subscribe(slotId, onChange) : () => {}, [collector, slotId]);
165
+ const getSnapshot = (0, _react.useMemo)(() => () => collector && slotId ? collector.read(slotId) : SETTLED_UNAVAILABLE, [collector, slotId]);
166
+ const collectorState = (0, _react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
167
+ const state = !request ?
168
+ // Not asking (disabled, or nothing worth retrieving on) is settled, not
169
+ // pending — a layout must not wait for an answer that will never come.
170
+ SETTLED_UNAVAILABLE : collector ? collectorState : soloState;
171
+ const resolvedUrl = state.status === 'resolved' ? state.slot.imageUrl : null;
172
+ return {
173
+ state,
174
+ // Pending deliberately renders nothing rather than the fallback: swapping
175
+ // fallback -> resolved a moment later is a visible flicker, and the layouts
176
+ // that use this already have a ground colour for the pending case.
177
+ url: state.status === 'pending' ? null : resolvedUrl ?? fallbackUrl ?? null,
178
+ backgroundColor: state.status === 'resolved' ? ((_state$slot$visualMet = state.slot.visualMetadata) == null ? void 0 : _state$slot$visualMet.backgroundColor) || undefined : undefined,
179
+ isPending: state.status === 'pending'
180
+ };
181
+ }
182
+ //# sourceMappingURL=useImageSlot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_ComponentDependenciesContext","_ImageSlotContext","_composeSemantic","TOKEN_PREFIX","decodeToken","raw","body","startsWith","slice","length","decodeURIComponent","trim","slotIdFor","role","subjectKey","ratio","width","h","material","toFixed","i","Math","imul","charCodeAt","toString","SETTLED_UNAVAILABLE","status","useImageSlot","options","_state$slot$visualMet","subject","fallbackUrl","enabled","deps","useOptionalComponentDependencies","collector","useImageSlotCollector","resolve","resolveImageSet","request","useMemo","from","_subject$entityId","entityId","id","kind","renderWidthPx","semantic","token","composeSemantic","title","lead","soloState","setSoloState","useState","soloSentRef","useRef","useEffect","declare","current","alive","then","res","got","slots","find","s","slotId","imageUrl","slot","catch","subscribe","onChange","getSnapshot","read","collectorState","useSyncExternalStore","state","resolvedUrl","url","backgroundColor","visualMetadata","undefined","isPending"],"sources":["../../../src/hooks/useImageSlot.ts"],"sourcesContent":["/**\n * `useImageSlot` — the whole section-facing surface for images.\n *\n * A section describes the hole in its layout and what the picture should be\n * about. It does not name a request, a batch, or a set boundary; that omission\n * is deliberate and load-bearing, because it is what lets the boundary widen\n * from the section to the page later without touching a single component.\n *\n * Replaces the per-image `useResolvedImageSources` path, whose endpoint was\n * removed and, once restored, answers with an empty list.\n */\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { useOptionalComponentDependencies } from '../context/ComponentDependenciesContext';\nimport { useImageSlotCollector } from '../context/ImageSlotContext';\nimport { composeSemantic } from '../image/composeSemantic';\nimport type {\n ImageSlotRequest,\n ImageSubject,\n SlotState,\n} from '../image/imageSlotTypes';\n\nexport interface UseImageSlotOptions {\n /** What the picture is for. Only affects how a layout reads the result. */\n role?: 'background' | 'inline' | 'card';\n /** width / height of the hole — a constant of the layout, never measured. */\n ratio: number;\n /**\n * Delivered pixel width. Pass a value off the breakpoint ladder, never a\n * measurement: a bucket keeps the slot id stable across a resize, keeps two\n * sections at 798px and 802px one cacheable shape, and gives the CDN a small\n * set of urls it can cache across visitors.\n */\n width?: 400 | 800 | 1200 | 1600;\n subject: ImageSubject;\n /** Rendered instead when the resolver cannot fill the slot — typically an\n * entity's own payload image. */\n fallbackUrl?: string;\n /**\n * Declare nothing when false. For the case where the layout already HAS its\n * picture — a direct url in props, a story fixture — and resolving would be a\n * request whose answer is thrown away. Default true.\n */\n enabled?: boolean;\n}\n\nexport interface ResolvedImage {\n state: SlotState;\n /** What to actually render — the resolved url, else the caller's fallback,\n * else nothing. Saves every layout writing the same three-way check. */\n url: string | null;\n /** Paint the letterbox with this while an image is showing. */\n backgroundColor?: string;\n isPending: boolean;\n}\n\n/** The `web5://image/` prefix an author's token carries. */\nconst TOKEN_PREFIX = 'web5://image/';\n\nconst decodeToken = (raw: string): string => {\n const body = raw.startsWith(TOKEN_PREFIX)\n ? raw.slice(TOKEN_PREFIX.length)\n : raw;\n try {\n return decodeURIComponent(body).trim();\n } catch {\n return body.trim();\n }\n};\n\n/**\n * Content-derived, never an index and never a React key. A re-render\n * re-declares the same id so it does not re-request, and reordering items\n * cannot remap pictures onto different cards — which an index-based id would\n * do, visibly. Kept short and readable rather than cryptographic: it only has\n * to be stable, and unique within one request.\n */\nconst slotIdFor = (\n role: string,\n subjectKey: string,\n ratio: number,\n width: number,\n): string => {\n let h = 0;\n const material = `${role}|${subjectKey}|${ratio.toFixed(4)}|${width}`;\n for (let i = 0; i < material.length; i++) {\n h = (Math.imul(31, h) + material.charCodeAt(i)) | 0;\n }\n return `${role}-${(h >>> 0).toString(36)}`;\n};\n\n/** Module-level so `getSnapshot` returns a STABLE reference. Returning a fresh\n * `{ status: 'unavailable' }` on every call makes `useSyncExternalStore`\n * believe the store changed on every check, and it re-renders forever. */\nconst SETTLED_UNAVAILABLE: SlotState = { status: 'unavailable' };\n\nexport function useImageSlot(options: UseImageSlotOptions): ResolvedImage {\n const {\n role = 'background',\n ratio,\n width = 800,\n subject,\n fallbackUrl,\n enabled = true,\n } = options;\n\n // Optional for the same reason the provider's is: a section rendered without\n // dependencies configured should lose its picture, not throw.\n const deps = useOptionalComponentDependencies();\n const collector = useImageSlotCollector();\n const resolve = deps?.resolveImageSet;\n\n /** The declaration, derived purely from props. */\n const request = useMemo<ImageSlotRequest | null>(() => {\n if (!enabled) {\n return null;\n }\n\n if (subject.from === 'entity') {\n const entityId = subject.entityId?.trim();\n if (!entityId) {\n return null;\n }\n return {\n id: slotIdFor(role, `e:${entityId}`, ratio, width),\n kind: 'IMAGE_SLOT_KIND_ENTITY',\n ratio,\n renderWidthPx: width,\n entityId,\n };\n }\n\n const semantic =\n subject.from === 'token'\n ? decodeToken(subject.token ?? '')\n : composeSemantic({ title: subject.title, lead: subject.lead });\n\n // Too thin to retrieve on is not a query worth sending. `composeSemantic`\n // returns empty rather than guessing, and a token can be empty too\n // (`![alt](web5://image/)`).\n if (!semantic) {\n return null;\n }\n\n return {\n id: slotIdFor(role, `s:${semantic}`, ratio, width),\n kind: 'IMAGE_SLOT_KIND_EDITORIAL',\n ratio,\n renderWidthPx: width,\n semantic,\n };\n }, [enabled, role, ratio, width, subject]);\n\n // Used only when no collector is mounted above — a host that has not adopted\n // the section wrapper's provider. Same answer, no batching with siblings.\n const [soloState, setSoloState] = useState<SlotState>({ status: 'pending' });\n const soloSentRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (!request) {\n return;\n }\n if (collector) {\n collector.declare(request);\n return;\n }\n if (soloSentRef.current === request.id) {\n return;\n }\n soloSentRef.current = request.id;\n\n if (!resolve) {\n setSoloState({ status: 'unavailable' });\n return;\n }\n\n let alive = true;\n resolve([request])\n .then((res) => {\n if (!alive) {\n return;\n }\n const got = res.slots.find((s) => s.slotId === request.id);\n setSoloState(\n got && got.imageUrl\n ? { status: 'resolved', slot: got }\n : { status: 'unavailable' },\n );\n })\n .catch(() => {\n if (alive) {\n setSoloState({ status: 'unavailable' });\n }\n });\n\n return () => {\n alive = false;\n };\n }, [collector, resolve, request]);\n\n // Subscribe to THIS slot only. `useSyncExternalStore` is the right shape\n // here: the collector holds slot state outside React so resolving one slot\n // does not re-render its siblings, and this is the supported way to read such\n // a store without tearing. Memoised on the slot id so a re-render does not\n // churn listeners.\n const slotId = request?.id;\n const subscribe = useMemo(\n () => (onChange: () => void) =>\n collector && slotId ? collector.subscribe(slotId, onChange) : () => {},\n [collector, slotId],\n );\n const getSnapshot = useMemo(\n () => () =>\n collector && slotId ? collector.read(slotId) : SETTLED_UNAVAILABLE,\n [collector, slotId],\n );\n const collectorState = useSyncExternalStore(\n subscribe,\n getSnapshot,\n getSnapshot,\n );\n\n const state: SlotState = !request\n ? // Not asking (disabled, or nothing worth retrieving on) is settled, not\n // pending — a layout must not wait for an answer that will never come.\n SETTLED_UNAVAILABLE\n : collector\n ? collectorState\n : soloState;\n\n const resolvedUrl = state.status === 'resolved' ? state.slot.imageUrl : null;\n\n return {\n state,\n // Pending deliberately renders nothing rather than the fallback: swapping\n // fallback -> resolved a moment later is a visible flicker, and the layouts\n // that use this already have a ground colour for the pending case.\n url:\n state.status === 'pending' ? null : (resolvedUrl ?? fallbackUrl ?? null),\n backgroundColor:\n state.status === 'resolved'\n ? state.slot.visualMetadata?.backgroundColor || undefined\n : undefined,\n isPending: state.status === 'pending',\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.composeSemantic = composeSemantic;
5
+ /**
6
+ * Turn a section's own words into a retrieval phrase.
7
+ *
8
+ * The section hands over its RAW props — a title, a lead paragraph — not a
9
+ * query string. Composing is this module's job, in one place, because the two
10
+ * call sites that do it today invent their own phrase, do not agree with each
11
+ * other, and neither is tested:
12
+ *
13
+ * w5-client-circana/src/components/sections/GenericEntitySection.tsx:144
14
+ * `web5://image/${title}` — raw, so `**Nike** collections`
15
+ * is searched WITH its asterisks
16
+ * w5-client-circana/src/components/sections/ResearchSection.tsx:68
17
+ * `web5://image/${encodeURIComponent(title)}` — same idea, escaped
18
+ *
19
+ * Centralising it is most of what the text-derived case is worth.
20
+ *
21
+ * Lifted from the seed client package (ADR 0225): it started there because the
22
+ * CDN build externalises core, so a core change could not be seen through
23
+ * `?clientBundleUrl=` until it had been published. 0223 always assigned it
24
+ * here, and this is that move.
25
+ */
26
+
27
+ /** Longer than this and the tail stops helping retrieval and starts diluting it. */
28
+ const MAX_LENGTH = 120;
29
+
30
+ /** Below this there is no query worth sending — better to render no picture
31
+ * than to retrieve on a word like "More". */
32
+ const MIN_LENGTH = 3;
33
+ const stripMarkdown = raw => raw
34
+ // images before links: ![alt](src) would otherwise leave a stray `!`
35
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1').replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
36
+ // emphasis / strong / strike / inline code — keep the words, drop the marks
37
+ .replace(/(\*\*\*|\*\*|\*|___|__|_|~~|`)/g, '')
38
+ // leading heading hashes and blockquote marks
39
+ .replace(/^\s*#{1,6}\s*/gm, '').replace(/^\s*>\s?/gm, '')
40
+ // html tags an author or the parser may have left behind
41
+ .replace(/<[^>]+>/g, ' ');
42
+ const tidy = raw => stripMarkdown(raw).replace(/\s+/g, ' ').trim()
43
+ // trailing punctuation is noise in a retrieval phrase
44
+ .replace(/[\s.,;:!?—–-]+$/g, '').trim();
45
+
46
+ /**
47
+ * Clamp without cutting a word in half. Slicing at a fixed length leaves
48
+ * fragments like "the kinds of product" — observed in the first real run — and
49
+ * a dangling partial word is noise in a retrieval phrase, not a shorter
50
+ * version of it. Falls back to a hard slice only when the budget cannot fit
51
+ * even one word.
52
+ */
53
+ const clampToWord = (raw, max) => {
54
+ if (raw.length <= max) {
55
+ return raw;
56
+ }
57
+ // +1 so a boundary landing exactly on the budget still counts as a break.
58
+ const cut = raw.slice(0, max + 1);
59
+ const lastBreak = cut.lastIndexOf(' ');
60
+ const out = lastBreak > 0 ? cut.slice(0, lastBreak) : raw.slice(0, max);
61
+ return out.replace(/[\s.,;:!?—–-]+$/g, '').trim();
62
+ };
63
+
64
+ /** The first clause carries the subject; what follows is usually qualification. */
65
+ const firstClause = raw => {
66
+ const m = raw.match(/^[^.!?;]+/);
67
+ return (m ? m[0] : raw).trim();
68
+ };
69
+ /**
70
+ * Returns the phrase, or `''` when what survives is too thin to retrieve on —
71
+ * the caller then declares no slot at all rather than sending a bad query.
72
+ */
73
+ function composeSemantic({
74
+ title,
75
+ lead,
76
+ sectionSemantic
77
+ }) {
78
+ // A mission, when there is one, is the whole answer.
79
+ const mission = tidy(sectionSemantic ?? '');
80
+ if (mission.length >= MIN_LENGTH) {
81
+ return clampToWord(mission, MAX_LENGTH);
82
+ }
83
+ const head = tidy(title ?? '');
84
+ const body = firstClause(tidy(lead ?? ''));
85
+ const joined = [head, body].filter(Boolean).join('. ');
86
+ if (joined.length < MIN_LENGTH) return '';
87
+ if (joined.length <= MAX_LENGTH) return joined;
88
+
89
+ // Over budget: keep the title whole if it fits, since it is the stronger
90
+ // signal, and spend whatever is left on the lead.
91
+ if (head.length >= MAX_LENGTH) return clampToWord(head, MAX_LENGTH);
92
+ return clampToWord(joined, MAX_LENGTH);
93
+ }
94
+ //# sourceMappingURL=composeSemantic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MAX_LENGTH","MIN_LENGTH","stripMarkdown","raw","replace","tidy","trim","clampToWord","max","length","cut","slice","lastBreak","lastIndexOf","out","firstClause","m","match","composeSemantic","title","lead","sectionSemantic","mission","head","body","joined","filter","Boolean","join"],"sources":["../../../src/image/composeSemantic.ts"],"sourcesContent":["/**\n * Turn a section's own words into a retrieval phrase.\n *\n * The section hands over its RAW props — a title, a lead paragraph — not a\n * query string. Composing is this module's job, in one place, because the two\n * call sites that do it today invent their own phrase, do not agree with each\n * other, and neither is tested:\n *\n * w5-client-circana/src/components/sections/GenericEntitySection.tsx:144\n * `web5://image/${title}` — raw, so `**Nike** collections`\n * is searched WITH its asterisks\n * w5-client-circana/src/components/sections/ResearchSection.tsx:68\n * `web5://image/${encodeURIComponent(title)}` — same idea, escaped\n *\n * Centralising it is most of what the text-derived case is worth.\n *\n * Lifted from the seed client package (ADR 0225): it started there because the\n * CDN build externalises core, so a core change could not be seen through\n * `?clientBundleUrl=` until it had been published. 0223 always assigned it\n * here, and this is that move.\n */\n\n/** Longer than this and the tail stops helping retrieval and starts diluting it. */\nconst MAX_LENGTH = 120;\n\n/** Below this there is no query worth sending — better to render no picture\n * than to retrieve on a word like \"More\". */\nconst MIN_LENGTH = 3;\n\nconst stripMarkdown = (raw: string): string =>\n raw\n // images before links: ![alt](src) would otherwise leave a stray `!`\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n // emphasis / strong / strike / inline code — keep the words, drop the marks\n .replace(/(\\*\\*\\*|\\*\\*|\\*|___|__|_|~~|`)/g, '')\n // leading heading hashes and blockquote marks\n .replace(/^\\s*#{1,6}\\s*/gm, '')\n .replace(/^\\s*>\\s?/gm, '')\n // html tags an author or the parser may have left behind\n .replace(/<[^>]+>/g, ' ');\n\nconst tidy = (raw: string): string =>\n stripMarkdown(raw)\n .replace(/\\s+/g, ' ')\n .trim()\n // trailing punctuation is noise in a retrieval phrase\n .replace(/[\\s.,;:!?—–-]+$/g, '')\n .trim();\n\n/**\n * Clamp without cutting a word in half. Slicing at a fixed length leaves\n * fragments like \"the kinds of product\" — observed in the first real run — and\n * a dangling partial word is noise in a retrieval phrase, not a shorter\n * version of it. Falls back to a hard slice only when the budget cannot fit\n * even one word.\n */\nconst clampToWord = (raw: string, max: number): string => {\n if (raw.length <= max) {\n return raw;\n }\n // +1 so a boundary landing exactly on the budget still counts as a break.\n const cut = raw.slice(0, max + 1);\n const lastBreak = cut.lastIndexOf(' ');\n const out = lastBreak > 0 ? cut.slice(0, lastBreak) : raw.slice(0, max);\n return out.replace(/[\\s.,;:!?—–-]+$/g, '').trim();\n};\n\n/** The first clause carries the subject; what follows is usually qualification. */\nconst firstClause = (raw: string): string => {\n const m = raw.match(/^[^.!?;]+/);\n return (m ? m[0] : raw).trim();\n};\n\nexport interface ComposeSemanticInput {\n /** The section's headline. */\n title?: string;\n /** Its lead paragraph or body copy. */\n lead?: string;\n /** The descriptor's own `semantic` / mission, when the host supplies one.\n * Outranks the prose: it is what the orchestrator meant the section to be\n * about, which is a better subject than what it happened to say. */\n sectionSemantic?: string;\n}\n\n/**\n * Returns the phrase, or `''` when what survives is too thin to retrieve on —\n * the caller then declares no slot at all rather than sending a bad query.\n */\nexport function composeSemantic({\n title,\n lead,\n sectionSemantic,\n}: ComposeSemanticInput): string {\n // A mission, when there is one, is the whole answer.\n const mission = tidy(sectionSemantic ?? '');\n if (mission.length >= MIN_LENGTH) {\n return clampToWord(mission, MAX_LENGTH);\n }\n\n const head = tidy(title ?? '');\n const body = firstClause(tidy(lead ?? ''));\n\n const joined = [head, body].filter(Boolean).join('. ');\n if (joined.length < MIN_LENGTH) return '';\n\n if (joined.length <= MAX_LENGTH) return joined;\n\n // Over budget: keep the title whole if it fits, since it is the stronger\n // signal, and spend whatever is left on the lead.\n if (head.length >= MAX_LENGTH) return clampToWord(head, MAX_LENGTH);\n return clampToWord(joined, MAX_LENGTH);\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ //# sourceMappingURL=imageSlotTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["../../../src/image/imageSlotTypes.ts"],"sourcesContent":["/**\n * The image-slot vocabulary: what a section declares, and what it gets back.\n *\n * A section describes the hole in its layout and what the picture should be\n * about. It does not name a request, a batch, or a set boundary — that omission\n * is deliberate and load-bearing, because it is what lets the boundary widen\n * from the section to the page later without touching a single component.\n *\n * These mirror `wix.enterprise.web_five.v1.ImageService/ResolveImageSet`. The\n * wire is `preserving_proto_field_name` (`slot_id`, `image_url`,\n * `visual_metadata`, `background_color`) — confirmed against a live response,\n * not assumed — and normalising that is the transport's job, so nothing behind\n * the port ever sees snake_case.\n */\n\nexport type ImageSlotKind =\n | 'IMAGE_SLOT_KIND_ENTITY'\n | 'IMAGE_SLOT_KIND_EDITORIAL';\n\n/**\n * How well the resolver thinks it did. Read THIS rather than the presence of a\n * url: a `FALLBACK` is a real image the resolver is not proud of, so a card may\n * reasonably prefer its own payload picture over one, while a hero may prefer\n * no picture at all.\n */\nexport type ImageMatchQuality =\n | 'IMAGE_MATCH_QUALITY_EXACT'\n | 'IMAGE_MATCH_QUALITY_DEGRADED'\n | 'IMAGE_MATCH_QUALITY_FALLBACK';\n\nexport type ImageBackground =\n | 'IMAGE_BACKGROUND_TRANSPARENT'\n | 'IMAGE_BACKGROUND_SOLID'\n | 'IMAGE_BACKGROUND_MIXED';\n\n/** One hole in a layout, as sent. */\nexport interface ImageSlotRequest {\n /** Echoed back as `slotId` — the ONLY way to match a result to a slot. */\n id: string;\n kind: ImageSlotKind;\n /** width / height of the hole. Drives both the crop and the fit score. */\n ratio: number;\n /** Delivered pixel width. Server default is 800 when omitted. */\n renderWidthPx?: number;\n /** ENTITY slots only — scopes candidates to that entity's own pictures. */\n entityId?: string;\n /** EDITORIAL slots only — free text, semantically retrieved over labels. */\n semantic?: string;\n}\n\nexport interface ImagePalette {\n dominant: string;\n swatches: string[];\n}\n\n/** A square grid of per-cell values, `edge` on a side. */\nexport interface ImageStatGrid {\n edge: number;\n cells: number[];\n}\n\nexport interface ImageVisualMetadata {\n width: number;\n height: number;\n background?: ImageBackground;\n /** Paint the letterbox with this — a product shot on a seamless backdrop\n * needs that backdrop's colour, and only the response knows it. */\n backgroundColor: string;\n palette?: ImagePalette;\n /** Per-cell luma. Enough to compute a scrim that belongs to the photograph\n * rather than washing every hero with one blanket alpha. */\n luma?: ImageStatGrid;\n labels: string[];\n}\n\nexport interface ResolvedImageSlot {\n slotId: string;\n /** Already cropped and resized to the slot — there is no client-side\n * windowing to do. Null when the resolver could not fill the slot. */\n imageUrl: string | null;\n match?: ImageMatchQuality;\n score?: number;\n visualMetadata?: ImageVisualMetadata;\n}\n\nexport interface ResolveImageSetResponse {\n mode?: ImageBackground;\n slots: ResolvedImageSlot[];\n}\n\n/**\n * The port a host fills. Declared here, provided by `web50-server-ui` through\n * `AppDependenciesProvider` — the same split `resolveContextualImage*` already\n * uses, where core owns the hook and the host owns the adapter.\n */\nexport type ResolveImageSetPort = (\n images: ImageSlotRequest[],\n) => Promise<ResolveImageSetResponse>;\n\n/** What a declaration gets back. `pending` is the first paint, always. */\nexport type SlotState =\n | { status: 'pending' }\n | { status: 'resolved'; slot: ResolvedImageSlot }\n | { status: 'unavailable' };\n\n/**\n * Where a slot's subject comes from. Evaluated per slot, first hit wins:\n * an explicit token outranks an entity ref, which outranks the section's own\n * words. The precedence is what keeps the three cases free of each other — a\n * section written for one becomes another the day an author writes a token,\n * with no branch in section code.\n */\nexport type ImageSubject =\n /** A `web5://image/...` token an author wrote, or a bare phrase. */\n | { from: 'token'; token: string }\n /** A catalog entity — the resolver scopes candidates to its own pictures. */\n | { from: 'entity'; entityId: string }\n /** The section's own words. Hand over raw props; composing is core's job. */\n | { from: 'text'; title?: string; lead?: string };\n"],"mappings":"","ignoreList":[]}
package/dist/cjs/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- 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 = exports.UNKNOWN_CONSENT = 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.SectionSkeleton = exports.SearchSectionDefinition = exports.SearchSection = exports.STREAMING_TIMEOUT_MS = exports.REFRESH_PROMPTS_WINDOW_MS = exports.REFRESH_PROMPTS_UNTIL_KEY = exports.PromptEntryEmptyState = exports.PlacementSmoothHeight = exports.PlacementResponseRenderer = exports.PlacementPayloadProvider = exports.PlacementLoader = exports.PRODUCT_BY_HANDLE_QUERY = exports.PRODUCT_BACK_SESSION_KEY = 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.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.Disclaimer = exports.DiagnosticsCollector = exports.DROP_SECTION = exports.DIAGNOSTIC_TYPES = exports.DEFAULT_ERROR_TEMPLATES = exports.DEFAULT_BACKEND_ENVIRONMENT = 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.ARTICLE_BY_HANDLE_QUERY = void 0;
5
- 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 = 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.cn = exports.clearForwardStack = exports.buildProbeUrl = exports.buildPlacementDependencies = exports.buildImageSearchFilter = exports.bucketOf = exports.backgroundFilter = exports.applyThemeOverrides = exports.analyzeBackdrop = void 0;
6
- 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.useFeatureToggles = exports.useFeatureToggle = exports.useEntityTransforms = exports.useDebugImageContext = 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.resolveClientBundleUrl = 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 = void 0;
4
+ 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 = exports.UNKNOWN_CONSENT = 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.SectionSkeleton = exports.SearchSectionDefinition = exports.SearchSection = exports.STREAMING_TIMEOUT_MS = exports.REFRESH_PROMPTS_WINDOW_MS = exports.REFRESH_PROMPTS_UNTIL_KEY = exports.PromptEntryEmptyState = exports.PlacementSmoothHeight = exports.PlacementResponseRenderer = exports.PlacementPayloadProvider = exports.PlacementLoader = exports.PRODUCT_BY_HANDLE_QUERY = exports.PRODUCT_BACK_SESSION_KEY = 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.Disclaimer = exports.DiagnosticsCollector = exports.DROP_SECTION = exports.DIAGNOSTIC_TYPES = exports.DEFAULT_ERROR_TEMPLATES = exports.DEFAULT_BACKEND_ENVIRONMENT = 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.ARTICLE_BY_HANDLE_QUERY = void 0;
5
+ exports.isThemeDebugEnabled = exports.isTemplatePickerRequested = exports.isSimulationTraffic = exports.isShopifyHost = exports.isProductFamilyEntityType = exports.isOneTrustHost = exports.isNavigableUrl = exports.isMatchDebugEnabled = exports.isLegacyUrl = exports.isHtmlComment = exports.isHslTriplet = exports.isEntityLink = 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.applyThemeOverrides = exports.analyzeBackdrop = exports.addToCart = void 0;
6
+ 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.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.resolveClientBundleUrl = 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 = void 0;
7
+ exports.writeProductBackHandoff = exports.validatePatternWithBlocks = void 0;
7
8
  var _clients = require("./clients");
8
9
  exports.CLIENT_IDS = _clients.CLIENT_IDS;
9
10
  exports.EXPERIMENT_IDS = _clients.EXPERIMENT_IDS;
@@ -132,6 +133,13 @@ var _useDebugImageContext = require("./hooks/useDebugImageContext");
132
133
  exports.useDebugImageContext = _useDebugImageContext.useDebugImageContext;
133
134
  var _useResolvedImageSources = require("./hooks/useResolvedImageSources");
134
135
  exports.useResolvedImageSources = _useResolvedImageSources.useResolvedImageSources;
136
+ var _useImageSlot = require("./hooks/useImageSlot");
137
+ exports.useImageSlot = _useImageSlot.useImageSlot;
138
+ var _ImageSlotContext = require("./context/ImageSlotContext");
139
+ exports.ImageSlotProvider = _ImageSlotContext.ImageSlotProvider;
140
+ exports.useImageSlotCollector = _ImageSlotContext.useImageSlotCollector;
141
+ var _composeSemantic = require("./image/composeSemantic");
142
+ exports.composeSemantic = _composeSemantic.composeSemantic;
135
143
  var _useResolveGenericEntityData = require("./hooks/useResolveGenericEntityData");
136
144
  exports.useResolveGenericEntityData = _useResolveGenericEntityData.useResolveGenericEntityData;
137
145
  var _useEntityTransforms = require("./hooks/useEntityTransforms");