@wix/web5-core 1.63.10 → 1.63.12

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 (43) hide show
  1. package/dist/cjs/component/componentDefinitions/CalloutSectionDefinition.js +13 -8
  2. package/dist/cjs/component/componentDefinitions/CalloutSectionDefinition.js.map +1 -1
  3. package/dist/cjs/entity/defaultExtractor.js +4 -0
  4. package/dist/cjs/entity/defaultExtractor.js.map +1 -1
  5. package/dist/cjs/entity/entityPayload.js +93 -0
  6. package/dist/cjs/entity/entityPayload.js.map +1 -0
  7. package/dist/cjs/entity/index.js +5 -2
  8. package/dist/cjs/entity/index.js.map +1 -1
  9. package/dist/cjs/entity/listEntityItems.js +8 -37
  10. package/dist/cjs/entity/listEntityItems.js.map +1 -1
  11. package/dist/cjs/index.js +4 -2
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/types/callout.js +11 -1
  14. package/dist/cjs/types/callout.js.map +1 -1
  15. package/dist/cjs/types/entity.js.map +1 -1
  16. package/dist/esm/component/componentDefinitions/CalloutSectionDefinition.js +13 -8
  17. package/dist/esm/component/componentDefinitions/CalloutSectionDefinition.js.map +1 -1
  18. package/dist/esm/entity/defaultExtractor.js +4 -0
  19. package/dist/esm/entity/defaultExtractor.js.map +1 -1
  20. package/dist/esm/entity/entityPayload.js +88 -0
  21. package/dist/esm/entity/entityPayload.js.map +1 -0
  22. package/dist/esm/entity/index.js +1 -0
  23. package/dist/esm/entity/index.js.map +1 -1
  24. package/dist/esm/entity/listEntityItems.js +9 -37
  25. package/dist/esm/entity/listEntityItems.js.map +1 -1
  26. package/dist/esm/index.js +1 -1
  27. package/dist/esm/index.js.map +1 -1
  28. package/dist/esm/types/callout.js +11 -1
  29. package/dist/esm/types/callout.js.map +1 -1
  30. package/dist/esm/types/entity.js.map +1 -1
  31. package/dist/types/entity/defaultExtractor.d.ts.map +1 -1
  32. package/dist/types/entity/entityPayload.d.ts +32 -0
  33. package/dist/types/entity/entityPayload.d.ts.map +1 -0
  34. package/dist/types/entity/index.d.ts +1 -0
  35. package/dist/types/entity/index.d.ts.map +1 -1
  36. package/dist/types/entity/listEntityItems.d.ts.map +1 -1
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/types/callout.d.ts +11 -1
  40. package/dist/types/types/callout.d.ts.map +1 -1
  41. package/dist/types/types/entity.d.ts +14 -0
  42. package/dist/types/types/entity.d.ts.map +1 -1
  43. package/package.json +2 -2
@@ -6,7 +6,6 @@ exports.CalloutSectionDefinition = void 0;
6
6
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
7
7
  var _parseUtils = require("./parse-utils");
8
8
  var _parts = require("../../parts/parts");
9
- var _callout = require("../../types/callout");
10
9
  var _sectionDefinition = require("../section-definition");
11
10
  /**
12
11
  * Callout — the page's dialog layer, written by the orchestrator itself rather
@@ -148,16 +147,22 @@ class CalloutSectionDefinition {
148
147
  /**
149
148
  * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the
150
149
  * same vocabulary the legacy `[!TIP]` marker produced, which is what any
151
- * kind-aware renderer already switches on. An unrecognised value carries no
152
- * kind rather than an invented one.
150
+ * kind-aware renderer already switches on.
151
+ *
152
+ * Deliberately NOT validated against a list. The prompts own this vocabulary,
153
+ * and they live in the prompt catalog precisely so it can change without
154
+ * shipping code — an allowlist here would mean every new semantic needs a
155
+ * release before it resolves, which is backwards. Nothing gates on `kind`
156
+ * either: it is metadata for a renderer that wants to vary by it, and the one
157
+ * renderer that does falls back when it sees a value it doesn't know.
158
+ *
159
+ * `CALLOUT_SEMANTICS` therefore documents what the prompts emit today; it is
160
+ * not a filter.
153
161
  */
154
162
  exports.CalloutSectionDefinition = CalloutSectionDefinition;
155
163
  function semanticToKind(semantic) {
156
- const value = semantic == null ? void 0 : semantic.trim().toLowerCase();
157
- if (!value) {
158
- return undefined;
159
- }
160
- return _callout.CALLOUT_SEMANTICS.includes(value) ? {
164
+ const value = semantic == null ? void 0 : semantic.trim();
165
+ return value ? {
161
166
  kind: value.toUpperCase()
162
167
  } : undefined;
163
168
  }
@@ -1 +1 @@
1
- {"version":3,"names":["_parseUtils","require","_parts","_callout","_sectionDefinition","CalloutSectionDefinition","constructor","_defineProperty2","default","callouts","text","markdown","expected","kind","parse","parts","calloutParts","getPartsByType","length","parseBlockquote","parseProse","_parts$find","descriptor","find","p","type","meta","section","inlinePartsToMarkdown","filter","trim","DROP_SECTION","semanticToKind","semantic","relatedLinks","seenUrls","Set","c","_c$meta","_c$content","content","parsed","parseCalloutTag","push","link","getLinks","_link$meta","_link$content","href","has","add","url","undefined","exports","value","toLowerCase","CALLOUT_SEMANTICS","includes","toUpperCase"],"sources":["../../../../src/component/componentDefinitions/CalloutSectionDefinition.ts"],"sourcesContent":["import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils';\nimport { getPartsByType, getLinks, type Part } from '../../parts/parts';\nimport { CALLOUT_SEMANTICS, type Callout } from '../../types/callout';\nimport type {\n CalloutCompProps,\n CalloutSectionComponent,\n SectionFixture,\n} from '../types';\nimport {\n DROP_SECTION,\n type DropSection,\n type SectionDefinition,\n} from '../section-definition';\n\n/**\n * Callout — the page's dialog layer, written by the orchestrator itself rather\n * than by a per-section model.\n *\n * Two markdown forms reach this definition, and both must keep working:\n *\n * 1. **Descriptor + plain prose** (current). `semantic` carries the kind:\n *\n * <!-- section:callout, semantic:tip -->\n * Prefer a gentle daily ritual? The 4 oz size is the one to sample with.\n *\n * 2. **GFM alert blockquote** (legacy). Still emitted by sites pinned to an\n * older prompt, and present in every historical page, so it is parsed\n * verbatim rather than migrated:\n *\n * <!-- section:callout -->\n * > [!TIP] Prefer a gentle daily ritual? …\n *\n * The two forms differ in one way that matters. A blockquote collapses to a\n * single `callout` part carrying the whole line in `meta.markdown`, so inline\n * ask links survive for free and `getLinks` finds nothing to harvest. A\n * paragraph is *unwrapped* into its inline children (`markdownBlocks.ts`), so\n * prose with a link arrives as `t + link + t` — separate parts. Reading only\n * the `t` parts would delete the link's url and label (DL #134's list bug) and\n * `getLinks` would then re-surface it as a footer link, showing the visitor the\n * same link twice. `inlinePartsToMarkdown` is what keeps the link inline, and\n * the prose path deliberately harvests no `relatedLinks` at all.\n */\nexport class CalloutSectionDefinition\n implements SectionDefinition<CalloutSectionComponent>\n{\n sectionType = 'callout' as const;\n patterns = [\n // Descriptor-led prose. `link` must be in the alternation or a callout\n // carrying an ask link fails to match and falls through to the fallback.\n 'html_comment[section=\"callout\"] > (t | link)+',\n // Legacy blockquote.\n 'html_comment? > callout+',\n ];\n\n defaults: CalloutCompProps = {\n callouts: [{ text: 'This is a callout.' }],\n };\n\n fixtures: SectionFixture<CalloutCompProps>[] = [\n {\n markdown:\n '<!-- section:callout, semantic:tip -->\\nThis is a callout paragraph.',\n expected: {\n callouts: [{ text: 'This is a callout paragraph.', kind: 'TIP' }],\n },\n },\n {\n markdown: '> This is a callout blockquote.',\n expected: {\n callouts: [{ text: 'This is a callout blockquote.' }],\n },\n },\n ];\n\n parse(parts: Part[]): CalloutCompProps | null | DropSection {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n\n return calloutParts.length > 0\n ? this.parseBlockquote(calloutParts, parts)\n : this.parseProse(parts);\n }\n\n /**\n * Descriptor + plain prose. The kind comes from the descriptor's `semantic`,\n * and every link in the body is inline prose — never a `relatedLinks` footer.\n */\n private parseProse(parts: Part[]): CalloutCompProps | null | DropSection {\n const descriptor = parts.find((p) => p.type === 'htmlComment' && p.meta)\n ?.meta as Record<string, string> | undefined;\n\n // The pattern already gates on this; re-checking keeps a direct\n // `parse()` call (and any future pattern edit) from mislabelling a\n // different section's prose as a callout.\n if (descriptor?.section !== 'callout') {\n return null;\n }\n\n const text = inlinePartsToMarkdown(\n parts.filter((p) => p.type !== 'htmlComment'),\n ).trim();\n\n // Matched, but there is nothing to say. DROP_SECTION consumes the nodes\n // and renders nothing; returning null would hand the descriptor to the\n // fallback, which renders it as stray text.\n if (!text) {\n return DROP_SECTION;\n }\n\n return {\n callouts: [{ text, ...(semanticToKind(descriptor.semantic) ?? {}) }],\n };\n }\n\n /** Legacy GFM alert blockquote. */\n private parseBlockquote(\n calloutParts: Part[],\n parts: Part[],\n ): CalloutCompProps | null | DropSection {\n const callouts: Callout[] = [];\n const relatedLinks: { text: string; url: string }[] = [];\n const seenUrls = new Set<string>();\n\n for (const c of calloutParts) {\n const text = (c.meta?.markdown as string)?.trim() || c.content?.trim();\n if (text) {\n const parsed = parseCalloutTag(text);\n if (parsed.text) {\n callouts.push({ text: parsed.text, kind: parsed.kind });\n }\n }\n }\n\n if (callouts.length === 0) {\n return DROP_SECTION;\n }\n\n // A blockquote yields no sibling link parts, so anything found here arrived\n // alongside the quote and is a genuine footer link.\n for (const link of getLinks(parts)) {\n const href = link.meta?.href;\n const text = link.content?.trim();\n if (text && href && !seenUrls.has(href)) {\n seenUrls.add(href);\n relatedLinks.push({ text, url: href });\n }\n }\n\n return {\n callouts,\n relatedLinks: relatedLinks.length > 0 ? relatedLinks : undefined,\n };\n }\n}\n\n/**\n * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the\n * same vocabulary the legacy `[!TIP]` marker produced, which is what any\n * kind-aware renderer already switches on. An unrecognised value carries no\n * kind rather than an invented one.\n */\nfunction semanticToKind(semantic?: string): { kind: string } | undefined {\n const value = semantic?.trim().toLowerCase();\n if (!value) {\n return undefined;\n }\n return (CALLOUT_SEMANTICS as readonly string[]).includes(value)\n ? { kind: value.toUpperCase() }\n : undefined;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_parseUtils","require","_parts","_sectionDefinition","CalloutSectionDefinition","constructor","_defineProperty2","default","callouts","text","markdown","expected","kind","parse","parts","calloutParts","getPartsByType","length","parseBlockquote","parseProse","_parts$find","descriptor","find","p","type","meta","section","inlinePartsToMarkdown","filter","trim","DROP_SECTION","semanticToKind","semantic","relatedLinks","seenUrls","Set","c","_c$meta","_c$content","content","parsed","parseCalloutTag","push","link","getLinks","_link$meta","_link$content","href","has","add","url","undefined","exports","value","toUpperCase"],"sources":["../../../../src/component/componentDefinitions/CalloutSectionDefinition.ts"],"sourcesContent":["import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils';\nimport { getPartsByType, getLinks, type Part } from '../../parts/parts';\nimport type { Callout } from '../../types/callout';\nimport type {\n CalloutCompProps,\n CalloutSectionComponent,\n SectionFixture,\n} from '../types';\nimport {\n DROP_SECTION,\n type DropSection,\n type SectionDefinition,\n} from '../section-definition';\n\n/**\n * Callout — the page's dialog layer, written by the orchestrator itself rather\n * than by a per-section model.\n *\n * Two markdown forms reach this definition, and both must keep working:\n *\n * 1. **Descriptor + plain prose** (current). `semantic` carries the kind:\n *\n * <!-- section:callout, semantic:tip -->\n * Prefer a gentle daily ritual? The 4 oz size is the one to sample with.\n *\n * 2. **GFM alert blockquote** (legacy). Still emitted by sites pinned to an\n * older prompt, and present in every historical page, so it is parsed\n * verbatim rather than migrated:\n *\n * <!-- section:callout -->\n * > [!TIP] Prefer a gentle daily ritual? …\n *\n * The two forms differ in one way that matters. A blockquote collapses to a\n * single `callout` part carrying the whole line in `meta.markdown`, so inline\n * ask links survive for free and `getLinks` finds nothing to harvest. A\n * paragraph is *unwrapped* into its inline children (`markdownBlocks.ts`), so\n * prose with a link arrives as `t + link + t` — separate parts. Reading only\n * the `t` parts would delete the link's url and label (DL #134's list bug) and\n * `getLinks` would then re-surface it as a footer link, showing the visitor the\n * same link twice. `inlinePartsToMarkdown` is what keeps the link inline, and\n * the prose path deliberately harvests no `relatedLinks` at all.\n */\nexport class CalloutSectionDefinition\n implements SectionDefinition<CalloutSectionComponent>\n{\n sectionType = 'callout' as const;\n patterns = [\n // Descriptor-led prose. `link` must be in the alternation or a callout\n // carrying an ask link fails to match and falls through to the fallback.\n 'html_comment[section=\"callout\"] > (t | link)+',\n // Legacy blockquote.\n 'html_comment? > callout+',\n ];\n\n defaults: CalloutCompProps = {\n callouts: [{ text: 'This is a callout.' }],\n };\n\n fixtures: SectionFixture<CalloutCompProps>[] = [\n {\n markdown:\n '<!-- section:callout, semantic:tip -->\\nThis is a callout paragraph.',\n expected: {\n callouts: [{ text: 'This is a callout paragraph.', kind: 'TIP' }],\n },\n },\n {\n markdown: '> This is a callout blockquote.',\n expected: {\n callouts: [{ text: 'This is a callout blockquote.' }],\n },\n },\n ];\n\n parse(parts: Part[]): CalloutCompProps | null | DropSection {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n\n return calloutParts.length > 0\n ? this.parseBlockquote(calloutParts, parts)\n : this.parseProse(parts);\n }\n\n /**\n * Descriptor + plain prose. The kind comes from the descriptor's `semantic`,\n * and every link in the body is inline prose — never a `relatedLinks` footer.\n */\n private parseProse(parts: Part[]): CalloutCompProps | null | DropSection {\n const descriptor = parts.find((p) => p.type === 'htmlComment' && p.meta)\n ?.meta as Record<string, string> | undefined;\n\n // The pattern already gates on this; re-checking keeps a direct\n // `parse()` call (and any future pattern edit) from mislabelling a\n // different section's prose as a callout.\n if (descriptor?.section !== 'callout') {\n return null;\n }\n\n const text = inlinePartsToMarkdown(\n parts.filter((p) => p.type !== 'htmlComment'),\n ).trim();\n\n // Matched, but there is nothing to say. DROP_SECTION consumes the nodes\n // and renders nothing; returning null would hand the descriptor to the\n // fallback, which renders it as stray text.\n if (!text) {\n return DROP_SECTION;\n }\n\n return {\n callouts: [{ text, ...(semanticToKind(descriptor.semantic) ?? {}) }],\n };\n }\n\n /** Legacy GFM alert blockquote. */\n private parseBlockquote(\n calloutParts: Part[],\n parts: Part[],\n ): CalloutCompProps | null | DropSection {\n const callouts: Callout[] = [];\n const relatedLinks: { text: string; url: string }[] = [];\n const seenUrls = new Set<string>();\n\n for (const c of calloutParts) {\n const text = (c.meta?.markdown as string)?.trim() || c.content?.trim();\n if (text) {\n const parsed = parseCalloutTag(text);\n if (parsed.text) {\n callouts.push({ text: parsed.text, kind: parsed.kind });\n }\n }\n }\n\n if (callouts.length === 0) {\n return DROP_SECTION;\n }\n\n // A blockquote yields no sibling link parts, so anything found here arrived\n // alongside the quote and is a genuine footer link.\n for (const link of getLinks(parts)) {\n const href = link.meta?.href;\n const text = link.content?.trim();\n if (text && href && !seenUrls.has(href)) {\n seenUrls.add(href);\n relatedLinks.push({ text, url: href });\n }\n }\n\n return {\n callouts,\n relatedLinks: relatedLinks.length > 0 ? relatedLinks : undefined,\n };\n }\n}\n\n/**\n * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the\n * same vocabulary the legacy `[!TIP]` marker produced, which is what any\n * kind-aware renderer already switches on.\n *\n * Deliberately NOT validated against a list. The prompts own this vocabulary,\n * and they live in the prompt catalog precisely so it can change without\n * shipping code — an allowlist here would mean every new semantic needs a\n * release before it resolves, which is backwards. Nothing gates on `kind`\n * either: it is metadata for a renderer that wants to vary by it, and the one\n * renderer that does falls back when it sees a value it doesn't know.\n *\n * `CALLOUT_SEMANTICS` therefore documents what the prompts emit today; it is\n * not a filter.\n */\nfunction semanticToKind(semantic?: string): { kind: string } | undefined {\n const value = semantic?.trim();\n return value ? { kind: value.toUpperCase() } : undefined;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -112,6 +112,10 @@ function transformToGenericEntityData(payloadItem, config) {
112
112
  excerpt: payloadItem.excerpt,
113
113
  imageUrl,
114
114
  matchedOptions: (0, _matchedVariant.toMatchedOptions)(payloadItem.options),
115
+ // Which child the row is wearing. Carried through so the live enricher
116
+ // prices THAT variant (`mergeShopifyEntityData` keeps the turn's value)
117
+ // rather than falling back to the product's default.
118
+ variantId: payloadItem.variantId,
115
119
  price: (0, _matchedVariant.formatPriceField)(payloadItem.price, currency),
116
120
  priceMin: (0, _matchedVariant.formatPriceField)(payloadItem.priceMin, currency),
117
121
  priceMax: (0, _matchedVariant.formatPriceField)(payloadItem.priceMax, currency),
@@ -1 +1 @@
1
- {"version":3,"names":["_linkTypes","require","_entityLinkParser","_matchedVariant","extractEntityIdFromUrl","entityUrl","trimmedUrl","trim","isWeb5EntityUrl","parsed","parseWeb5Url","type","Web5UrlType","ENTITY","entityType","entityId","matchesUrl","payloadItem","config","id","urlValue","urlMatchProperty","pattern","urlMatchPattern","endsWith","includes","findEntityInPayload","payload","entityConfig","typeConfig","entityTypes","matchedItem","find","item","transformToSolutionEntityData","transformToGenericEntityData","formatEntityDate","timestamp","_parts$find","_parts$find2","_parts$find3","date","Date","isNaN","getTime","undefined","formatter","Intl","DateTimeFormat","day","month","timeZone","year","parts","formatToParts","part","value","transformToBlogPostEntityData","_payloadItem$labels","_payloadItem$tags","imageUrl","publishTimestamp","publishDate","published_at","img","imgAlreadyAbsolute","startsWith","imageBaseUrl","currency","readCurrencyCode","priceCurrency","url","title","description","excerpt","matchedOptions","toMatchedOptions","options","price","formatPriceField","priceMin","priceMax","update_ts","author","authorName","cmsItemId","cms_item_id","usage","tags","labels","length","defaultExtractor","exports"],"sources":["../../../src/entity/defaultExtractor.ts"],"sourcesContent":["import type { SolutionEntityData, BlogEntityData, EntityItemData } from '../component/types';\nimport type {\n ApiPayloadItem,\n EntityConfig,\n EntityTypeConfig,\n} from '../types/entity';\nimport type { EntityExtractor } from './types';\nimport { Web5UrlType, isWeb5EntityUrl } from '../types/link-types';\nimport { parseWeb5Url } from '../utils/entityLinkParser';\nimport {\n formatPriceField,\n readCurrencyCode,\n toMatchedOptions,\n} from './matchedVariant';\n\n/**\n * Extract entity type and ID from a web5://entity/ URL.\n * Delegates to the central `parseWeb5Url` parser.\n */\nfunction extractEntityIdFromUrl(\n entityUrl: string,\n): { entityType: string; entityId: string } | null {\n const trimmedUrl = entityUrl?.trim();\n if (!trimmedUrl || !isWeb5EntityUrl(trimmedUrl)) {\n return null;\n }\n const parsed = parseWeb5Url(trimmedUrl);\n if (parsed?.type !== Web5UrlType.ENTITY || !parsed.entityType || !parsed.entityId) {\n return null;\n }\n return { entityType: parsed.entityType, entityId: parsed.entityId };\n}\n\nfunction matchesUrl(\n payloadItem: ApiPayloadItem,\n entityId: string,\n config: EntityTypeConfig,\n): boolean {\n // Entities resolve by `entityId` (the `<id>` in `web5://entity/<type>/<id>`).\n // Accept a direct `id` match, then fall back to the configured url strategy.\n if (payloadItem.id === entityId) {\n return true;\n }\n\n const urlValue = payloadItem[config.urlMatchProperty as keyof ApiPayloadItem];\n\n if (typeof urlValue !== 'string') {\n return false;\n }\n\n const pattern = config.urlMatchPattern || 'endsWith';\n\n switch (pattern) {\n case 'endsWith':\n return urlValue.endsWith(`/${entityId}`);\n case 'contains':\n return urlValue.includes(entityId);\n case 'equals':\n return urlValue === entityId;\n default:\n return urlValue.endsWith(`/${entityId}`);\n }\n}\n\nfunction findEntityInPayload(\n entityType: string,\n entityId: string,\n payload: ApiPayloadItem[],\n entityConfig: EntityConfig,\n): ApiPayloadItem | null {\n const typeConfig = entityConfig.entityTypes[entityType];\n\n if (!typeConfig) {\n return null;\n }\n\n // Resolution keys off `entityId` (not the type): find the payload item whose\n // id/url matches, using the type's configured url strategy.\n const matchedItem = payload.find((item) =>\n matchesUrl(item, entityId, typeConfig),\n );\n\n return matchedItem || null;\n}\n\n/** @deprecated Use transformToGenericEntityData */\nexport function transformToSolutionEntityData(\n payloadItem: ApiPayloadItem,\n config: EntityTypeConfig,\n): SolutionEntityData {\n return transformToGenericEntityData(payloadItem, config);\n}\n\nfunction formatEntityDate(timestamp: string): string | undefined {\n const date = new Date(timestamp);\n if (isNaN(date.getTime())) {\n return undefined;\n }\n const formatter = new Intl.DateTimeFormat('en-US', {\n day: 'numeric',\n month: 'short',\n timeZone: 'Europe/Kiev',\n year: 'numeric',\n });\n const parts = formatter.formatToParts(date);\n const day = parts.find((part) => part.type === 'day')?.value;\n const month = parts.find((part) => part.type === 'month')?.value;\n const year = parts.find((part) => part.type === 'year')?.value;\n\n if (!day || !month || !year) {\n return undefined;\n }\n\n return `${day} ${month} ${year}`;\n}\n\n/** @deprecated Use transformToGenericEntityData */\nexport function transformToBlogPostEntityData(\n payloadItem: ApiPayloadItem,\n config: EntityTypeConfig,\n): BlogEntityData {\n return transformToGenericEntityData(payloadItem, config);\n}\n\nexport function transformToGenericEntityData(\n payloadItem: ApiPayloadItem,\n config: EntityTypeConfig,\n): EntityItemData {\n let imageUrl: string | undefined;\n const publishTimestamp = payloadItem.publishDate || payloadItem.published_at;\n\n if (payloadItem.img) {\n const imgAlreadyAbsolute = payloadItem.img.startsWith('http');\n imageUrl = config.imageBaseUrl && !imgAlreadyAbsolute\n ? `${config.imageBaseUrl}${payloadItem.img}`\n : payloadItem.img;\n }\n\n // A document that retrieval narrowed to one child arrives with that child's\n // presentation fields already overlaid (`url`, `img`, `price`) plus the pairs\n // that identify it and the span its parent covers. The overlay is done\n // server-side; all that is left here is to shape those two for display.\n const currency = readCurrencyCode(\n payloadItem.currency,\n payloadItem.priceCurrency,\n );\n\n return {\n url: payloadItem.url,\n title: payloadItem.title,\n type: payloadItem.type,\n description: payloadItem.description,\n excerpt: payloadItem.excerpt,\n imageUrl,\n matchedOptions: toMatchedOptions(payloadItem.options),\n price: formatPriceField(payloadItem.price, currency),\n priceMin: formatPriceField(payloadItem.priceMin, currency),\n priceMax: formatPriceField(payloadItem.priceMax, currency),\n date: payloadItem.update_ts\n ? formatEntityDate(payloadItem.update_ts)\n : undefined,\n publishDate: publishTimestamp\n ? formatEntityDate(publishTimestamp)\n : undefined,\n author: payloadItem.author || payloadItem.authorName,\n cmsItemId: payloadItem.cms_item_id,\n usage: payloadItem.usage,\n tags: payloadItem.labels?.length\n ? payloadItem.labels\n : payloadItem.tags?.length\n ? payloadItem.tags\n : undefined,\n };\n}\n\n/**\n * Default entity extractor - config-driven extraction for any client.\n * Uses entityConfig from client JSON to resolve web5://entity/ URLs against API payload.\n */\nexport const defaultExtractor: EntityExtractor = {\n extractEntityIdFromUrl,\n findEntityInPayload,\n};\n"],"mappings":";;;;;;;AAOA,IAAAA,UAAA,GAAAC,OAAA;AACA,IAAAC,iBAAA,GAAAD,OAAA;AACA,IAAAE,eAAA,GAAAF,OAAA;AAMA;AACA;AACA;AACA;AACA,SAASG,sBAAsBA,CAC7BC,SAAiB,EACgC;EACjD,MAAMC,UAAU,GAAGD,SAAS,oBAATA,SAAS,CAAEE,IAAI,CAAC,CAAC;EACpC,IAAI,CAACD,UAAU,IAAI,CAAC,IAAAE,0BAAe,EAACF,UAAU,CAAC,EAAE;IAC/C,OAAO,IAAI;EACb;EACA,MAAMG,MAAM,GAAG,IAAAC,8BAAY,EAACJ,UAAU,CAAC;EACvC,IAAI,CAAAG,MAAM,oBAANA,MAAM,CAAEE,IAAI,MAAKC,sBAAW,CAACC,MAAM,IAAI,CAACJ,MAAM,CAACK,UAAU,IAAI,CAACL,MAAM,CAACM,QAAQ,EAAE;IACjF,OAAO,IAAI;EACb;EACA,OAAO;IAAED,UAAU,EAAEL,MAAM,CAACK,UAAU;IAAEC,QAAQ,EAAEN,MAAM,CAACM;EAAS,CAAC;AACrE;AAEA,SAASC,UAAUA,CACjBC,WAA2B,EAC3BF,QAAgB,EAChBG,MAAwB,EACf;EACT;EACA;EACA,IAAID,WAAW,CAACE,EAAE,KAAKJ,QAAQ,EAAE;IAC/B,OAAO,IAAI;EACb;EAEA,MAAMK,QAAQ,GAAGH,WAAW,CAACC,MAAM,CAACG,gBAAgB,CAAyB;EAE7E,IAAI,OAAOD,QAAQ,KAAK,QAAQ,EAAE;IAChC,OAAO,KAAK;EACd;EAEA,MAAME,OAAO,GAAGJ,MAAM,CAACK,eAAe,IAAI,UAAU;EAEpD,QAAQD,OAAO;IACb,KAAK,UAAU;MACb,OAAOF,QAAQ,CAACI,QAAQ,CAAC,IAAIT,QAAQ,EAAE,CAAC;IAC1C,KAAK,UAAU;MACb,OAAOK,QAAQ,CAACK,QAAQ,CAACV,QAAQ,CAAC;IACpC,KAAK,QAAQ;MACX,OAAOK,QAAQ,KAAKL,QAAQ;IAC9B;MACE,OAAOK,QAAQ,CAACI,QAAQ,CAAC,IAAIT,QAAQ,EAAE,CAAC;EAC5C;AACF;AAEA,SAASW,mBAAmBA,CAC1BZ,UAAkB,EAClBC,QAAgB,EAChBY,OAAyB,EACzBC,YAA0B,EACH;EACvB,MAAMC,UAAU,GAAGD,YAAY,CAACE,WAAW,CAAChB,UAAU,CAAC;EAEvD,IAAI,CAACe,UAAU,EAAE;IACf,OAAO,IAAI;EACb;;EAEA;EACA;EACA,MAAME,WAAW,GAAGJ,OAAO,CAACK,IAAI,CAAEC,IAAI,IACpCjB,UAAU,CAACiB,IAAI,EAAElB,QAAQ,EAAEc,UAAU,CACvC,CAAC;EAED,OAAOE,WAAW,IAAI,IAAI;AAC5B;;AAEA;AACO,SAASG,6BAA6BA,CAC3CjB,WAA2B,EAC3BC,MAAwB,EACJ;EACpB,OAAOiB,4BAA4B,CAAClB,WAAW,EAAEC,MAAM,CAAC;AAC1D;AAEA,SAASkB,gBAAgBA,CAACC,SAAiB,EAAsB;EAAA,IAAAC,WAAA,EAAAC,YAAA,EAAAC,YAAA;EAC/D,MAAMC,IAAI,GAAG,IAAIC,IAAI,CAACL,SAAS,CAAC;EAChC,IAAIM,KAAK,CAACF,IAAI,CAACG,OAAO,CAAC,CAAC,CAAC,EAAE;IACzB,OAAOC,SAAS;EAClB;EACA,MAAMC,SAAS,GAAG,IAAIC,IAAI,CAACC,cAAc,CAAC,OAAO,EAAE;IACjDC,GAAG,EAAE,SAAS;IACdC,KAAK,EAAE,OAAO;IACdC,QAAQ,EAAE,aAAa;IACvBC,IAAI,EAAE;EACR,CAAC,CAAC;EACF,MAAMC,KAAK,GAAGP,SAAS,CAACQ,aAAa,CAACb,IAAI,CAAC;EAC3C,MAAMQ,GAAG,IAAAX,WAAA,GAAGe,KAAK,CAACrB,IAAI,CAAEuB,IAAI,IAAKA,IAAI,CAAC5C,IAAI,KAAK,KAAK,CAAC,qBAAzC2B,WAAA,CAA2CkB,KAAK;EAC5D,MAAMN,KAAK,IAAAX,YAAA,GAAGc,KAAK,CAACrB,IAAI,CAAEuB,IAAI,IAAKA,IAAI,CAAC5C,IAAI,KAAK,OAAO,CAAC,qBAA3C4B,YAAA,CAA6CiB,KAAK;EAChE,MAAMJ,IAAI,IAAAZ,YAAA,GAAGa,KAAK,CAACrB,IAAI,CAAEuB,IAAI,IAAKA,IAAI,CAAC5C,IAAI,KAAK,MAAM,CAAC,qBAA1C6B,YAAA,CAA4CgB,KAAK;EAE9D,IAAI,CAACP,GAAG,IAAI,CAACC,KAAK,IAAI,CAACE,IAAI,EAAE;IAC3B,OAAOP,SAAS;EAClB;EAEA,OAAO,GAAGI,GAAG,IAAIC,KAAK,IAAIE,IAAI,EAAE;AAClC;;AAEA;AACO,SAASK,6BAA6BA,CAC3CxC,WAA2B,EAC3BC,MAAwB,EACR;EAChB,OAAOiB,4BAA4B,CAAClB,WAAW,EAAEC,MAAM,CAAC;AAC1D;AAEO,SAASiB,4BAA4BA,CAC1ClB,WAA2B,EAC3BC,MAAwB,EACR;EAAA,IAAAwC,mBAAA,EAAAC,iBAAA;EAChB,IAAIC,QAA4B;EAChC,MAAMC,gBAAgB,GAAG5C,WAAW,CAAC6C,WAAW,IAAI7C,WAAW,CAAC8C,YAAY;EAE5E,IAAI9C,WAAW,CAAC+C,GAAG,EAAE;IACnB,MAAMC,kBAAkB,GAAGhD,WAAW,CAAC+C,GAAG,CAACE,UAAU,CAAC,MAAM,CAAC;IAC7DN,QAAQ,GAAG1C,MAAM,CAACiD,YAAY,IAAI,CAACF,kBAAkB,GACjD,GAAG/C,MAAM,CAACiD,YAAY,GAAGlD,WAAW,CAAC+C,GAAG,EAAE,GAC1C/C,WAAW,CAAC+C,GAAG;EACrB;;EAEA;EACA;EACA;EACA;EACA,MAAMI,QAAQ,GAAG,IAAAC,gCAAgB,EAC/BpD,WAAW,CAACmD,QAAQ,EACpBnD,WAAW,CAACqD,aACd,CAAC;EAED,OAAO;IACLC,GAAG,EAAEtD,WAAW,CAACsD,GAAG;IACpBC,KAAK,EAAEvD,WAAW,CAACuD,KAAK;IACxB7D,IAAI,EAAEM,WAAW,CAACN,IAAI;IACtB8D,WAAW,EAAExD,WAAW,CAACwD,WAAW;IACpCC,OAAO,EAAEzD,WAAW,CAACyD,OAAO;IAC5Bd,QAAQ;IACRe,cAAc,EAAE,IAAAC,gCAAgB,EAAC3D,WAAW,CAAC4D,OAAO,CAAC;IACrDC,KAAK,EAAE,IAAAC,gCAAgB,EAAC9D,WAAW,CAAC6D,KAAK,EAAEV,QAAQ,CAAC;IACpDY,QAAQ,EAAE,IAAAD,gCAAgB,EAAC9D,WAAW,CAAC+D,QAAQ,EAAEZ,QAAQ,CAAC;IAC1Da,QAAQ,EAAE,IAAAF,gCAAgB,EAAC9D,WAAW,CAACgE,QAAQ,EAAEb,QAAQ,CAAC;IAC1D3B,IAAI,EAAExB,WAAW,CAACiE,SAAS,GACvB9C,gBAAgB,CAACnB,WAAW,CAACiE,SAAS,CAAC,GACvCrC,SAAS;IACbiB,WAAW,EAAED,gBAAgB,GACzBzB,gBAAgB,CAACyB,gBAAgB,CAAC,GAClChB,SAAS;IACbsC,MAAM,EAAElE,WAAW,CAACkE,MAAM,IAAIlE,WAAW,CAACmE,UAAU;IACpDC,SAAS,EAAEpE,WAAW,CAACqE,WAAW;IAClCC,KAAK,EAAEtE,WAAW,CAACsE,KAAK;IACxBC,IAAI,EAAE,CAAA9B,mBAAA,GAAAzC,WAAW,CAACwE,MAAM,aAAlB/B,mBAAA,CAAoBgC,MAAM,GAC5BzE,WAAW,CAACwE,MAAM,GAClB,CAAA9B,iBAAA,GAAA1C,WAAW,CAACuE,IAAI,aAAhB7B,iBAAA,CAAkB+B,MAAM,GACxBzE,WAAW,CAACuE,IAAI,GAChB3C;EACN,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACO,MAAM8C,gBAAiC,GAAAC,OAAA,CAAAD,gBAAA,GAAG;EAC/CvF,sBAAsB;EACtBsB;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["_linkTypes","require","_entityLinkParser","_matchedVariant","extractEntityIdFromUrl","entityUrl","trimmedUrl","trim","isWeb5EntityUrl","parsed","parseWeb5Url","type","Web5UrlType","ENTITY","entityType","entityId","matchesUrl","payloadItem","config","id","urlValue","urlMatchProperty","pattern","urlMatchPattern","endsWith","includes","findEntityInPayload","payload","entityConfig","typeConfig","entityTypes","matchedItem","find","item","transformToSolutionEntityData","transformToGenericEntityData","formatEntityDate","timestamp","_parts$find","_parts$find2","_parts$find3","date","Date","isNaN","getTime","undefined","formatter","Intl","DateTimeFormat","day","month","timeZone","year","parts","formatToParts","part","value","transformToBlogPostEntityData","_payloadItem$labels","_payloadItem$tags","imageUrl","publishTimestamp","publishDate","published_at","img","imgAlreadyAbsolute","startsWith","imageBaseUrl","currency","readCurrencyCode","priceCurrency","url","title","description","excerpt","matchedOptions","toMatchedOptions","options","variantId","price","formatPriceField","priceMin","priceMax","update_ts","author","authorName","cmsItemId","cms_item_id","usage","tags","labels","length","defaultExtractor","exports"],"sources":["../../../src/entity/defaultExtractor.ts"],"sourcesContent":["import type { SolutionEntityData, BlogEntityData, EntityItemData } from '../component/types';\nimport type {\n ApiPayloadItem,\n EntityConfig,\n EntityTypeConfig,\n} from '../types/entity';\nimport type { EntityExtractor } from './types';\nimport { Web5UrlType, isWeb5EntityUrl } from '../types/link-types';\nimport { parseWeb5Url } from '../utils/entityLinkParser';\nimport {\n formatPriceField,\n readCurrencyCode,\n toMatchedOptions,\n} from './matchedVariant';\n\n/**\n * Extract entity type and ID from a web5://entity/ URL.\n * Delegates to the central `parseWeb5Url` parser.\n */\nfunction extractEntityIdFromUrl(\n entityUrl: string,\n): { entityType: string; entityId: string } | null {\n const trimmedUrl = entityUrl?.trim();\n if (!trimmedUrl || !isWeb5EntityUrl(trimmedUrl)) {\n return null;\n }\n const parsed = parseWeb5Url(trimmedUrl);\n if (parsed?.type !== Web5UrlType.ENTITY || !parsed.entityType || !parsed.entityId) {\n return null;\n }\n return { entityType: parsed.entityType, entityId: parsed.entityId };\n}\n\nfunction matchesUrl(\n payloadItem: ApiPayloadItem,\n entityId: string,\n config: EntityTypeConfig,\n): boolean {\n // Entities resolve by `entityId` (the `<id>` in `web5://entity/<type>/<id>`).\n // Accept a direct `id` match, then fall back to the configured url strategy.\n if (payloadItem.id === entityId) {\n return true;\n }\n\n const urlValue = payloadItem[config.urlMatchProperty as keyof ApiPayloadItem];\n\n if (typeof urlValue !== 'string') {\n return false;\n }\n\n const pattern = config.urlMatchPattern || 'endsWith';\n\n switch (pattern) {\n case 'endsWith':\n return urlValue.endsWith(`/${entityId}`);\n case 'contains':\n return urlValue.includes(entityId);\n case 'equals':\n return urlValue === entityId;\n default:\n return urlValue.endsWith(`/${entityId}`);\n }\n}\n\nfunction findEntityInPayload(\n entityType: string,\n entityId: string,\n payload: ApiPayloadItem[],\n entityConfig: EntityConfig,\n): ApiPayloadItem | null {\n const typeConfig = entityConfig.entityTypes[entityType];\n\n if (!typeConfig) {\n return null;\n }\n\n // Resolution keys off `entityId` (not the type): find the payload item whose\n // id/url matches, using the type's configured url strategy.\n const matchedItem = payload.find((item) =>\n matchesUrl(item, entityId, typeConfig),\n );\n\n return matchedItem || null;\n}\n\n/** @deprecated Use transformToGenericEntityData */\nexport function transformToSolutionEntityData(\n payloadItem: ApiPayloadItem,\n config: EntityTypeConfig,\n): SolutionEntityData {\n return transformToGenericEntityData(payloadItem, config);\n}\n\nfunction formatEntityDate(timestamp: string): string | undefined {\n const date = new Date(timestamp);\n if (isNaN(date.getTime())) {\n return undefined;\n }\n const formatter = new Intl.DateTimeFormat('en-US', {\n day: 'numeric',\n month: 'short',\n timeZone: 'Europe/Kiev',\n year: 'numeric',\n });\n const parts = formatter.formatToParts(date);\n const day = parts.find((part) => part.type === 'day')?.value;\n const month = parts.find((part) => part.type === 'month')?.value;\n const year = parts.find((part) => part.type === 'year')?.value;\n\n if (!day || !month || !year) {\n return undefined;\n }\n\n return `${day} ${month} ${year}`;\n}\n\n/** @deprecated Use transformToGenericEntityData */\nexport function transformToBlogPostEntityData(\n payloadItem: ApiPayloadItem,\n config: EntityTypeConfig,\n): BlogEntityData {\n return transformToGenericEntityData(payloadItem, config);\n}\n\nexport function transformToGenericEntityData(\n payloadItem: ApiPayloadItem,\n config: EntityTypeConfig,\n): EntityItemData {\n let imageUrl: string | undefined;\n const publishTimestamp = payloadItem.publishDate || payloadItem.published_at;\n\n if (payloadItem.img) {\n const imgAlreadyAbsolute = payloadItem.img.startsWith('http');\n imageUrl = config.imageBaseUrl && !imgAlreadyAbsolute\n ? `${config.imageBaseUrl}${payloadItem.img}`\n : payloadItem.img;\n }\n\n // A document that retrieval narrowed to one child arrives with that child's\n // presentation fields already overlaid (`url`, `img`, `price`) plus the pairs\n // that identify it and the span its parent covers. The overlay is done\n // server-side; all that is left here is to shape those two for display.\n const currency = readCurrencyCode(\n payloadItem.currency,\n payloadItem.priceCurrency,\n );\n\n return {\n url: payloadItem.url,\n title: payloadItem.title,\n type: payloadItem.type,\n description: payloadItem.description,\n excerpt: payloadItem.excerpt,\n imageUrl,\n matchedOptions: toMatchedOptions(payloadItem.options),\n // Which child the row is wearing. Carried through so the live enricher\n // prices THAT variant (`mergeShopifyEntityData` keeps the turn's value)\n // rather than falling back to the product's default.\n variantId: payloadItem.variantId,\n price: formatPriceField(payloadItem.price, currency),\n priceMin: formatPriceField(payloadItem.priceMin, currency),\n priceMax: formatPriceField(payloadItem.priceMax, currency),\n date: payloadItem.update_ts\n ? formatEntityDate(payloadItem.update_ts)\n : undefined,\n publishDate: publishTimestamp\n ? formatEntityDate(publishTimestamp)\n : undefined,\n author: payloadItem.author || payloadItem.authorName,\n cmsItemId: payloadItem.cms_item_id,\n usage: payloadItem.usage,\n tags: payloadItem.labels?.length\n ? payloadItem.labels\n : payloadItem.tags?.length\n ? payloadItem.tags\n : undefined,\n };\n}\n\n/**\n * Default entity extractor - config-driven extraction for any client.\n * Uses entityConfig from client JSON to resolve web5://entity/ URLs against API payload.\n */\nexport const defaultExtractor: EntityExtractor = {\n extractEntityIdFromUrl,\n findEntityInPayload,\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.entityPayloadFromItems = entityPayloadFromItems;
5
+ exports.normalizeEntityItem = normalizeEntityItem;
6
+ /**
7
+ * One entity row, normalized onto the `ApiPayloadItem` shape both resolution
8
+ * paths key off.
9
+ *
10
+ * Two producers, one document. `list-items` answers with the raw retrieval
11
+ * document; the turn answers with the engine's allowlisted projection of the
12
+ * SAME document — same field names, plus the keys only a card reads (`type`,
13
+ * `variantId`, and the matched child's `url`/`img`/`price` already overlaid).
14
+ * Normalizing both here is what lets `findEntityInPayload` resolve a reference
15
+ * without caring which one answered.
16
+ *
17
+ * `id` is what a `web5://entity/<type>/<id>` reference resolves against, and
18
+ * the reference carries the DOCUMENT id — so `doc_id` becomes `id`, exactly as
19
+ * the `list-items` path has always done. The fallback to a row's own `id` is
20
+ * for the day a store publishes one (none does today); a row with neither has
21
+ * nothing to be keyed by and is dropped.
22
+ */
23
+ function normalizeEntityItem(raw) {
24
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
25
+ return null;
26
+ }
27
+ const rec = raw;
28
+ const docId = typeof rec.doc_id === 'string' ? rec.doc_id : '';
29
+ const ownId = typeof rec.id === 'string' ? rec.id : '';
30
+ const id = docId || ownId;
31
+ if (!id) {
32
+ return null;
33
+ }
34
+
35
+ // Rows travel as-is apart from the `doc_id` → `id` alias every consumer keys
36
+ // off, and the image alias below.
37
+ //
38
+ // `url` is deliberately NOT rewritten — `?variant=…` is exactly how a
39
+ // resolved variant travels, and rebuilding the path from `handle` would
40
+ // throw it away.
41
+ const normalized = {
42
+ ...rec,
43
+ id
44
+ };
45
+
46
+ // Product rows carry the image as `thumbnail` / `images[]` (the Vespa doc
47
+ // shape), but the shared transform (`transformToGenericEntityData`) reads
48
+ // `img`. Alias it so product cards keep their image.
49
+ //
50
+ // A bridge, not a contract: a row that already carries `img` — a CMS doc, or
51
+ // one where retrieval overlaid a matched child's presentation fields — passes
52
+ // through untouched. Drop the alias once every producer emits `img`.
53
+ if (normalized.img == null) {
54
+ if (typeof rec.thumbnail === 'string' && rec.thumbnail) {
55
+ normalized.img = rec.thumbnail;
56
+ } else if (Array.isArray(rec.images) && typeof rec.images[0] === 'string' && rec.images[0]) {
57
+ normalized.img = rec.images[0];
58
+ }
59
+ }
60
+ return normalized;
61
+ }
62
+
63
+ /**
64
+ * Read an `{ items: [...] }` container into a normalized payload.
65
+ *
66
+ * Both carriers of the turn's resolved entities use that shape: the live
67
+ * stream's `entities` frame (`TurnEntities.items`) and the persisted
68
+ * `additional_data["documents"]` the reload path reads back. A bare array is
69
+ * accepted too, so a caller that already unwrapped one can pass it straight in.
70
+ *
71
+ * Anything that isn't a keyable row is dropped rather than passed on — a
72
+ * payload entry with no id can only ever fail to match.
73
+ */
74
+ function entityPayloadFromItems(container) {
75
+ let items;
76
+ if (Array.isArray(container)) {
77
+ items = container;
78
+ } else if (container && typeof container === 'object') {
79
+ items = container.items;
80
+ }
81
+ if (!Array.isArray(items)) {
82
+ return [];
83
+ }
84
+ const payload = [];
85
+ for (const item of items) {
86
+ const normalized = normalizeEntityItem(item);
87
+ if (normalized) {
88
+ payload.push(normalized);
89
+ }
90
+ }
91
+ return payload;
92
+ }
93
+ //# sourceMappingURL=entityPayload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["normalizeEntityItem","raw","Array","isArray","rec","docId","doc_id","ownId","id","normalized","img","thumbnail","images","entityPayloadFromItems","container","items","payload","item","push"],"sources":["../../../src/entity/entityPayload.ts"],"sourcesContent":["import type { ApiPayloadItem } from '../types/entity';\n\n/**\n * One entity row, normalized onto the `ApiPayloadItem` shape both resolution\n * paths key off.\n *\n * Two producers, one document. `list-items` answers with the raw retrieval\n * document; the turn answers with the engine's allowlisted projection of the\n * SAME document — same field names, plus the keys only a card reads (`type`,\n * `variantId`, and the matched child's `url`/`img`/`price` already overlaid).\n * Normalizing both here is what lets `findEntityInPayload` resolve a reference\n * without caring which one answered.\n *\n * `id` is what a `web5://entity/<type>/<id>` reference resolves against, and\n * the reference carries the DOCUMENT id — so `doc_id` becomes `id`, exactly as\n * the `list-items` path has always done. The fallback to a row's own `id` is\n * for the day a store publishes one (none does today); a row with neither has\n * nothing to be keyed by and is dropped.\n */\nexport function normalizeEntityItem(raw: unknown): ApiPayloadItem | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return null;\n }\n const rec = raw as Record<string, unknown>;\n const docId = typeof rec.doc_id === 'string' ? rec.doc_id : '';\n const ownId = typeof rec.id === 'string' ? rec.id : '';\n const id = docId || ownId;\n if (!id) {\n return null;\n }\n\n // Rows travel as-is apart from the `doc_id` → `id` alias every consumer keys\n // off, and the image alias below.\n //\n // `url` is deliberately NOT rewritten — `?variant=…` is exactly how a\n // resolved variant travels, and rebuilding the path from `handle` would\n // throw it away.\n const normalized: Record<string, unknown> = { ...rec, id };\n\n // Product rows carry the image as `thumbnail` / `images[]` (the Vespa doc\n // shape), but the shared transform (`transformToGenericEntityData`) reads\n // `img`. Alias it so product cards keep their image.\n //\n // A bridge, not a contract: a row that already carries `img` — a CMS doc, or\n // one where retrieval overlaid a matched child's presentation fields — passes\n // through untouched. Drop the alias once every producer emits `img`.\n if (normalized.img == null) {\n if (typeof rec.thumbnail === 'string' && rec.thumbnail) {\n normalized.img = rec.thumbnail;\n } else if (\n Array.isArray(rec.images) &&\n typeof rec.images[0] === 'string' &&\n rec.images[0]\n ) {\n normalized.img = rec.images[0];\n }\n }\n\n return normalized as unknown as ApiPayloadItem;\n}\n\n/**\n * Read an `{ items: [...] }` container into a normalized payload.\n *\n * Both carriers of the turn's resolved entities use that shape: the live\n * stream's `entities` frame (`TurnEntities.items`) and the persisted\n * `additional_data[\"documents\"]` the reload path reads back. A bare array is\n * accepted too, so a caller that already unwrapped one can pass it straight in.\n *\n * Anything that isn't a keyable row is dropped rather than passed on — a\n * payload entry with no id can only ever fail to match.\n */\nexport function entityPayloadFromItems(container: unknown): ApiPayloadItem[] {\n let items: unknown;\n if (Array.isArray(container)) {\n items = container;\n } else if (container && typeof container === 'object') {\n items = (container as { items?: unknown }).items;\n }\n if (!Array.isArray(items)) {\n return [];\n }\n\n const payload: ApiPayloadItem[] = [];\n for (const item of items) {\n const normalized = normalizeEntityItem(item);\n if (normalized) {\n payload.push(normalized);\n }\n }\n return payload;\n}\n"],"mappings":";;;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,mBAAmBA,CAACC,GAAY,EAAyB;EACvE,IAAI,CAACA,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ,IAAIC,KAAK,CAACC,OAAO,CAACF,GAAG,CAAC,EAAE;IACzD,OAAO,IAAI;EACb;EACA,MAAMG,GAAG,GAAGH,GAA8B;EAC1C,MAAMI,KAAK,GAAG,OAAOD,GAAG,CAACE,MAAM,KAAK,QAAQ,GAAGF,GAAG,CAACE,MAAM,GAAG,EAAE;EAC9D,MAAMC,KAAK,GAAG,OAAOH,GAAG,CAACI,EAAE,KAAK,QAAQ,GAAGJ,GAAG,CAACI,EAAE,GAAG,EAAE;EACtD,MAAMA,EAAE,GAAGH,KAAK,IAAIE,KAAK;EACzB,IAAI,CAACC,EAAE,EAAE;IACP,OAAO,IAAI;EACb;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA,MAAMC,UAAmC,GAAG;IAAE,GAAGL,GAAG;IAAEI;EAAG,CAAC;;EAE1D;EACA;EACA;EACA;EACA;EACA;EACA;EACA,IAAIC,UAAU,CAACC,GAAG,IAAI,IAAI,EAAE;IAC1B,IAAI,OAAON,GAAG,CAACO,SAAS,KAAK,QAAQ,IAAIP,GAAG,CAACO,SAAS,EAAE;MACtDF,UAAU,CAACC,GAAG,GAAGN,GAAG,CAACO,SAAS;IAChC,CAAC,MAAM,IACLT,KAAK,CAACC,OAAO,CAACC,GAAG,CAACQ,MAAM,CAAC,IACzB,OAAOR,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,IACjCR,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC,EACb;MACAH,UAAU,CAACC,GAAG,GAAGN,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC;IAChC;EACF;EAEA,OAAOH,UAAU;AACnB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASI,sBAAsBA,CAACC,SAAkB,EAAoB;EAC3E,IAAIC,KAAc;EAClB,IAAIb,KAAK,CAACC,OAAO,CAACW,SAAS,CAAC,EAAE;IAC5BC,KAAK,GAAGD,SAAS;EACnB,CAAC,MAAM,IAAIA,SAAS,IAAI,OAAOA,SAAS,KAAK,QAAQ,EAAE;IACrDC,KAAK,GAAID,SAAS,CAAyBC,KAAK;EAClD;EACA,IAAI,CAACb,KAAK,CAACC,OAAO,CAACY,KAAK,CAAC,EAAE;IACzB,OAAO,EAAE;EACX;EAEA,MAAMC,OAAyB,GAAG,EAAE;EACpC,KAAK,MAAMC,IAAI,IAAIF,KAAK,EAAE;IACxB,MAAMN,UAAU,GAAGT,mBAAmB,CAACiB,IAAI,CAAC;IAC5C,IAAIR,UAAU,EAAE;MACdO,OAAO,CAACE,IAAI,CAACT,UAAU,CAAC;IAC1B;EACF;EACA,OAAOO,OAAO;AAChB","ignoreList":[]}
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.formatProductPriceLabel = exports.formatPriceField = exports.formatMoney = exports.fetchEntityListData = exports.enrichEntitiesFromPayload = exports.LIST_ITEMS_ENDPOINT = void 0;
4
+ exports.formatProductPriceLabel = exports.formatPriceField = exports.formatMoney = exports.fetchEntityListData = exports.entityPayloadFromItems = exports.enrichEntitiesFromPayload = exports.LIST_ITEMS_ENDPOINT = void 0;
5
5
  exports.getEntityExtractor = getEntityExtractor;
6
- exports.readCurrencyCode = exports.mergeEntityData = exports.listEntityItems = void 0;
6
+ exports.readCurrencyCode = exports.normalizeEntityItem = exports.mergeEntityData = exports.listEntityItems = void 0;
7
7
  exports.registerEntityExtractor = registerEntityExtractor;
8
8
  exports.transformToSolutionEntityData = exports.transformToGenericEntityData = exports.transformToBlogPostEntityData = exports.toMatchedOptions = void 0;
9
9
  var _defaultExtractor = require("./defaultExtractor");
@@ -13,6 +13,9 @@ exports.transformToBlogPostEntityData = _defaultExtractor.transformToBlogPostEnt
13
13
  exports.transformToGenericEntityData = _defaultExtractor.transformToGenericEntityData;
14
14
  var _enrichEntitiesFromPayload = require("./enrichEntitiesFromPayload");
15
15
  exports.enrichEntitiesFromPayload = _enrichEntitiesFromPayload.enrichEntitiesFromPayload;
16
+ var _entityPayload = require("./entityPayload");
17
+ exports.normalizeEntityItem = _entityPayload.normalizeEntityItem;
18
+ exports.entityPayloadFromItems = _entityPayload.entityPayloadFromItems;
16
19
  var _mergeEntityData = require("./mergeEntityData");
17
20
  exports.mergeEntityData = _mergeEntityData.mergeEntityData;
18
21
  var _matchedVariant = require("./matchedVariant");
@@ -1 +1 @@
1
- {"version":3,"names":["_defaultExtractor","require","exports","defaultExtractor","transformToSolutionEntityData","transformToBlogPostEntityData","transformToGenericEntityData","_enrichEntitiesFromPayload","enrichEntitiesFromPayload","_mergeEntityData","mergeEntityData","_matchedVariant","toMatchedOptions","formatMoney","readCurrencyCode","formatPriceField","formatProductPriceLabel","_fetchEntityListData","fetchEntityListData","_listEntityItems","listEntityItems","LIST_ITEMS_ENDPOINT","extractorRegistry","getEntityExtractor","clientId","registerEntityExtractor","extractor"],"sources":["../../../src/entity/index.ts"],"sourcesContent":["import type { EntityExtractor } from './types';\nimport { defaultExtractor } from './defaultExtractor';\n\nexport {\n defaultExtractor,\n transformToSolutionEntityData,\n transformToBlogPostEntityData,\n transformToGenericEntityData,\n} from './defaultExtractor';\n\nexport { enrichEntitiesFromPayload } from './enrichEntitiesFromPayload';\nexport { mergeEntityData } from './mergeEntityData';\nexport {\n toMatchedOptions,\n formatMoney,\n readCurrencyCode,\n formatPriceField,\n formatProductPriceLabel,\n type MatchedOption,\n type ProductPriceFields,\n} from './matchedVariant';\nexport {\n fetchEntityListData,\n type FetchEntityListDataOptions,\n} from './fetchEntityListData';\nexport {\n listEntityItems,\n LIST_ITEMS_ENDPOINT,\n type ListEntityItemsOptions,\n} from './listEntityItems';\n\nexport type { EntityExtractor } from './types';\n\n/**\n * Registry of client-specific entity extractors.\n * Only needed if a client requires custom extraction logic beyond the config-driven default.\n */\nconst extractorRegistry: Record<string, EntityExtractor> = {};\n\n/**\n * Get the entity extractor for a specific client.\n * The default extractor is config-driven and works for any client with entityConfig.\n */\nexport function getEntityExtractor(clientId?: string): EntityExtractor {\n if (clientId && extractorRegistry[clientId]) {\n return extractorRegistry[clientId];\n }\n return defaultExtractor;\n}\n\n/**\n * Register a custom entity extractor for a client\n * Useful for runtime registration or testing\n */\nexport function registerEntityExtractor(\n clientId: string,\n extractor: EntityExtractor,\n): void {\n extractorRegistry[clientId] = extractor;\n}\n"],"mappings":";;;;;;;;AACA,IAAAA,iBAAA,GAAAC,OAAA;AAAsDC,OAAA,CAAAC,gBAAA,GAAAH,iBAAA,CAAAG,gBAAA;AAAAD,OAAA,CAAAE,6BAAA,GAAAJ,iBAAA,CAAAI,6BAAA;AAAAF,OAAA,CAAAG,6BAAA,GAAAL,iBAAA,CAAAK,6BAAA;AAAAH,OAAA,CAAAI,4BAAA,GAAAN,iBAAA,CAAAM,4BAAA;AAStD,IAAAC,0BAAA,GAAAN,OAAA;AAAwEC,OAAA,CAAAM,yBAAA,GAAAD,0BAAA,CAAAC,yBAAA;AACxE,IAAAC,gBAAA,GAAAR,OAAA;AAAoDC,OAAA,CAAAQ,eAAA,GAAAD,gBAAA,CAAAC,eAAA;AACpD,IAAAC,eAAA,GAAAV,OAAA;AAQ0BC,OAAA,CAAAU,gBAAA,GAAAD,eAAA,CAAAC,gBAAA;AAAAV,OAAA,CAAAW,WAAA,GAAAF,eAAA,CAAAE,WAAA;AAAAX,OAAA,CAAAY,gBAAA,GAAAH,eAAA,CAAAG,gBAAA;AAAAZ,OAAA,CAAAa,gBAAA,GAAAJ,eAAA,CAAAI,gBAAA;AAAAb,OAAA,CAAAc,uBAAA,GAAAL,eAAA,CAAAK,uBAAA;AAC1B,IAAAC,oBAAA,GAAAhB,OAAA;AAG+BC,OAAA,CAAAgB,mBAAA,GAAAD,oBAAA,CAAAC,mBAAA;AAC/B,IAAAC,gBAAA,GAAAlB,OAAA;AAI2BC,OAAA,CAAAkB,eAAA,GAAAD,gBAAA,CAAAC,eAAA;AAAAlB,OAAA,CAAAmB,mBAAA,GAAAF,gBAAA,CAAAE,mBAAA;AAI3B;AACA;AACA;AACA;AACA,MAAMC,iBAAkD,GAAG,CAAC,CAAC;;AAE7D;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAACC,QAAiB,EAAmB;EACrE,IAAIA,QAAQ,IAAIF,iBAAiB,CAACE,QAAQ,CAAC,EAAE;IAC3C,OAAOF,iBAAiB,CAACE,QAAQ,CAAC;EACpC;EACA,OAAOrB,kCAAgB;AACzB;;AAEA;AACA;AACA;AACA;AACO,SAASsB,uBAAuBA,CACrCD,QAAgB,EAChBE,SAA0B,EACpB;EACNJ,iBAAiB,CAACE,QAAQ,CAAC,GAAGE,SAAS;AACzC","ignoreList":[]}
1
+ {"version":3,"names":["_defaultExtractor","require","exports","defaultExtractor","transformToSolutionEntityData","transformToBlogPostEntityData","transformToGenericEntityData","_enrichEntitiesFromPayload","enrichEntitiesFromPayload","_entityPayload","normalizeEntityItem","entityPayloadFromItems","_mergeEntityData","mergeEntityData","_matchedVariant","toMatchedOptions","formatMoney","readCurrencyCode","formatPriceField","formatProductPriceLabel","_fetchEntityListData","fetchEntityListData","_listEntityItems","listEntityItems","LIST_ITEMS_ENDPOINT","extractorRegistry","getEntityExtractor","clientId","registerEntityExtractor","extractor"],"sources":["../../../src/entity/index.ts"],"sourcesContent":["import type { EntityExtractor } from './types';\nimport { defaultExtractor } from './defaultExtractor';\n\nexport {\n defaultExtractor,\n transformToSolutionEntityData,\n transformToBlogPostEntityData,\n transformToGenericEntityData,\n} from './defaultExtractor';\n\nexport { enrichEntitiesFromPayload } from './enrichEntitiesFromPayload';\nexport { normalizeEntityItem, entityPayloadFromItems } from './entityPayload';\nexport { mergeEntityData } from './mergeEntityData';\nexport {\n toMatchedOptions,\n formatMoney,\n readCurrencyCode,\n formatPriceField,\n formatProductPriceLabel,\n type MatchedOption,\n type ProductPriceFields,\n} from './matchedVariant';\nexport {\n fetchEntityListData,\n type FetchEntityListDataOptions,\n} from './fetchEntityListData';\nexport {\n listEntityItems,\n LIST_ITEMS_ENDPOINT,\n type ListEntityItemsOptions,\n} from './listEntityItems';\n\nexport type { EntityExtractor } from './types';\n\n/**\n * Registry of client-specific entity extractors.\n * Only needed if a client requires custom extraction logic beyond the config-driven default.\n */\nconst extractorRegistry: Record<string, EntityExtractor> = {};\n\n/**\n * Get the entity extractor for a specific client.\n * The default extractor is config-driven and works for any client with entityConfig.\n */\nexport function getEntityExtractor(clientId?: string): EntityExtractor {\n if (clientId && extractorRegistry[clientId]) {\n return extractorRegistry[clientId];\n }\n return defaultExtractor;\n}\n\n/**\n * Register a custom entity extractor for a client\n * Useful for runtime registration or testing\n */\nexport function registerEntityExtractor(\n clientId: string,\n extractor: EntityExtractor,\n): void {\n extractorRegistry[clientId] = extractor;\n}\n"],"mappings":";;;;;;;;AACA,IAAAA,iBAAA,GAAAC,OAAA;AAAsDC,OAAA,CAAAC,gBAAA,GAAAH,iBAAA,CAAAG,gBAAA;AAAAD,OAAA,CAAAE,6BAAA,GAAAJ,iBAAA,CAAAI,6BAAA;AAAAF,OAAA,CAAAG,6BAAA,GAAAL,iBAAA,CAAAK,6BAAA;AAAAH,OAAA,CAAAI,4BAAA,GAAAN,iBAAA,CAAAM,4BAAA;AAStD,IAAAC,0BAAA,GAAAN,OAAA;AAAwEC,OAAA,CAAAM,yBAAA,GAAAD,0BAAA,CAAAC,yBAAA;AACxE,IAAAC,cAAA,GAAAR,OAAA;AAA8EC,OAAA,CAAAQ,mBAAA,GAAAD,cAAA,CAAAC,mBAAA;AAAAR,OAAA,CAAAS,sBAAA,GAAAF,cAAA,CAAAE,sBAAA;AAC9E,IAAAC,gBAAA,GAAAX,OAAA;AAAoDC,OAAA,CAAAW,eAAA,GAAAD,gBAAA,CAAAC,eAAA;AACpD,IAAAC,eAAA,GAAAb,OAAA;AAQ0BC,OAAA,CAAAa,gBAAA,GAAAD,eAAA,CAAAC,gBAAA;AAAAb,OAAA,CAAAc,WAAA,GAAAF,eAAA,CAAAE,WAAA;AAAAd,OAAA,CAAAe,gBAAA,GAAAH,eAAA,CAAAG,gBAAA;AAAAf,OAAA,CAAAgB,gBAAA,GAAAJ,eAAA,CAAAI,gBAAA;AAAAhB,OAAA,CAAAiB,uBAAA,GAAAL,eAAA,CAAAK,uBAAA;AAC1B,IAAAC,oBAAA,GAAAnB,OAAA;AAG+BC,OAAA,CAAAmB,mBAAA,GAAAD,oBAAA,CAAAC,mBAAA;AAC/B,IAAAC,gBAAA,GAAArB,OAAA;AAI2BC,OAAA,CAAAqB,eAAA,GAAAD,gBAAA,CAAAC,eAAA;AAAArB,OAAA,CAAAsB,mBAAA,GAAAF,gBAAA,CAAAE,mBAAA;AAI3B;AACA;AACA;AACA;AACA,MAAMC,iBAAkD,GAAG,CAAC,CAAC;;AAE7D;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAACC,QAAiB,EAAmB;EACrE,IAAIA,QAAQ,IAAIF,iBAAiB,CAACE,QAAQ,CAAC,EAAE;IAC3C,OAAOF,iBAAiB,CAACE,QAAQ,CAAC;EACpC;EACA,OAAOxB,kCAAgB;AACzB;;AAEA;AACA;AACA;AACA;AACO,SAASyB,uBAAuBA,CACrCD,QAAgB,EAChBE,SAA0B,EACpB;EACNJ,iBAAiB,CAACE,QAAQ,CAAC,GAAGE,SAAS;AACzC","ignoreList":[]}
@@ -3,6 +3,7 @@
3
3
  exports.__esModule = true;
4
4
  exports.LIST_ITEMS_ENDPOINT = void 0;
5
5
  exports.listEntityItems = listEntityItems;
6
+ var _entityPayload = require("./entityPayload");
6
7
  /**
7
8
  * Entity resolution via the `list-items` RPC on the ConversationConfiguration
8
9
  * service.
@@ -53,44 +54,14 @@ async function listEntityItems(ids, opts) {
53
54
  throw new Error(`list-items ${res.status} ${res.statusText}`);
54
55
  }
55
56
  const body = await res.json();
56
- const items = body.items ?? [];
57
- for (const item of items) {
58
- const rec = item;
59
- const id = rec.doc_id;
60
- if (!id) {
61
- continue;
57
+ for (const item of body.items ?? []) {
58
+ // Same normalization the turn's own entity rows get (`doc_id` `id`, the
59
+ // `thumbnail`/`images[]` `img` alias): one shape, whichever producer
60
+ // answered. A row with no id at all is skipped — nothing could key it.
61
+ const normalized = (0, _entityPayload.normalizeEntityItem)(item);
62
+ if (normalized) {
63
+ result.set(normalized.id, normalized);
62
64
  }
63
- // Rows travel as-is apart from the `doc_id` → `id` alias both callers key
64
- // off, and the image alias below.
65
- //
66
- // `url` is deliberately NOT rewritten. It used to be rebuilt as
67
- // `/products/<handle>` for product rows to keep the link same-origin, which
68
- // threw away the query string — and `?variant=…` is exactly how a resolved
69
- // variant travels. The client already strips the origin without losing the
70
- // query: `canonicalizeFeatureProductUrl` (w5-client-feature-com) and
71
- // `toRelativeUrl` (web50-server-ui/src/utils/linkResolver.ts) both keep
72
- // `search` and `hash`.
73
- const normalized = {
74
- ...rec,
75
- id
76
- };
77
- // `list-items` product rows carry the image as `thumbnail` / `images[]`
78
- // (the Vespa doc shape — see `bo-web5-history`'s `ragDocShared.tsx`), but
79
- // the shared transform (`transformToGenericEntityData`) reads `img`. Alias
80
- // it so product cards keep their image.
81
- //
82
- // This is a bridge, not a contract: rows that already carry `img` — CMS
83
- // docs, and any row where retrieval overlaid a matched child's
84
- // presentation fields server-side — pass through untouched. Drop the
85
- // alias once `list-items` emits `img` for product rows too.
86
- if (normalized.img == null) {
87
- if (typeof rec.thumbnail === 'string' && rec.thumbnail) {
88
- normalized.img = rec.thumbnail;
89
- } else if (Array.isArray(rec.images) && typeof rec.images[0] === 'string' && rec.images[0]) {
90
- normalized.img = rec.images[0];
91
- }
92
- }
93
- result.set(id, normalized);
94
65
  }
95
66
  return result;
96
67
  }
@@ -1 +1 @@
1
- {"version":3,"names":["LIST_ITEMS_ENDPOINT","exports","listEntityItems","ids","opts","result","Map","length","res","fetchImpl","method","headers","body","JSON","stringify","ok","Error","status","statusText","json","items","item","rec","id","doc_id","normalized","img","thumbnail","Array","isArray","images","set"],"sources":["../../../src/entity/listEntityItems.ts"],"sourcesContent":["import type { ApiPayloadItem } from '../types/entity';\n\n/**\n * Entity resolution via the `list-items` RPC on the ConversationConfiguration\n * service.\n *\n * Entities are resolved through `list-items` (POST `{ ids }`). The response is\n * `{ items: [...] }` where each row is keyed by `doc_id`. We normalize to\n * `ApiPayloadItem` keyed by that id (and alias it onto `id`) so both callers\n * get back the same `Map<id, ApiPayloadItem>` contract.\n *\n * TODO(ambassador): the `@wix/ambassador-enterprise-web-five-v1-configuration`\n * `listItems` builder does NOT yet emit the public URL below — as of 1.0.27 it\n * resolves to the internal `/v1/conversation-configuration/list-items` path,\n * which 405s at the www edge (no public mapping). The gateway mapping was added\n * out-of-band (`/web5/conversation-configuration` → service root), giving the\n * URL below. Once an ambassador version ships with that mapping, replace the\n * hand-built request with the builder + `client.fetchWithAuth`, mirroring\n * `web50-server-ui/src/services/backendConfigService.ts`:\n * const options = listItems({ ids })({ isSSR: false, host: 'www.wixapis.com' });\n * const url = new URL(options.url!, 'https://www.wixapis.com');\n */\n\n/**\n * Public URL for the `list-items` RPC on `www.wixapis.com`. The `/web5/conversation-configuration`\n * gateway prefix is prepended to the proto path, hence the repeated segment.\n */\nexport const LIST_ITEMS_ENDPOINT =\n 'https://www.wixapis.com/web5/conversation-configuration/v1/conversation-configuration/list-items';\n\n/** Minimal fetch shape accepted by both the placement `fetchImpl` and the main app's `client.fetchWithAuth`. */\ntype FetchLike = (\n input: string | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\ninterface ListItemsResponse {\n items?: Record<string, unknown>[] | null;\n}\n\nexport interface ListEntityItemsOptions {\n /** Auth-aware fetch (`client.fetchWithAuth` in the main app, the placement's Wix-auth fetch). */\n fetchImpl: FetchLike;\n}\n\n/**\n * POSTs `{ ids }` to the `list-items` endpoint and returns a Map of\n * `doc_id → ApiPayloadItem`. Throws on network/HTTP error so callers keep their\n * own diagnostics; on success returns only the rows the BE resolved.\n */\nexport async function listEntityItems(\n ids: string[],\n opts: ListEntityItemsOptions,\n): Promise<Map<string, ApiPayloadItem>> {\n const result = new Map<string, ApiPayloadItem>();\n if (!ids.length) return result;\n\n const res = await opts.fetchImpl(LIST_ITEMS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ids }),\n });\n\n if (!res.ok) {\n throw new Error(`list-items ${res.status} ${res.statusText}`);\n }\n\n const body = (await res.json()) as ListItemsResponse;\n const items = body.items ?? [];\n for (const item of items) {\n const rec = item as Record<string, unknown>;\n const id = rec.doc_id as string | undefined;\n if (!id) {\n continue;\n }\n // Rows travel as-is apart from the `doc_id` → `id` alias both callers key\n // off, and the image alias below.\n //\n // `url` is deliberately NOT rewritten. It used to be rebuilt as\n // `/products/<handle>` for product rows to keep the link same-origin, which\n // threw away the query string — and `?variant=…` is exactly how a resolved\n // variant travels. The client already strips the origin without losing the\n // query: `canonicalizeFeatureProductUrl` (w5-client-feature-com) and\n // `toRelativeUrl` (web50-server-ui/src/utils/linkResolver.ts) both keep\n // `search` and `hash`.\n const normalized: Record<string, unknown> = { ...rec, id };\n // `list-items` product rows carry the image as `thumbnail` / `images[]`\n // (the Vespa doc shape — see `bo-web5-history`'s `ragDocShared.tsx`), but\n // the shared transform (`transformToGenericEntityData`) reads `img`. Alias\n // it so product cards keep their image.\n //\n // This is a bridge, not a contract: rows that already carry `img` — CMS\n // docs, and any row where retrieval overlaid a matched child's\n // presentation fields server-side pass through untouched. Drop the\n // alias once `list-items` emits `img` for product rows too.\n if (normalized.img == null) {\n if (typeof rec.thumbnail === 'string' && rec.thumbnail) {\n normalized.img = rec.thumbnail;\n } else if (\n Array.isArray(rec.images) &&\n typeof rec.images[0] === 'string' &&\n rec.images[0]\n ) {\n normalized.img = rec.images[0];\n }\n }\n result.set(id, normalized as unknown as ApiPayloadItem);\n }\n return result;\n}\n"],"mappings":";;;;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACO,MAAMA,mBAAmB,GAAAC,OAAA,CAAAD,mBAAA,GAC9B,kGAAkG;;AAEpG;;AAeA;AACA;AACA;AACA;AACA;AACO,eAAeE,eAAeA,CACnCC,GAAa,EACbC,IAA4B,EACU;EACtC,MAAMC,MAAM,GAAG,IAAIC,GAAG,CAAyB,CAAC;EAChD,IAAI,CAACH,GAAG,CAACI,MAAM,EAAE,OAAOF,MAAM;EAE9B,MAAMG,GAAG,GAAG,MAAMJ,IAAI,CAACK,SAAS,CAACT,mBAAmB,EAAE;IACpDU,MAAM,EAAE,MAAM;IACdC,OAAO,EAAE;MAAE,cAAc,EAAE;IAAmB,CAAC;IAC/CC,IAAI,EAAEC,IAAI,CAACC,SAAS,CAAC;MAAEX;IAAI,CAAC;EAC9B,CAAC,CAAC;EAEF,IAAI,CAACK,GAAG,CAACO,EAAE,EAAE;IACX,MAAM,IAAIC,KAAK,CAAC,cAAcR,GAAG,CAACS,MAAM,IAAIT,GAAG,CAACU,UAAU,EAAE,CAAC;EAC/D;EAEA,MAAMN,IAAI,GAAI,MAAMJ,GAAG,CAACW,IAAI,CAAC,CAAuB;EACpD,MAAMC,KAAK,GAAGR,IAAI,CAACQ,KAAK,IAAI,EAAE;EAC9B,KAAK,MAAMC,IAAI,IAAID,KAAK,EAAE;IACxB,MAAME,GAAG,GAAGD,IAA+B;IAC3C,MAAME,EAAE,GAAGD,GAAG,CAACE,MAA4B;IAC3C,IAAI,CAACD,EAAE,EAAE;MACP;IACF;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,MAAME,UAAmC,GAAG;MAAE,GAAGH,GAAG;MAAEC;IAAG,CAAC;IAC1D;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,IAAIE,UAAU,CAACC,GAAG,IAAI,IAAI,EAAE;MAC1B,IAAI,OAAOJ,GAAG,CAACK,SAAS,KAAK,QAAQ,IAAIL,GAAG,CAACK,SAAS,EAAE;QACtDF,UAAU,CAACC,GAAG,GAAGJ,GAAG,CAACK,SAAS;MAChC,CAAC,MAAM,IACLC,KAAK,CAACC,OAAO,CAACP,GAAG,CAACQ,MAAM,CAAC,IACzB,OAAOR,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,IACjCR,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC,EACb;QACAL,UAAU,CAACC,GAAG,GAAGJ,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC;MAChC;IACF;IACAzB,MAAM,CAAC0B,GAAG,CAACR,EAAE,EAAEE,UAAuC,CAAC;EACzD;EACA,OAAOpB,MAAM;AACf","ignoreList":[]}
1
+ {"version":3,"names":["_entityPayload","require","LIST_ITEMS_ENDPOINT","exports","listEntityItems","ids","opts","result","Map","length","res","fetchImpl","method","headers","body","JSON","stringify","ok","Error","status","statusText","json","item","items","normalized","normalizeEntityItem","set","id"],"sources":["../../../src/entity/listEntityItems.ts"],"sourcesContent":["import type { ApiPayloadItem } from '../types/entity';\nimport { normalizeEntityItem } from './entityPayload';\n\n/**\n * Entity resolution via the `list-items` RPC on the ConversationConfiguration\n * service.\n *\n * Entities are resolved through `list-items` (POST `{ ids }`). The response is\n * `{ items: [...] }` where each row is keyed by `doc_id`. We normalize to\n * `ApiPayloadItem` keyed by that id (and alias it onto `id`) so both callers\n * get back the same `Map<id, ApiPayloadItem>` contract.\n *\n * TODO(ambassador): the `@wix/ambassador-enterprise-web-five-v1-configuration`\n * `listItems` builder does NOT yet emit the public URL below — as of 1.0.27 it\n * resolves to the internal `/v1/conversation-configuration/list-items` path,\n * which 405s at the www edge (no public mapping). The gateway mapping was added\n * out-of-band (`/web5/conversation-configuration` → service root), giving the\n * URL below. Once an ambassador version ships with that mapping, replace the\n * hand-built request with the builder + `client.fetchWithAuth`, mirroring\n * `web50-server-ui/src/services/backendConfigService.ts`:\n * const options = listItems({ ids })({ isSSR: false, host: 'www.wixapis.com' });\n * const url = new URL(options.url!, 'https://www.wixapis.com');\n */\n\n/**\n * Public URL for the `list-items` RPC on `www.wixapis.com`. The `/web5/conversation-configuration`\n * gateway prefix is prepended to the proto path, hence the repeated segment.\n */\nexport const LIST_ITEMS_ENDPOINT =\n 'https://www.wixapis.com/web5/conversation-configuration/v1/conversation-configuration/list-items';\n\n/** Minimal fetch shape accepted by both the placement `fetchImpl` and the main app's `client.fetchWithAuth`. */\ntype FetchLike = (\n input: string | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\ninterface ListItemsResponse {\n items?: Record<string, unknown>[] | null;\n}\n\nexport interface ListEntityItemsOptions {\n /** Auth-aware fetch (`client.fetchWithAuth` in the main app, the placement's Wix-auth fetch). */\n fetchImpl: FetchLike;\n}\n\n/**\n * POSTs `{ ids }` to the `list-items` endpoint and returns a Map of\n * `doc_id → ApiPayloadItem`. Throws on network/HTTP error so callers keep their\n * own diagnostics; on success returns only the rows the BE resolved.\n */\nexport async function listEntityItems(\n ids: string[],\n opts: ListEntityItemsOptions,\n): Promise<Map<string, ApiPayloadItem>> {\n const result = new Map<string, ApiPayloadItem>();\n if (!ids.length) return result;\n\n const res = await opts.fetchImpl(LIST_ITEMS_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ids }),\n });\n\n if (!res.ok) {\n throw new Error(`list-items ${res.status} ${res.statusText}`);\n }\n\n const body = (await res.json()) as ListItemsResponse;\n for (const item of body.items ?? []) {\n // Same normalization the turn's own entity rows get (`doc_id` → `id`, the\n // `thumbnail`/`images[]` `img` alias): one shape, whichever producer\n // answered. A row with no id at all is skippednothing could key it.\n const normalized = normalizeEntityItem(item);\n if (normalized) {\n result.set(normalized.id, normalized);\n }\n }\n return result;\n}\n"],"mappings":";;;;;AACA,IAAAA,cAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACO,MAAMC,mBAAmB,GAAAC,OAAA,CAAAD,mBAAA,GAC9B,kGAAkG;;AAEpG;;AAeA;AACA;AACA;AACA;AACA;AACO,eAAeE,eAAeA,CACnCC,GAAa,EACbC,IAA4B,EACU;EACtC,MAAMC,MAAM,GAAG,IAAIC,GAAG,CAAyB,CAAC;EAChD,IAAI,CAACH,GAAG,CAACI,MAAM,EAAE,OAAOF,MAAM;EAE9B,MAAMG,GAAG,GAAG,MAAMJ,IAAI,CAACK,SAAS,CAACT,mBAAmB,EAAE;IACpDU,MAAM,EAAE,MAAM;IACdC,OAAO,EAAE;MAAE,cAAc,EAAE;IAAmB,CAAC;IAC/CC,IAAI,EAAEC,IAAI,CAACC,SAAS,CAAC;MAAEX;IAAI,CAAC;EAC9B,CAAC,CAAC;EAEF,IAAI,CAACK,GAAG,CAACO,EAAE,EAAE;IACX,MAAM,IAAIC,KAAK,CAAC,cAAcR,GAAG,CAACS,MAAM,IAAIT,GAAG,CAACU,UAAU,EAAE,CAAC;EAC/D;EAEA,MAAMN,IAAI,GAAI,MAAMJ,GAAG,CAACW,IAAI,CAAC,CAAuB;EACpD,KAAK,MAAMC,IAAI,IAAIR,IAAI,CAACS,KAAK,IAAI,EAAE,EAAE;IACnC;IACA;IACA;IACA,MAAMC,UAAU,GAAG,IAAAC,kCAAmB,EAACH,IAAI,CAAC;IAC5C,IAAIE,UAAU,EAAE;MACdjB,MAAM,CAACmB,GAAG,CAACF,UAAU,CAACG,EAAE,EAAEH,UAAU,CAAC;IACvC;EACF;EACA,OAAOjB,MAAM;AACf","ignoreList":[]}
package/dist/cjs/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  exports.__esModule = true;
4
4
  exports.buildPlacementDependencies = exports.buildImageSearchFilter = exports.bucketOf = exports.backgroundFilter = 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.UserQueryProvider = exports.UserQuery = exports.UnifiedMarkdownParser = exports.UnifiedLink = 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.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.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.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.parseAstToMarkdown = exports.normalizeImageUrl = exports.normalizeIconUrls = exports.nodesToParts = exports.mergeSectionsWithStableReferences = exports.mergeEntityData = exports.mergeClientConfig = 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.isTrustedBundleHost = exports.isThemeDebugEnabled = exports.isTemplatePickerRequested = exports.isSimulationTraffic = exports.isNavigableUrl = exports.isMatchDebugEnabled = exports.isLegacyUrl = exports.isHtmlComment = exports.isHslTriplet = exports.isEntityLink = exports.hslTripletToHex = exports.hslToRgb = exports.hostAliasFor = exports.hexToHslTriplet = exports.hasImage = exports.hasAnalyticsConsent = exports.getTemplateOverride = exports.getSessionId = exports.getResizedImageUrl = exports.getRefreshPromptsExpiry = exports.getPartsByType = exports.getPartByRole = exports.getOrCreateSessionId = exports.getLinks = exports.getIntentFromMarkdown = exports.getImages = exports.getHeading = exports.getForwardStack = exports.getErrorTypeFromStatus = exports.getEntityExtractor = exports.getContextualImageFilename = 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.ensureMinLightness = exports.enrichEntitiesFromPayload = exports.enableRefreshPrompts = exports.disableRefreshPrompts = exports.detectLinkType = exports.deriveRole = exports.deriveLightColor = exports.deriveDarkGradient = exports.deriveDarkColor = exports.defaultExtractor = exports.decodeLinkText = exports.createWixAuthFetch = exports.createSdkRegistry = exports.createPageSection = exports.createFeatureToggleReader = exports.createErrorMarkdown = exports.convertToBlockElementsWithMapping = exports.convertToBlockElements = exports.computeContentBBox = exports.cn = exports.clearForwardStack = exports.buildProbeUrl = 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.tryParseComponent = exports.trimTrailingWhitespace = exports.transformToSolutionEntityData = exports.transformToGenericEntityData = exports.transformToBlogPostEntityData = exports.transformShopifyProduct = exports.transformShopifyEntityToItemData = exports.transformShopifyCollection = exports.transformShopifyArticle = exports.transformSSProductToEntityItemData = exports.toRgb = exports.toMatchedOptions = exports.stripMarkdown = exports.startNewChatId = exports.shouldRefreshPrompts = exports.setMatchDebug = exports.setForwardStack = exports.setChatId = exports.setBackendEnvironment = exports.rgbToHsl = exports.resolveShopifyEntity = exports.resolveShopifyConfig = exports.resolveErrorTemplate = exports.resolveClientBundleUrl = exports.resetMatchDebugCache = exports.resetChatIdForTests = exports.registerEntityExtractor = exports.readProductBackHandoff = exports.readCurrencyCode = exports.pushToForwardStack = exports.pushPromptSubmit = exports.pushLinkClick = exports.pushExit = exports.pushEvent = exports.pushError = exports.pushEntityFiltered = exports.preprocessMarkdown = exports.popFromForwardStack = exports.parseWeb5Url = exports.parseMarkdownToComponents = exports.parseMarkdownToAst = exports.parseEntityLink = void 0;
5
+ exports.normalizeIconUrls = exports.normalizeEntityItem = exports.nodesToParts = exports.mergeSectionsWithStableReferences = exports.mergeEntityData = exports.mergeClientConfig = 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.isTrustedBundleHost = exports.isThemeDebugEnabled = exports.isTemplatePickerRequested = exports.isSimulationTraffic = exports.isNavigableUrl = exports.isMatchDebugEnabled = exports.isLegacyUrl = exports.isHtmlComment = exports.isHslTriplet = exports.isEntityLink = exports.hslTripletToHex = exports.hslToRgb = exports.hostAliasFor = exports.hexToHslTriplet = exports.hasImage = exports.hasAnalyticsConsent = exports.getTemplateOverride = exports.getSessionId = exports.getResizedImageUrl = exports.getRefreshPromptsExpiry = exports.getPartsByType = exports.getPartByRole = exports.getOrCreateSessionId = exports.getLinks = exports.getIntentFromMarkdown = exports.getImages = exports.getHeading = exports.getForwardStack = exports.getErrorTypeFromStatus = exports.getEntityExtractor = exports.getContextualImageFilename = 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.ensureMinLightness = exports.enrichEntitiesFromPayload = exports.enableRefreshPrompts = exports.disableRefreshPrompts = exports.detectLinkType = exports.deriveRole = exports.deriveLightColor = exports.deriveDarkGradient = exports.deriveDarkColor = exports.defaultExtractor = exports.decodeLinkText = exports.createWixAuthFetch = exports.createSdkRegistry = exports.createPageSection = exports.createFeatureToggleReader = exports.createErrorMarkdown = exports.convertToBlockElementsWithMapping = exports.convertToBlockElements = exports.computeContentBBox = exports.cn = exports.clearForwardStack = exports.buildProbeUrl = 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.tryParseComponent = exports.trimTrailingWhitespace = exports.transformToSolutionEntityData = exports.transformToGenericEntityData = exports.transformToBlogPostEntityData = exports.transformShopifyProduct = exports.transformShopifyEntityToItemData = exports.transformShopifyCollection = exports.transformShopifyArticle = exports.transformSSProductToEntityItemData = exports.toRgb = exports.toMatchedOptions = exports.stripMarkdown = exports.startNewChatId = exports.shouldRefreshPrompts = exports.setMatchDebug = exports.setForwardStack = exports.setChatId = exports.setBackendEnvironment = exports.rgbToHsl = exports.resolveShopifyEntity = exports.resolveShopifyConfig = exports.resolveErrorTemplate = exports.resolveClientBundleUrl = exports.resetMatchDebugCache = exports.resetChatIdForTests = exports.registerEntityExtractor = exports.readProductBackHandoff = exports.readCurrencyCode = exports.pushToForwardStack = exports.pushPromptSubmit = exports.pushLinkClick = exports.pushExit = exports.pushEvent = exports.pushError = exports.pushEntityFiltered = exports.preprocessMarkdown = exports.popFromForwardStack = exports.parseWeb5Url = exports.parseMarkdownToComponents = exports.parseMarkdownToAst = exports.parseEntityLink = exports.parseAstToMarkdown = exports.normalizeImageUrl = void 0;
7
7
  var _clients = require("./clients");
8
8
  exports.CLIENT_IDS = _clients.CLIENT_IDS;
9
9
  exports.EXPERIMENT_IDS = _clients.EXPERIMENT_IDS;
@@ -101,6 +101,8 @@ exports.useChips = _ChipsContext.useChips;
101
101
  var _entity = require("./entity");
102
102
  exports.defaultExtractor = _entity.defaultExtractor;
103
103
  exports.enrichEntitiesFromPayload = _entity.enrichEntitiesFromPayload;
104
+ exports.normalizeEntityItem = _entity.normalizeEntityItem;
105
+ exports.entityPayloadFromItems = _entity.entityPayloadFromItems;
104
106
  exports.mergeEntityData = _entity.mergeEntityData;
105
107
  exports.fetchEntityListData = _entity.fetchEntityListData;
106
108
  exports.listEntityItems = _entity.listEntityItems;