@wix/web5-core 1.63.14 → 1.63.16

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 (38) hide show
  1. package/dist/cjs/component/componentDefinitions/TextBlockSectionDefinition.js +36 -32
  2. package/dist/cjs/component/componentDefinitions/TextBlockSectionDefinition.js.map +1 -1
  3. package/dist/cjs/component/componentDefinitions/index.js +5 -4
  4. package/dist/cjs/component/componentDefinitions/index.js.map +1 -1
  5. package/dist/cjs/component/componentDefinitions/parse-utils.js +43 -6
  6. package/dist/cjs/component/componentDefinitions/parse-utils.js.map +1 -1
  7. package/dist/cjs/entity/matchedVariant.js +31 -9
  8. package/dist/cjs/entity/matchedVariant.js.map +1 -1
  9. package/dist/cjs/hooks/useResolveShopifyEntityData.js +6 -0
  10. package/dist/cjs/hooks/useResolveShopifyEntityData.js.map +1 -1
  11. package/dist/cjs/match/astToParts.js +14 -0
  12. package/dist/cjs/match/astToParts.js.map +1 -1
  13. package/dist/cjs/services/shopify/transformShopifyEntity.js +17 -5
  14. package/dist/cjs/services/shopify/transformShopifyEntity.js.map +1 -1
  15. package/dist/esm/component/componentDefinitions/TextBlockSectionDefinition.js +37 -33
  16. package/dist/esm/component/componentDefinitions/TextBlockSectionDefinition.js.map +1 -1
  17. package/dist/esm/component/componentDefinitions/index.js +5 -4
  18. package/dist/esm/component/componentDefinitions/index.js.map +1 -1
  19. package/dist/esm/component/componentDefinitions/parse-utils.js +42 -6
  20. package/dist/esm/component/componentDefinitions/parse-utils.js.map +1 -1
  21. package/dist/esm/entity/matchedVariant.js +31 -9
  22. package/dist/esm/entity/matchedVariant.js.map +1 -1
  23. package/dist/esm/hooks/useResolveShopifyEntityData.js +6 -0
  24. package/dist/esm/hooks/useResolveShopifyEntityData.js.map +1 -1
  25. package/dist/esm/match/astToParts.js +14 -0
  26. package/dist/esm/match/astToParts.js.map +1 -1
  27. package/dist/esm/services/shopify/transformShopifyEntity.js +17 -5
  28. package/dist/esm/services/shopify/transformShopifyEntity.js.map +1 -1
  29. package/dist/types/component/componentDefinitions/TextBlockSectionDefinition.d.ts.map +1 -1
  30. package/dist/types/component/componentDefinitions/index.d.ts.map +1 -1
  31. package/dist/types/component/componentDefinitions/parse-utils.d.ts +21 -0
  32. package/dist/types/component/componentDefinitions/parse-utils.d.ts.map +1 -1
  33. package/dist/types/entity/matchedVariant.d.ts +18 -9
  34. package/dist/types/entity/matchedVariant.d.ts.map +1 -1
  35. package/dist/types/hooks/useResolveShopifyEntityData.d.ts.map +1 -1
  36. package/dist/types/match/astToParts.d.ts.map +1 -1
  37. package/dist/types/services/shopify/transformShopifyEntity.d.ts.map +1 -1
  38. package/package.json +2 -2
@@ -55,10 +55,11 @@ export function createSdkRegistry() {
55
55
  .register(new CalloutSectionDefinition()).register(new HtmlCommentSectionDefinition()).register(new CtaBannerSectionDefinition())
56
56
  // textBlock comes after every definition whose block opens `h2 > t`
57
57
  // (entity, listItems, featureCards, ctaBanner, comparison, …). Its
58
- // trailing callout is optional, so its pattern also matches a bare
59
- // `h2 > t`, and a pattern matches a PREFIX of the block — registered
60
- // above those it would claim the heading + intro paragraph and strand
61
- // the list / links / table that follow in a separate section.
58
+ // `h2 > t+` also matches a bare `h2 > t`, and a pattern matches a PREFIX
59
+ // of the block registered above those it would claim the heading +
60
+ // intro copy and strand the list / links / table that follow in a
61
+ // separate section. It stops before a trailing callout, which Callout
62
+ // (registered above) then claims on the parser's next iteration.
62
63
  .register(new TextBlockSectionDefinition()).register(new FallbackSectionDefinition()).register(new ErrorSectionDefinition());
63
64
  }
64
65
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["ComparisonSectionDefinition","TextBlockSectionDefinition","HeroSectionDefinition","HeroEntitySectionDefinition","KpiSectionDefinition","FeatureCardsSectionDefinition","EntitySectionDefinition","EntityCollectionSectionDefinition","CtaBannerSectionDefinition","CalloutSectionDefinition","NextStepsSectionDefinition","FeatureSection9PlusDefinition","ListItemsSectionDefinition","SkipNodesSectionDefinition","HtmlCommentSectionDefinition","SearchSectionDefinition","ErrorSectionDefinition","FallbackSectionDefinition","ComponentRegistry","createSdkRegistry","register"],"sources":["../../../../src/component/componentDefinitions/index.ts"],"sourcesContent":["export type { SectionDefinition } from '../section-definition';\nexport { ComparisonSectionDefinition } from './ComparisonSectionDefinition';\nexport { TextBlockSectionDefinition } from './TextBlockSectionDefinition';\nexport { HeroSectionDefinition } from './HeroSectionDefinition';\nexport { HeroEntitySectionDefinition } from './HeroEntitySectionDefinition';\nexport { KpiSectionDefinition } from './KpiSectionDefinition';\nexport { FeatureCardsSectionDefinition } from './FeatureCardsSectionDefinition';\nexport { EntitySectionDefinition } from './EntitySectionDefinition';\nexport { EntityCollectionSectionDefinition } from './EntityCollectionSectionDefinition';\nexport { CtaBannerSectionDefinition } from './CtaBannerSectionDefinition';\nexport { CalloutSectionDefinition } from './CalloutSectionDefinition';\nexport { NextStepsSectionDefinition } from './NextStepsSectionDefinition';\nexport { FeatureSection9PlusDefinition } from './FeatureSection9PlusDefinition';\nexport { ListItemsSectionDefinition } from './listItemsSectionDefinition';\nexport { SkipNodesSectionDefinition } from './SkipNodesSectionDefinition';\nexport { HtmlCommentSectionDefinition } from './HtmlCommentSectionDefinition';\nexport { SearchSectionDefinition } from './SearchSectionDefinition';\nexport { ErrorSectionDefinition } from './ErrorSectionDefinition';\nexport { FallbackSectionDefinition } from './FallbackSectionDefinition';\n\nimport { ComponentRegistry } from '../../registry';\nimport { SkipNodesSectionDefinition } from './SkipNodesSectionDefinition';\nimport { SearchSectionDefinition } from './SearchSectionDefinition';\nimport { KpiSectionDefinition } from './KpiSectionDefinition';\nimport { NextStepsSectionDefinition } from './NextStepsSectionDefinition';\nimport { EntitySectionDefinition } from './EntitySectionDefinition';\nimport { EntityCollectionSectionDefinition } from './EntityCollectionSectionDefinition';\nimport { HeroEntitySectionDefinition } from './HeroEntitySectionDefinition';\nimport { HeroSectionDefinition } from './HeroSectionDefinition';\nimport { FeatureCardsSectionDefinition } from './FeatureCardsSectionDefinition';\nimport { FeatureSection9PlusDefinition } from './FeatureSection9PlusDefinition';\nimport { ListItemsSectionDefinition } from './listItemsSectionDefinition';\nimport { ComparisonSectionDefinition } from './ComparisonSectionDefinition';\nimport { HtmlCommentSectionDefinition } from './HtmlCommentSectionDefinition';\nimport { CtaBannerSectionDefinition } from './CtaBannerSectionDefinition';\nimport { CalloutSectionDefinition } from './CalloutSectionDefinition';\nimport { FallbackSectionDefinition } from './FallbackSectionDefinition';\nimport { ErrorSectionDefinition } from './ErrorSectionDefinition';\nimport { TextBlockSectionDefinition } from './TextBlockSectionDefinition';\n\n/**\n * Creates an SDK registry with all section definitions registered in the\n * same priority order as the app registry (createAppRegistry.ts).\n * Registration order determines evaluation priority — first match wins.\n */\nexport function createSdkRegistry(): ComponentRegistry<null> {\n return (\n new ComponentRegistry<null>(null)\n .register(new SkipNodesSectionDefinition())\n .register(new SearchSectionDefinition())\n .register(new KpiSectionDefinition())\n .register(new NextStepsSectionDefinition())\n // Collection entities are claimed by their own definition first — it is a\n // strictly narrower match than EntitySectionDefinition, which would\n // otherwise swallow collection, product and article blocks alike.\n .register(new EntityCollectionSectionDefinition())\n .register(new EntitySectionDefinition())\n .register(new HeroEntitySectionDefinition())\n .register(new HeroSectionDefinition())\n .register(new FeatureCardsSectionDefinition())\n .register(new ListItemsSectionDefinition())\n .register(new FeatureSection9PlusDefinition())\n .register(new ComparisonSectionDefinition())\n // Callout sits above HtmlComment: a callout block opens with its own\n // `<!-- section:callout -->` descriptor, and HtmlComment's bare\n // `html_comment` pattern would match that comment alone. A pattern may\n // match a PREFIX of a block, so HtmlComment would claim the descriptor\n // and strand the prose below it in the fallback, unstyled.\n .register(new CalloutSectionDefinition())\n .register(new HtmlCommentSectionDefinition())\n .register(new CtaBannerSectionDefinition())\n // textBlock comes after every definition whose block opens `h2 > t`\n // (entity, listItems, featureCards, ctaBanner, comparison, …). Its\n // trailing callout is optional, so its pattern also matches a bare\n // `h2 > t`, and a pattern matches a PREFIX of the block — registered\n // above those it would claim the heading + intro paragraph and strand\n // the list / links / table that follow in a separate section.\n .register(new TextBlockSectionDefinition())\n .register(new FallbackSectionDefinition())\n .register(new ErrorSectionDefinition())\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["ComparisonSectionDefinition","TextBlockSectionDefinition","HeroSectionDefinition","HeroEntitySectionDefinition","KpiSectionDefinition","FeatureCardsSectionDefinition","EntitySectionDefinition","EntityCollectionSectionDefinition","CtaBannerSectionDefinition","CalloutSectionDefinition","NextStepsSectionDefinition","FeatureSection9PlusDefinition","ListItemsSectionDefinition","SkipNodesSectionDefinition","HtmlCommentSectionDefinition","SearchSectionDefinition","ErrorSectionDefinition","FallbackSectionDefinition","ComponentRegistry","createSdkRegistry","register"],"sources":["../../../../src/component/componentDefinitions/index.ts"],"sourcesContent":["export type { SectionDefinition } from '../section-definition';\nexport { ComparisonSectionDefinition } from './ComparisonSectionDefinition';\nexport { TextBlockSectionDefinition } from './TextBlockSectionDefinition';\nexport { HeroSectionDefinition } from './HeroSectionDefinition';\nexport { HeroEntitySectionDefinition } from './HeroEntitySectionDefinition';\nexport { KpiSectionDefinition } from './KpiSectionDefinition';\nexport { FeatureCardsSectionDefinition } from './FeatureCardsSectionDefinition';\nexport { EntitySectionDefinition } from './EntitySectionDefinition';\nexport { EntityCollectionSectionDefinition } from './EntityCollectionSectionDefinition';\nexport { CtaBannerSectionDefinition } from './CtaBannerSectionDefinition';\nexport { CalloutSectionDefinition } from './CalloutSectionDefinition';\nexport { NextStepsSectionDefinition } from './NextStepsSectionDefinition';\nexport { FeatureSection9PlusDefinition } from './FeatureSection9PlusDefinition';\nexport { ListItemsSectionDefinition } from './listItemsSectionDefinition';\nexport { SkipNodesSectionDefinition } from './SkipNodesSectionDefinition';\nexport { HtmlCommentSectionDefinition } from './HtmlCommentSectionDefinition';\nexport { SearchSectionDefinition } from './SearchSectionDefinition';\nexport { ErrorSectionDefinition } from './ErrorSectionDefinition';\nexport { FallbackSectionDefinition } from './FallbackSectionDefinition';\n\nimport { ComponentRegistry } from '../../registry';\nimport { SkipNodesSectionDefinition } from './SkipNodesSectionDefinition';\nimport { SearchSectionDefinition } from './SearchSectionDefinition';\nimport { KpiSectionDefinition } from './KpiSectionDefinition';\nimport { NextStepsSectionDefinition } from './NextStepsSectionDefinition';\nimport { EntitySectionDefinition } from './EntitySectionDefinition';\nimport { EntityCollectionSectionDefinition } from './EntityCollectionSectionDefinition';\nimport { HeroEntitySectionDefinition } from './HeroEntitySectionDefinition';\nimport { HeroSectionDefinition } from './HeroSectionDefinition';\nimport { FeatureCardsSectionDefinition } from './FeatureCardsSectionDefinition';\nimport { FeatureSection9PlusDefinition } from './FeatureSection9PlusDefinition';\nimport { ListItemsSectionDefinition } from './listItemsSectionDefinition';\nimport { ComparisonSectionDefinition } from './ComparisonSectionDefinition';\nimport { HtmlCommentSectionDefinition } from './HtmlCommentSectionDefinition';\nimport { CtaBannerSectionDefinition } from './CtaBannerSectionDefinition';\nimport { CalloutSectionDefinition } from './CalloutSectionDefinition';\nimport { FallbackSectionDefinition } from './FallbackSectionDefinition';\nimport { ErrorSectionDefinition } from './ErrorSectionDefinition';\nimport { TextBlockSectionDefinition } from './TextBlockSectionDefinition';\n\n/**\n * Creates an SDK registry with all section definitions registered in the\n * same priority order as the app registry (createAppRegistry.ts).\n * Registration order determines evaluation priority — first match wins.\n */\nexport function createSdkRegistry(): ComponentRegistry<null> {\n return (\n new ComponentRegistry<null>(null)\n .register(new SkipNodesSectionDefinition())\n .register(new SearchSectionDefinition())\n .register(new KpiSectionDefinition())\n .register(new NextStepsSectionDefinition())\n // Collection entities are claimed by their own definition first — it is a\n // strictly narrower match than EntitySectionDefinition, which would\n // otherwise swallow collection, product and article blocks alike.\n .register(new EntityCollectionSectionDefinition())\n .register(new EntitySectionDefinition())\n .register(new HeroEntitySectionDefinition())\n .register(new HeroSectionDefinition())\n .register(new FeatureCardsSectionDefinition())\n .register(new ListItemsSectionDefinition())\n .register(new FeatureSection9PlusDefinition())\n .register(new ComparisonSectionDefinition())\n // Callout sits above HtmlComment: a callout block opens with its own\n // `<!-- section:callout -->` descriptor, and HtmlComment's bare\n // `html_comment` pattern would match that comment alone. A pattern may\n // match a PREFIX of a block, so HtmlComment would claim the descriptor\n // and strand the prose below it in the fallback, unstyled.\n .register(new CalloutSectionDefinition())\n .register(new HtmlCommentSectionDefinition())\n .register(new CtaBannerSectionDefinition())\n // textBlock comes after every definition whose block opens `h2 > t`\n // (entity, listItems, featureCards, ctaBanner, comparison, …). Its\n // `h2 > t+` also matches a bare `h2 > t`, and a pattern matches a PREFIX\n // of the block — registered above those it would claim the heading +\n // intro copy and strand the list / links / table that follow in a\n // separate section. It stops before a trailing callout, which Callout\n // (registered above) then claims on the parser's next iteration.\n .register(new TextBlockSectionDefinition())\n .register(new FallbackSectionDefinition())\n .register(new ErrorSectionDefinition())\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -38,6 +38,42 @@ export function bodyText(parts) {
38
38
  return [...textParts, ...richTextParts].map(markdownOrText).filter(Boolean).join('\n\n');
39
39
  }
40
40
 
41
+ /**
42
+ * Body copy as markdown, with inline links intact and paragraph breaks kept.
43
+ *
44
+ * `bodyText` reads text parts only, so a paragraph carrying an inline link —
45
+ * `Try narrowing to [men](web5://ask/…) instead` — arrives as `t + link + t`
46
+ * and comes back as `"Try narrowing to\n\ninstead"`: label and url both
47
+ * deleted, and the sentence broken in two at the gap. That is DL #134's list
48
+ * bug in a different helper, and it is why an ask link in prose never reached
49
+ * the page.
50
+ *
51
+ * `inlinePartsToMarkdown` already rebuilds a run of inline parts with links
52
+ * intact, but it flattens to one line, which is wrong for multi-paragraph
53
+ * copy. So group first: `astToParts` marks every part after the first of a
54
+ * given AST node with `meta.continuesNode`, which is exactly "same paragraph,
55
+ * keep going". Parts without it open a new paragraph.
56
+ *
57
+ * Kept separate from `bodyText` deliberately — the other seven callers hand
58
+ * their result to consumers that do not render markdown, and would show a
59
+ * visitor a raw `[label](url)`.
60
+ */
61
+ export function bodyMarkdown(parts) {
62
+ const paragraphs = [];
63
+ for (const part of parts) {
64
+ var _part$meta;
65
+ if (part.type !== 'text' && part.type !== 'richText' && part.type !== 'link') {
66
+ continue;
67
+ }
68
+ if ((_part$meta = part.meta) != null && _part$meta.continuesNode && paragraphs.length > 0) {
69
+ paragraphs[paragraphs.length - 1].push(part);
70
+ } else {
71
+ paragraphs.push([part]);
72
+ }
73
+ }
74
+ return paragraphs.map(inlinePartsToMarkdown).map(paragraph => paragraph.trim()).filter(Boolean).join('\n\n');
75
+ }
76
+
41
77
  /**
42
78
  * Entity sections may be authored as one paragraph per entity:
43
79
  * `[Product](web5://entity/...) - why it matches`.
@@ -237,14 +273,14 @@ function appendInlineToken(current, token) {
237
273
  */
238
274
  export function inlinePartsToMarkdown(parts) {
239
275
  return parts.reduce((text, part) => {
240
- var _part$meta;
276
+ var _part$meta2;
241
277
  if (part.type === 'link') {
242
278
  var _meta2, _part$content;
243
279
  const href = ((_meta2 = part.meta) == null ? void 0 : _meta2.href) ?? '';
244
280
  const label = ((_part$content = part.content) == null ? void 0 : _part$content.trim()) ?? '';
245
281
  return appendInlineToken(text, href ? `[${label}](${href})` : label);
246
282
  }
247
- return appendInlineToken(text, ((_part$meta = part.meta) == null ? void 0 : _part$meta.markdown) ?? part.content ?? '');
283
+ return appendInlineToken(text, ((_part$meta2 = part.meta) == null ? void 0 : _part$meta2.markdown) ?? part.content ?? '');
248
284
  }, '');
249
285
  }
250
286
  export function extractListItemText(item) {
@@ -268,14 +304,14 @@ export function extractListItemText(item) {
268
304
  */
269
305
  export function partsToMarkdown(sectionParts) {
270
306
  const inline = part => {
271
- var _part$meta2;
272
- return ((_part$meta2 = part.meta) == null ? void 0 : _part$meta2.markdown) ?? part.content;
307
+ var _part$meta3;
308
+ return ((_part$meta3 = part.meta) == null ? void 0 : _part$meta3.markdown) ?? part.content;
273
309
  };
274
310
  const lines = [];
275
311
  for (const part of sectionParts) {
276
312
  if (part.type === 'heading') {
277
- var _part$meta3;
278
- const level = ((_part$meta3 = part.meta) == null ? void 0 : _part$meta3.level) ?? 2;
313
+ var _part$meta4;
314
+ const level = ((_part$meta4 = part.meta) == null ? void 0 : _part$meta4.level) ?? 2;
279
315
  lines.push(`${'#'.repeat(level)} ${inline(part)}`);
280
316
  } else if (part.type === 'text' || part.type === 'richText') {
281
317
  lines.push(inline(part));
@@ -1 +1 @@
1
- {"version":3,"names":["getPartsByType","getHeading","getImages","getLinks","parseWeb5Url","Web5UrlType","isWeb5EntityUrl","str","p","_p$content","content","trim","markdownOrText","_p$meta","meta","markdown","normalizeEntityDescription","description","replace","stripMarkdown","md","headingText","parts","level","heading","headingMarkdown","_meta$markdown","bodyText","textParts","richTextParts","map","filter","Boolean","join","entitySectionBodyText","firstEntityLinkIndex","findIndex","_meta","type","isEntityHref","href","sectionParts","slice","linksToHeroButtons","links","length","lists","flatMap","l","_l$meta","items","item","_item$meta","i","_l$content","id","text","variant","onClick","firstImageSrc","_img$meta","images","img","src","undefined","firstLink","_link$content","link","blockquoteText","bqs","_p$meta2","calloutFromParts","_p$meta3","calloutParts","raw","parsed","parseCalloutTag","kind","listToMarkdown","lines","list","_list$meta","extractListItemText","push","parseEntityUrl","url","trimmedUrl","ENTITY","entityType","entityId","parseTableValue","value","normalized","toLowerCase","parseKpiItem","trimmed","boldMatch","match","title","afterBold","indexOf","appendInlineToken","current","token","test","inlinePartsToMarkdown","reduce","part","_part$meta","_meta2","_part$content","label","_item$meta2","partsToMarkdown","inline","_part$meta2","_part$meta3","repeat","_list$meta2","_item$meta3","toUpperCase","generatePartId","Math","random","toString","extractNestedListItems","itemParts","nestedLists","features","nested","_list$meta3","extractEntityRefs","entities","_list$meta4","_item$meta4","entityLinks","_meta3","_link$content2","_images$","_p$content2","callout","_calloutParts$0$meta","parsed2","linkText","entityUrl","markdownImageUrl","markdownFeatures","topLinks","_l$meta2","some","e","_link$content3","linkIndex","j","_meta4","_p$content3","_meta5","_p$meta4","parsedCallout","_meta6"],"sources":["../../../../src/component/componentDefinitions/parse-utils.ts"],"sourcesContent":["import type {\n Part,\n ListPart,\n LinkPart,\n ListItemPart,\n ImagePart,\n} from '../../parts/parts';\nimport {\n getPartsByType,\n getHeading,\n getImages,\n getLinks,\n} from '../../parts/parts';\nimport { parseWeb5Url } from '../../utils/entityLinkParser';\nimport { Web5UrlType, isWeb5EntityUrl } from '../../types/link-types';\nimport type { Callout } from '../../types/callout';\nimport type { HeroButton } from '../types';\n\nfunction str(p: Part | undefined): string {\n return p?.content?.trim() ?? '';\n}\n\nfunction markdownOrText(p: Part): string {\n return ((p.meta?.markdown as string)?.trim() || str(p)).trim();\n}\n\nfunction normalizeEntityDescription(description: string): string {\n return description\n .replace(/^[\\s\\u2013\\u2014-]+/, '')\n .replace(/^[:\\s]+/, '')\n .trim();\n}\n\n/** Strip basic markdown formatting to get plain text */\nexport function stripMarkdown(md: string): string {\n return md\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/\\*(.+?)\\*/g, '$1')\n .replace(/_(.+?)_/g, '$1')\n .replace(/~~(.+?)~~/g, '$1')\n .replace(/`(.+?)`/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .trim();\n}\n\nexport function headingText(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n return str(heading);\n}\n\n/** Like headingText but preserves inline markdown formatting (bold, italic, etc.) */\nexport function headingMarkdown(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n if (!heading) {\n return '';\n }\n const meta = heading.meta as Record<string, unknown>;\n return (meta?.markdown as string)?.trim() || str(heading);\n}\n\nexport function bodyText(parts: Part[]): string {\n const textParts = getPartsByType<Part>(parts, 'text');\n const richTextParts = getPartsByType<Part>(parts, 'richText');\n return [...textParts, ...richTextParts]\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Entity sections may be authored as one paragraph per entity:\n * `[Product](web5://entity/...) - why it matches`.\n * In that shape, text after the first entity link is item copy rather than\n * section copy, so only keep prose that appears before the first entity link.\n */\nexport function entitySectionBodyText(parts: Part[]): string {\n const firstEntityLinkIndex = parts.findIndex(\n (p) => p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n const sectionParts =\n firstEntityLinkIndex >= 0 ? parts.slice(0, firstEntityLinkIndex) : parts;\n\n return sectionParts\n .filter((p) => p.type === 'text' || p.type === 'richText')\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** Build HeroButton[] from link parts — aligned to HeroSection1Button shape */\nexport function linksToHeroButtons(parts: Part[]): HeroButton[] {\n let links = getLinks(parts);\n if (!links.length) {\n const lists = getPartsByType<ListPart>(parts, 'list');\n links = lists.flatMap((l) =>\n (l.meta?.items ?? []).flatMap((item) =>\n ((item.meta?.parts ?? []) as Part[]).filter(\n (p): p is LinkPart => p.type === 'link',\n ),\n ),\n );\n }\n return links.map(\n (l, i): HeroButton => ({\n id: `btn-${i}`,\n text: l.content?.trim() || 'Learn more',\n variant: i === 0 ? 'primary' : 'secondary',\n onClick: l.meta.href,\n }),\n );\n}\n\n/** Return just the src URL string of the first image part */\nexport function firstImageSrc(parts: Part[]): string | undefined {\n const images = getImages(parts);\n const img = images[0];\n return img?.meta?.src || undefined;\n}\n\n/** Return the first link as { text, href } */\nexport function firstLink(\n parts: Part[],\n): { text: string; href: string } | undefined {\n const links = getLinks(parts);\n const link = links[0];\n if (!link) {\n return undefined;\n }\n return { text: link.content?.trim() || '', href: link.meta.href };\n}\n\n/** Extract text content from blockquote parts, preserving markdown formatting when available */\nexport function blockquoteText(parts: Part[]): string | undefined {\n const bqs = getPartsByType<Part>(parts, 'blockquote');\n if (bqs.length === 0) {\n return undefined;\n }\n const text = bqs\n .map((p) => (p.meta?.markdown as string)?.trim() || str(p))\n .filter(Boolean)\n .join('\\n');\n return text || undefined;\n}\n\n/** Extract first callout from parts as a Callout object */\nexport function calloutFromParts(parts: Part[]): Callout | undefined {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n if (calloutParts.length === 0) {\n return undefined;\n }\n const p = calloutParts[0];\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsed = parseCalloutTag(raw);\n return parsed.text ? { text: parsed.text, kind: parsed.kind } : undefined;\n}\n\n/** Convert list parts to a markdown-like string */\nexport function listToMarkdown(parts: Part[]): string | undefined {\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length === 0) {\n return undefined;\n }\n const lines: string[] = [];\n for (const list of lists) {\n const items = list.meta?.items ?? [];\n for (const item of items) {\n const text = extractListItemText(item);\n if (text) {\n lines.push(`- ${text}`);\n }\n }\n }\n return lines.length > 0 ? lines.join('\\n') : undefined;\n}\n\n/**\n * Parse a web5://entity/ URL into entityType and entityId.\n * Delegates to the central `parseWeb5Url` parser.\n */\nexport function parseEntityUrl(\n url: string,\n): { entityType: string; entityId: string } | null {\n const trimmedUrl = url?.trim();\n if (!trimmedUrl || !isWeb5EntityUrl(trimmedUrl)) {\n return null;\n }\n const parsed = parseWeb5Url(trimmedUrl);\n if (\n parsed?.type !== Web5UrlType.ENTITY ||\n !parsed.entityType ||\n !parsed.entityId\n ) {\n return null;\n }\n return { entityType: parsed.entityType, entityId: parsed.entityId };\n}\n\n/**\n * Parse a table cell value into boolean or string.\n * Ported from web50-server-ui comparisonSectionParser.tsx.\n */\nexport function parseTableValue(value: string): boolean | string {\n const normalized = value.trim().toLowerCase();\n\n if (\n normalized === 'true' ||\n normalized === 'yes' ||\n normalized === '✓' ||\n normalized === 'check' ||\n normalized === 'y'\n ) {\n return true;\n }\n if (\n normalized === 'false' ||\n normalized === 'no' ||\n normalized === '✗' ||\n normalized === 'x' ||\n normalized === 'n' ||\n normalized === ''\n ) {\n return false;\n }\n\n return value.trim();\n}\n\n/**\n * Parse a KPI item from markdown text.\n * The first **bold** segment becomes the title; everything after it (leading \":\" stripped) is the description.\n */\nexport function parseKpiItem(\n markdown: string,\n): { title: string; description: string } | null {\n const trimmed = markdown.trim();\n if (!trimmed) {\n return null;\n }\n const boldMatch = trimmed.match(/\\*\\*(.+?)\\*\\*/);\n if (!boldMatch) {\n return null;\n }\n const title = boldMatch[1].trim();\n const afterBold = trimmed.slice(\n trimmed.indexOf(boldMatch[0]) + boldMatch[0].length,\n );\n const description = afterBold.replace(/^[:\\s]+/, '').trim();\n return { title, description };\n}\n\n/** Join inline tokens the way prose reads: single spaces, none before punctuation. */\nfunction appendInlineToken(current: string, token: string): string {\n const trimmed = token.trim();\n if (!trimmed) {\n return current;\n }\n if (!current) {\n return trimmed;\n }\n if (/^[,.;:!?)]/.test(trimmed)) {\n return `${current}${trimmed}`;\n }\n return `${current} ${trimmed}`;\n}\n\n/**\n * Serialize a run of inline parts back to markdown, preserving links and\n * inline emphasis.\n *\n * Concatenating `part.content` alone silently deletes links: a `link` part\n * keeps its label in `content` and its url in `meta.href`, so a filter that\n * reads only text parts turns `Read the [pricing page](/pricing) first` into\n * `Read the first` — url and label both vanish, with nothing in the output to\n * show anything was lost.\n *\n * `content` is also the *plain* text of a text part — `nodesToParts` strips\n * inline formatting and keeps the original in `meta.markdown` whenever the two\n * differ. Reading `content` therefore drops emphasis the same way it used to\n * drop links: `The **4 oz size** is best` becomes `The 4 oz size is best`.\n * Prefer `meta.markdown`, which is exactly the segment's own markdown.\n */\nexport function inlinePartsToMarkdown(parts: Part[]): string {\n return parts.reduce((text, part) => {\n if (part.type === 'link') {\n const href = (part as LinkPart).meta?.href ?? '';\n const label = part.content?.trim() ?? '';\n return appendInlineToken(text, href ? `[${label}](${href})` : label);\n }\n return appendInlineToken(\n text,\n (part.meta?.markdown as string | undefined) ?? part.content ?? '',\n );\n }, '');\n}\n\nexport function extractListItemText(item: ListItemPart): string {\n if (item.content) {\n return item.content;\n }\n if (item.meta?.parts) {\n return (item.meta.parts as Part[])\n .map((p) => p.content)\n .filter(Boolean)\n .join(' ');\n }\n return '';\n}\n\n/**\n * Reconstruct markdown-like content from parts.\n *\n * `part.content` is the *plain* text — `nodesToParts` strips inline\n * formatting and preserves the original markdown (bold, italic, links) in\n * `part.meta.markdown` when they differ. Prefer it so markdown-rendering\n * consumers (e.g. the fallback section's ReactMarkdown) keep `**bold**`.\n */\nexport function partsToMarkdown(sectionParts: Part[]): string {\n const inline = (part: Part): string =>\n (part.meta?.markdown as string | undefined) ?? part.content;\n const lines: string[] = [];\n for (const part of sectionParts) {\n if (part.type === 'heading') {\n const level = (part.meta as { level: number })?.level ?? 2;\n lines.push(`${'#'.repeat(level)} ${inline(part)}`);\n } else if (part.type === 'text' || part.type === 'richText') {\n lines.push(inline(part));\n } else if (part.type === 'list') {\n const list = part as ListPart;\n for (const item of list.meta?.items ?? []) {\n lines.push(\n `- ${(item.meta?.markdown as string | undefined) ?? extractListItemText(item)}`,\n );\n }\n } else if (part.type === 'blockquote' || part.type === 'callout') {\n lines.push(`> ${inline(part)}`);\n }\n }\n return lines.join('\\n\\n');\n}\n\n/**\n * Extract callout kind tag from blockquote text.\n * Handles [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION] GFM syntax.\n */\nexport function parseCalloutTag(text: string): { text: string; kind?: string } {\n const match = text.match(/^\\[!(\\w+)\\]\\s*(.*)/s);\n if (match) {\n return { kind: match[1].toUpperCase(), text: match[2].trim() };\n }\n return { text: text.trim() };\n}\n\n/** Generate a simple unique ID */\nexport function generatePartId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction isEntityHref(href: string | undefined): boolean {\n return !!href && isWeb5EntityUrl(href);\n}\n\nexport interface EntityRef {\n entityId: string;\n entityType: string;\n linkText: string;\n entityUrl: string;\n description: string;\n markdownImageUrl?: string;\n markdownFeatures?: string[];\n callout?: Callout;\n}\n\n/**\n * Extract text from nested list items within a list item's parts.\n */\nfunction extractNestedListItems(itemParts: Part[]): string[] {\n const nestedLists = itemParts.filter((p): p is ListPart => p.type === 'list');\n const features: string[] = [];\n for (const list of nestedLists) {\n for (const nested of list.meta?.items ?? []) {\n const text = extractListItemText(nested);\n if (text) {\n features.push(text);\n }\n }\n }\n return features;\n}\n\n/**\n * Extract entity references from list items containing web5://entity/ links.\n * Also extracts markdownFeatures from nested list items within each entity item.\n */\nexport function extractEntityRefs(parts: Part[]): EntityRef[] {\n const entities: EntityRef[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const entityLinks = itemParts.filter(\n (p): p is LinkPart =>\n p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n\n for (const link of entityLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed) {\n const images = itemParts.filter(\n (p): p is ImagePart => p.type === 'image',\n );\n const textParts = itemParts.filter(\n (p) => p.type === 'text' && p.content?.trim(),\n );\n const description = normalizeEntityDescription(\n textParts.map(markdownOrText).join(' '),\n );\n const features = extractNestedListItems(itemParts);\n const calloutParts = itemParts.filter(\n (p) => p.type === 'callout' || p.type === 'blockquote',\n );\n let callout: Callout | undefined;\n if (calloutParts.length > 0) {\n const raw =\n (calloutParts[0].meta?.markdown as string)?.trim() ||\n str(calloutParts[0]);\n const parsed2 = parseCalloutTag(raw);\n if (parsed2.text) {\n callout = { text: parsed2.text, kind: parsed2.kind };\n }\n }\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description,\n markdownImageUrl: images[0]?.meta?.src,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n }\n }\n\n // Also check top-level links (with surrounding context for description/image/list)\n const topLinks = getLinks(parts).filter((l) => isEntityHref(l.meta?.href));\n for (const link of topLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed && !entities.some((e) => e.entityId === parsed.entityId)) {\n // Look for description text and image after this link in the parts array\n const linkIndex = parts.indexOf(link);\n let description = '';\n let markdownImageUrl: string | undefined;\n let callout: Callout | undefined;\n const features: string[] = [];\n\n if (linkIndex >= 0) {\n // Scan following parts for description, image, callout, and list items\n for (let j = linkIndex + 1; j < parts.length; j++) {\n const p = parts[j];\n // Stop at next entity link\n if (p.type === 'link' && isEntityHref((p as LinkPart).meta?.href)) {\n break;\n }\n if (p.type === 'text' && p.content?.trim()) {\n const text = markdownOrText(p);\n description = description\n ? `${description} ${text}`\n : text;\n } else if (p.type === 'image' && !markdownImageUrl) {\n markdownImageUrl = (p as ImagePart).meta?.src;\n } else if (\n (p.type === 'callout' || p.type === 'blockquote') &&\n !callout\n ) {\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsedCallout = parseCalloutTag(raw);\n if (parsedCallout.text) {\n callout = { text: parsedCallout.text, kind: parsedCallout.kind };\n }\n } else if (p.type === 'list') {\n for (const item of (p as ListPart).meta?.items ?? []) {\n const text = extractListItemText(item);\n if (text) {\n features.push(text);\n }\n }\n }\n }\n }\n\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description: normalizeEntityDescription(description),\n markdownImageUrl,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n\n return entities;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["getPartsByType","getHeading","getImages","getLinks","parseWeb5Url","Web5UrlType","isWeb5EntityUrl","str","p","_p$content","content","trim","markdownOrText","_p$meta","meta","markdown","normalizeEntityDescription","description","replace","stripMarkdown","md","headingText","parts","level","heading","headingMarkdown","_meta$markdown","bodyText","textParts","richTextParts","map","filter","Boolean","join","bodyMarkdown","paragraphs","part","_part$meta","type","continuesNode","length","push","inlinePartsToMarkdown","paragraph","entitySectionBodyText","firstEntityLinkIndex","findIndex","_meta","isEntityHref","href","sectionParts","slice","linksToHeroButtons","links","lists","flatMap","l","_l$meta","items","item","_item$meta","i","_l$content","id","text","variant","onClick","firstImageSrc","_img$meta","images","img","src","undefined","firstLink","_link$content","link","blockquoteText","bqs","_p$meta2","calloutFromParts","_p$meta3","calloutParts","raw","parsed","parseCalloutTag","kind","listToMarkdown","lines","list","_list$meta","extractListItemText","parseEntityUrl","url","trimmedUrl","ENTITY","entityType","entityId","parseTableValue","value","normalized","toLowerCase","parseKpiItem","trimmed","boldMatch","match","title","afterBold","indexOf","appendInlineToken","current","token","test","reduce","_part$meta2","_meta2","_part$content","label","_item$meta2","partsToMarkdown","inline","_part$meta3","_part$meta4","repeat","_list$meta2","_item$meta3","toUpperCase","generatePartId","Math","random","toString","extractNestedListItems","itemParts","nestedLists","features","nested","_list$meta3","extractEntityRefs","entities","_list$meta4","_item$meta4","entityLinks","_meta3","_link$content2","_images$","_p$content2","callout","_calloutParts$0$meta","parsed2","linkText","entityUrl","markdownImageUrl","markdownFeatures","topLinks","_l$meta2","some","e","_link$content3","linkIndex","j","_meta4","_p$content3","_meta5","_p$meta4","parsedCallout","_meta6"],"sources":["../../../../src/component/componentDefinitions/parse-utils.ts"],"sourcesContent":["import type {\n Part,\n ListPart,\n LinkPart,\n ListItemPart,\n ImagePart,\n} from '../../parts/parts';\nimport {\n getPartsByType,\n getHeading,\n getImages,\n getLinks,\n} from '../../parts/parts';\nimport { parseWeb5Url } from '../../utils/entityLinkParser';\nimport { Web5UrlType, isWeb5EntityUrl } from '../../types/link-types';\nimport type { Callout } from '../../types/callout';\nimport type { HeroButton } from '../types';\n\nfunction str(p: Part | undefined): string {\n return p?.content?.trim() ?? '';\n}\n\nfunction markdownOrText(p: Part): string {\n return ((p.meta?.markdown as string)?.trim() || str(p)).trim();\n}\n\nfunction normalizeEntityDescription(description: string): string {\n return description\n .replace(/^[\\s\\u2013\\u2014-]+/, '')\n .replace(/^[:\\s]+/, '')\n .trim();\n}\n\n/** Strip basic markdown formatting to get plain text */\nexport function stripMarkdown(md: string): string {\n return md\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/\\*(.+?)\\*/g, '$1')\n .replace(/_(.+?)_/g, '$1')\n .replace(/~~(.+?)~~/g, '$1')\n .replace(/`(.+?)`/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .trim();\n}\n\nexport function headingText(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n return str(heading);\n}\n\n/** Like headingText but preserves inline markdown formatting (bold, italic, etc.) */\nexport function headingMarkdown(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n if (!heading) {\n return '';\n }\n const meta = heading.meta as Record<string, unknown>;\n return (meta?.markdown as string)?.trim() || str(heading);\n}\n\nexport function bodyText(parts: Part[]): string {\n const textParts = getPartsByType<Part>(parts, 'text');\n const richTextParts = getPartsByType<Part>(parts, 'richText');\n return [...textParts, ...richTextParts]\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Body copy as markdown, with inline links intact and paragraph breaks kept.\n *\n * `bodyText` reads text parts only, so a paragraph carrying an inline link —\n * `Try narrowing to [men](web5://ask/…) instead` — arrives as `t + link + t`\n * and comes back as `\"Try narrowing to\\n\\ninstead\"`: label and url both\n * deleted, and the sentence broken in two at the gap. That is DL #134's list\n * bug in a different helper, and it is why an ask link in prose never reached\n * the page.\n *\n * `inlinePartsToMarkdown` already rebuilds a run of inline parts with links\n * intact, but it flattens to one line, which is wrong for multi-paragraph\n * copy. So group first: `astToParts` marks every part after the first of a\n * given AST node with `meta.continuesNode`, which is exactly \"same paragraph,\n * keep going\". Parts without it open a new paragraph.\n *\n * Kept separate from `bodyText` deliberately — the other seven callers hand\n * their result to consumers that do not render markdown, and would show a\n * visitor a raw `[label](url)`.\n */\nexport function bodyMarkdown(parts: Part[]): string {\n const paragraphs: Part[][] = [];\n\n for (const part of parts) {\n if (\n part.type !== 'text' &&\n part.type !== 'richText' &&\n part.type !== 'link'\n ) {\n continue;\n }\n if (part.meta?.continuesNode && paragraphs.length > 0) {\n paragraphs[paragraphs.length - 1].push(part);\n } else {\n paragraphs.push([part]);\n }\n }\n\n return paragraphs\n .map(inlinePartsToMarkdown)\n .map((paragraph) => paragraph.trim())\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Entity sections may be authored as one paragraph per entity:\n * `[Product](web5://entity/...) - why it matches`.\n * In that shape, text after the first entity link is item copy rather than\n * section copy, so only keep prose that appears before the first entity link.\n */\nexport function entitySectionBodyText(parts: Part[]): string {\n const firstEntityLinkIndex = parts.findIndex(\n (p) => p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n const sectionParts =\n firstEntityLinkIndex >= 0 ? parts.slice(0, firstEntityLinkIndex) : parts;\n\n return sectionParts\n .filter((p) => p.type === 'text' || p.type === 'richText')\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** Build HeroButton[] from link parts — aligned to HeroSection1Button shape */\nexport function linksToHeroButtons(parts: Part[]): HeroButton[] {\n let links = getLinks(parts);\n if (!links.length) {\n const lists = getPartsByType<ListPart>(parts, 'list');\n links = lists.flatMap((l) =>\n (l.meta?.items ?? []).flatMap((item) =>\n ((item.meta?.parts ?? []) as Part[]).filter(\n (p): p is LinkPart => p.type === 'link',\n ),\n ),\n );\n }\n return links.map(\n (l, i): HeroButton => ({\n id: `btn-${i}`,\n text: l.content?.trim() || 'Learn more',\n variant: i === 0 ? 'primary' : 'secondary',\n onClick: l.meta.href,\n }),\n );\n}\n\n/** Return just the src URL string of the first image part */\nexport function firstImageSrc(parts: Part[]): string | undefined {\n const images = getImages(parts);\n const img = images[0];\n return img?.meta?.src || undefined;\n}\n\n/** Return the first link as { text, href } */\nexport function firstLink(\n parts: Part[],\n): { text: string; href: string } | undefined {\n const links = getLinks(parts);\n const link = links[0];\n if (!link) {\n return undefined;\n }\n return { text: link.content?.trim() || '', href: link.meta.href };\n}\n\n/** Extract text content from blockquote parts, preserving markdown formatting when available */\nexport function blockquoteText(parts: Part[]): string | undefined {\n const bqs = getPartsByType<Part>(parts, 'blockquote');\n if (bqs.length === 0) {\n return undefined;\n }\n const text = bqs\n .map((p) => (p.meta?.markdown as string)?.trim() || str(p))\n .filter(Boolean)\n .join('\\n');\n return text || undefined;\n}\n\n/** Extract first callout from parts as a Callout object */\nexport function calloutFromParts(parts: Part[]): Callout | undefined {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n if (calloutParts.length === 0) {\n return undefined;\n }\n const p = calloutParts[0];\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsed = parseCalloutTag(raw);\n return parsed.text ? { text: parsed.text, kind: parsed.kind } : undefined;\n}\n\n/** Convert list parts to a markdown-like string */\nexport function listToMarkdown(parts: Part[]): string | undefined {\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length === 0) {\n return undefined;\n }\n const lines: string[] = [];\n for (const list of lists) {\n const items = list.meta?.items ?? [];\n for (const item of items) {\n const text = extractListItemText(item);\n if (text) {\n lines.push(`- ${text}`);\n }\n }\n }\n return lines.length > 0 ? lines.join('\\n') : undefined;\n}\n\n/**\n * Parse a web5://entity/ URL into entityType and entityId.\n * Delegates to the central `parseWeb5Url` parser.\n */\nexport function parseEntityUrl(\n url: string,\n): { entityType: string; entityId: string } | null {\n const trimmedUrl = url?.trim();\n if (!trimmedUrl || !isWeb5EntityUrl(trimmedUrl)) {\n return null;\n }\n const parsed = parseWeb5Url(trimmedUrl);\n if (\n parsed?.type !== Web5UrlType.ENTITY ||\n !parsed.entityType ||\n !parsed.entityId\n ) {\n return null;\n }\n return { entityType: parsed.entityType, entityId: parsed.entityId };\n}\n\n/**\n * Parse a table cell value into boolean or string.\n * Ported from web50-server-ui comparisonSectionParser.tsx.\n */\nexport function parseTableValue(value: string): boolean | string {\n const normalized = value.trim().toLowerCase();\n\n if (\n normalized === 'true' ||\n normalized === 'yes' ||\n normalized === '✓' ||\n normalized === 'check' ||\n normalized === 'y'\n ) {\n return true;\n }\n if (\n normalized === 'false' ||\n normalized === 'no' ||\n normalized === '✗' ||\n normalized === 'x' ||\n normalized === 'n' ||\n normalized === ''\n ) {\n return false;\n }\n\n return value.trim();\n}\n\n/**\n * Parse a KPI item from markdown text.\n * The first **bold** segment becomes the title; everything after it (leading \":\" stripped) is the description.\n */\nexport function parseKpiItem(\n markdown: string,\n): { title: string; description: string } | null {\n const trimmed = markdown.trim();\n if (!trimmed) {\n return null;\n }\n const boldMatch = trimmed.match(/\\*\\*(.+?)\\*\\*/);\n if (!boldMatch) {\n return null;\n }\n const title = boldMatch[1].trim();\n const afterBold = trimmed.slice(\n trimmed.indexOf(boldMatch[0]) + boldMatch[0].length,\n );\n const description = afterBold.replace(/^[:\\s]+/, '').trim();\n return { title, description };\n}\n\n/** Join inline tokens the way prose reads: single spaces, none before punctuation. */\nfunction appendInlineToken(current: string, token: string): string {\n const trimmed = token.trim();\n if (!trimmed) {\n return current;\n }\n if (!current) {\n return trimmed;\n }\n if (/^[,.;:!?)]/.test(trimmed)) {\n return `${current}${trimmed}`;\n }\n return `${current} ${trimmed}`;\n}\n\n/**\n * Serialize a run of inline parts back to markdown, preserving links and\n * inline emphasis.\n *\n * Concatenating `part.content` alone silently deletes links: a `link` part\n * keeps its label in `content` and its url in `meta.href`, so a filter that\n * reads only text parts turns `Read the [pricing page](/pricing) first` into\n * `Read the first` — url and label both vanish, with nothing in the output to\n * show anything was lost.\n *\n * `content` is also the *plain* text of a text part — `nodesToParts` strips\n * inline formatting and keeps the original in `meta.markdown` whenever the two\n * differ. Reading `content` therefore drops emphasis the same way it used to\n * drop links: `The **4 oz size** is best` becomes `The 4 oz size is best`.\n * Prefer `meta.markdown`, which is exactly the segment's own markdown.\n */\nexport function inlinePartsToMarkdown(parts: Part[]): string {\n return parts.reduce((text, part) => {\n if (part.type === 'link') {\n const href = (part as LinkPart).meta?.href ?? '';\n const label = part.content?.trim() ?? '';\n return appendInlineToken(text, href ? `[${label}](${href})` : label);\n }\n return appendInlineToken(\n text,\n (part.meta?.markdown as string | undefined) ?? part.content ?? '',\n );\n }, '');\n}\n\nexport function extractListItemText(item: ListItemPart): string {\n if (item.content) {\n return item.content;\n }\n if (item.meta?.parts) {\n return (item.meta.parts as Part[])\n .map((p) => p.content)\n .filter(Boolean)\n .join(' ');\n }\n return '';\n}\n\n/**\n * Reconstruct markdown-like content from parts.\n *\n * `part.content` is the *plain* text — `nodesToParts` strips inline\n * formatting and preserves the original markdown (bold, italic, links) in\n * `part.meta.markdown` when they differ. Prefer it so markdown-rendering\n * consumers (e.g. the fallback section's ReactMarkdown) keep `**bold**`.\n */\nexport function partsToMarkdown(sectionParts: Part[]): string {\n const inline = (part: Part): string =>\n (part.meta?.markdown as string | undefined) ?? part.content;\n const lines: string[] = [];\n for (const part of sectionParts) {\n if (part.type === 'heading') {\n const level = (part.meta as { level: number })?.level ?? 2;\n lines.push(`${'#'.repeat(level)} ${inline(part)}`);\n } else if (part.type === 'text' || part.type === 'richText') {\n lines.push(inline(part));\n } else if (part.type === 'list') {\n const list = part as ListPart;\n for (const item of list.meta?.items ?? []) {\n lines.push(\n `- ${(item.meta?.markdown as string | undefined) ?? extractListItemText(item)}`,\n );\n }\n } else if (part.type === 'blockquote' || part.type === 'callout') {\n lines.push(`> ${inline(part)}`);\n }\n }\n return lines.join('\\n\\n');\n}\n\n/**\n * Extract callout kind tag from blockquote text.\n * Handles [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION] GFM syntax.\n */\nexport function parseCalloutTag(text: string): { text: string; kind?: string } {\n const match = text.match(/^\\[!(\\w+)\\]\\s*(.*)/s);\n if (match) {\n return { kind: match[1].toUpperCase(), text: match[2].trim() };\n }\n return { text: text.trim() };\n}\n\n/** Generate a simple unique ID */\nexport function generatePartId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction isEntityHref(href: string | undefined): boolean {\n return !!href && isWeb5EntityUrl(href);\n}\n\nexport interface EntityRef {\n entityId: string;\n entityType: string;\n linkText: string;\n entityUrl: string;\n description: string;\n markdownImageUrl?: string;\n markdownFeatures?: string[];\n callout?: Callout;\n}\n\n/**\n * Extract text from nested list items within a list item's parts.\n */\nfunction extractNestedListItems(itemParts: Part[]): string[] {\n const nestedLists = itemParts.filter((p): p is ListPart => p.type === 'list');\n const features: string[] = [];\n for (const list of nestedLists) {\n for (const nested of list.meta?.items ?? []) {\n const text = extractListItemText(nested);\n if (text) {\n features.push(text);\n }\n }\n }\n return features;\n}\n\n/**\n * Extract entity references from list items containing web5://entity/ links.\n * Also extracts markdownFeatures from nested list items within each entity item.\n */\nexport function extractEntityRefs(parts: Part[]): EntityRef[] {\n const entities: EntityRef[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const entityLinks = itemParts.filter(\n (p): p is LinkPart =>\n p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n\n for (const link of entityLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed) {\n const images = itemParts.filter(\n (p): p is ImagePart => p.type === 'image',\n );\n const textParts = itemParts.filter(\n (p) => p.type === 'text' && p.content?.trim(),\n );\n const description = normalizeEntityDescription(\n textParts.map(markdownOrText).join(' '),\n );\n const features = extractNestedListItems(itemParts);\n const calloutParts = itemParts.filter(\n (p) => p.type === 'callout' || p.type === 'blockquote',\n );\n let callout: Callout | undefined;\n if (calloutParts.length > 0) {\n const raw =\n (calloutParts[0].meta?.markdown as string)?.trim() ||\n str(calloutParts[0]);\n const parsed2 = parseCalloutTag(raw);\n if (parsed2.text) {\n callout = { text: parsed2.text, kind: parsed2.kind };\n }\n }\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description,\n markdownImageUrl: images[0]?.meta?.src,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n }\n }\n\n // Also check top-level links (with surrounding context for description/image/list)\n const topLinks = getLinks(parts).filter((l) => isEntityHref(l.meta?.href));\n for (const link of topLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed && !entities.some((e) => e.entityId === parsed.entityId)) {\n // Look for description text and image after this link in the parts array\n const linkIndex = parts.indexOf(link);\n let description = '';\n let markdownImageUrl: string | undefined;\n let callout: Callout | undefined;\n const features: string[] = [];\n\n if (linkIndex >= 0) {\n // Scan following parts for description, image, callout, and list items\n for (let j = linkIndex + 1; j < parts.length; j++) {\n const p = parts[j];\n // Stop at next entity link\n if (p.type === 'link' && isEntityHref((p as LinkPart).meta?.href)) {\n break;\n }\n if (p.type === 'text' && p.content?.trim()) {\n const text = markdownOrText(p);\n description = description\n ? `${description} ${text}`\n : text;\n } else if (p.type === 'image' && !markdownImageUrl) {\n markdownImageUrl = (p as ImagePart).meta?.src;\n } else if (\n (p.type === 'callout' || p.type === 'blockquote') &&\n !callout\n ) {\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsedCallout = parseCalloutTag(raw);\n if (parsedCallout.text) {\n callout = { text: parsedCallout.text, kind: parsedCallout.kind };\n }\n } else if (p.type === 'list') {\n for (const item of (p as ListPart).meta?.items ?? []) {\n const text = extractListItemText(item);\n if (text) {\n features.push(text);\n }\n }\n }\n }\n }\n\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description: normalizeEntityDescription(description),\n markdownImageUrl,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n\n return entities;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -118,19 +118,38 @@ export function formatPriceField(value, currency) {
118
118
 
119
119
  /** The already-formatted price fields a card reads. */
120
120
 
121
+ /**
122
+ * Whether this row stands for one variant rather than for the whole product.
123
+ *
124
+ * Either mark is enough. `variantId` is the machine answer and `matchedOptions`
125
+ * the human one, and a row can arrive with only one of them: retrieval that
126
+ * resolved a document down to a child sends the pairs, while a row whose
127
+ * identity came from the `?variant=` on its own URL has only the id. Both mean
128
+ * the same thing — the card names a child, so the card must price that child.
129
+ */
130
+ function namesOneVariant(fields) {
131
+ var _fields$matchedOption;
132
+ return Boolean(fields.variantId) || Boolean((_fields$matchedOption = fields.matchedOptions) == null ? void 0 : _fields$matchedOption.length);
133
+ }
134
+
121
135
  /**
122
136
  * What a product card should print as its price.
123
137
  *
124
- * `price` belongs to the single variant the search matched the same variant
125
- * the card's image and link point at. `priceMin`/`priceMax` describe the whole
126
- * product. When those two differ the product has no one price, and printing
127
- * the matched variant's number on its own would pass it off as the product's;
128
- * printing it after "from" would be worse still, because "from" is a claim
129
- * about the floor of the span and the matched variant is not always the
130
- * cheapest one. So a span prints its own floor, prefixed.
138
+ * A card that names a variant prices THAT variant. `price` belongs to the
139
+ * single child the search resolved to the same child the card's image, its
140
+ * `?variant=` link and the pairs printed above the price all describe so it
141
+ * is the only number that can follow them without contradicting them. "from"
142
+ * is wrong there twice over: it claims a range where the card named one thing,
143
+ * and the floor it points at is a different child's price wearing this one's
144
+ * name. That is what shipped: five colourways of one shoe, each naming its own
145
+ * colour and each printing the cheapest colour's price.
146
+ *
147
+ * `priceMin`/`priceMax` describe the whole product, and they are the answer
148
+ * only when the row IS the whole product. Then the span's floor prints
149
+ * prefixed, because no single number can stand for a product that has several.
131
150
  *
132
- * With no span — the two ends equal, or only one of them known — nothing
133
- * changes: the matched variant's price is printed as it always was.
151
+ * With no span — the ends equal, or only one of them known — the row's own
152
+ * price prints as it always did.
134
153
  */
135
154
  export function formatProductPriceLabel(fields) {
136
155
  const {
@@ -138,6 +157,9 @@ export function formatProductPriceLabel(fields) {
138
157
  priceMin,
139
158
  priceMax
140
159
  } = fields;
160
+ if (price && namesOneVariant(fields)) {
161
+ return price;
162
+ }
141
163
  if (priceMin && priceMax && priceMin !== priceMax) {
142
164
  return `from ${priceMin}`;
143
165
  }
@@ -1 +1 @@
1
- {"version":3,"names":["readText","value","trim","Number","isFinite","String","toMatchedOptions","raw","Array","isArray","undefined","pairs","rawName","rawValue","Object","entries","name","push","length","formatMoney","amount","currency","Intl","NumberFormat","style","minimumFractionDigits","isInteger","format","readCurrencyCode","_len","arguments","candidates","_key","candidate","code","toUpperCase","test","formatPriceField","formatProductPriceLabel","fields","price","priceMin","priceMax"],"sources":["../../../src/entity/matchedVariant.ts"],"sourcesContent":["/**\n * Presentation helpers for a document that retrieval resolved down to one\n * child.\n *\n * When a query filters on an attribute that lives on the child rather than on\n * the parent, retrieval overlays the matching child's presentation fields onto\n * the parent document before it reaches us: `url`, `img` and `price` already\n * describe the child, while `title`, `description` and `doc_id` stay the\n * parent's. Two fields need shaping before a card can print them, and both\n * live here:\n *\n * - `options` — the child's own `{ name: value }` pairs.\n * - `priceMin` / `priceMax` — the parent's span, against which the child's\n * single `price` has to be read.\n *\n * The overlay itself is done server-side and is deliberately NOT repeated\n * here.\n */\n\n/** One `{ name: value }` pair, exactly as the store named it. */\nexport interface MatchedOption {\n name: string;\n value: string;\n}\n\nfunction readText(value: unknown): string {\n if (typeof value === 'string') {\n return value.trim();\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return String(value);\n }\n return '';\n}\n\n/**\n * Turn a document's raw `options` map into an ordered list of pairs.\n *\n * Every entry the store put in the map is kept, under the store's own name and\n * in the store's own order. Nothing here knows — or may learn — what any\n * particular name means: one catalogue's pairs describe a garment, the next\n * one's a power tool, and both have to come out identical. Entries with an\n * empty name or an empty/non-printable value are dropped, since a card can do\n * nothing with them.\n *\n * Returns `undefined` (not `[]`) when there is nothing to show, so callers can\n * treat it like every other optional field on the entity.\n */\nexport function toMatchedOptions(raw: unknown): MatchedOption[] | undefined {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return undefined;\n }\n\n const pairs: MatchedOption[] = [];\n for (const [rawName, rawValue] of Object.entries(\n raw as Record<string, unknown>,\n )) {\n const name = rawName.trim();\n const value = readText(rawValue);\n if (name && value) {\n pairs.push({ name, value });\n }\n }\n\n return pairs.length ? pairs : undefined;\n}\n\n/**\n * Render one amount in the document's currency.\n *\n * With no currency code on the document the amount is printed bare: assuming\n * \"$\" prints a false fact on every store that doesn't sell in dollars. Whole\n * amounts stay whole (\"107\" not \"107.00\"); a real fractional amount keeps its\n * minor units.\n */\nexport function formatMoney(amount: number, currency?: string): string {\n if (!currency) {\n return String(amount);\n }\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,\n }).format(amount);\n } catch {\n // A well-formed but unknown code: show it rather than dropping it.\n return `${amount} ${currency}`;\n }\n}\n\n/** Normalize a document currency to an ISO-4217 code, or drop it. */\nexport function readCurrencyCode(...candidates: unknown[]): string | undefined {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') {\n continue;\n }\n const code = candidate.trim().toUpperCase();\n if (/^[A-Z]{3}$/.test(code)) {\n return code;\n }\n }\n return undefined;\n}\n\n/**\n * Format one of a document's price fields for display. Numbers are rendered in\n * the document's currency; an already-formatted string is passed through\n * untouched; anything else yields `undefined`.\n */\nexport function formatPriceField(\n value: unknown,\n currency?: string,\n): string | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return formatMoney(value, currency);\n }\n if (typeof value === 'string' && value.trim()) {\n return value.trim();\n }\n return undefined;\n}\n\n/** The already-formatted price fields a card reads. */\nexport interface ProductPriceFields {\n /** Price of the one variant this result matched. */\n price?: string;\n /** Lowest price across the whole product's variants. */\n priceMin?: string;\n /** Highest price across the whole product's variants. */\n priceMax?: string;\n}\n\n/**\n * What a product card should print as its price.\n *\n * `price` belongs to the single variant the search matched — the same variant\n * the card's image and link point at. `priceMin`/`priceMax` describe the whole\n * product. When those two differ the product has no one price, and printing\n * the matched variant's number on its own would pass it off as the product's;\n * printing it after \"from\" would be worse still, because \"from\" is a claim\n * about the floor of the span and the matched variant is not always the\n * cheapest one. So a span prints its own floor, prefixed.\n *\n * With no span — the two ends equal, or only one of them known — nothing\n * changes: the matched variant's price is printed as it always was.\n */\nexport function formatProductPriceLabel(\n fields: ProductPriceFields,\n): string | undefined {\n const { price, priceMin, priceMax } = fields;\n if (priceMin && priceMax && priceMin !== priceMax) {\n return `from ${priceMin}`;\n }\n return price;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["readText","value","trim","Number","isFinite","String","toMatchedOptions","raw","Array","isArray","undefined","pairs","rawName","rawValue","Object","entries","name","push","length","formatMoney","amount","currency","Intl","NumberFormat","style","minimumFractionDigits","isInteger","format","readCurrencyCode","_len","arguments","candidates","_key","candidate","code","toUpperCase","test","formatPriceField","namesOneVariant","fields","_fields$matchedOption","Boolean","variantId","matchedOptions","formatProductPriceLabel","price","priceMin","priceMax"],"sources":["../../../src/entity/matchedVariant.ts"],"sourcesContent":["/**\n * Presentation helpers for a document that retrieval resolved down to one\n * child.\n *\n * When a query filters on an attribute that lives on the child rather than on\n * the parent, retrieval overlays the matching child's presentation fields onto\n * the parent document before it reaches us: `url`, `img` and `price` already\n * describe the child, while `title`, `description` and `doc_id` stay the\n * parent's. Two fields need shaping before a card can print them, and both\n * live here:\n *\n * - `options` — the child's own `{ name: value }` pairs.\n * - `priceMin` / `priceMax` — the parent's span, against which the child's\n * single `price` has to be read.\n *\n * The overlay itself is done server-side and is deliberately NOT repeated\n * here.\n */\n\n/** One `{ name: value }` pair, exactly as the store named it. */\nexport interface MatchedOption {\n name: string;\n value: string;\n}\n\nfunction readText(value: unknown): string {\n if (typeof value === 'string') {\n return value.trim();\n }\n if (typeof value === 'number' && Number.isFinite(value)) {\n return String(value);\n }\n return '';\n}\n\n/**\n * Turn a document's raw `options` map into an ordered list of pairs.\n *\n * Every entry the store put in the map is kept, under the store's own name and\n * in the store's own order. Nothing here knows — or may learn — what any\n * particular name means: one catalogue's pairs describe a garment, the next\n * one's a power tool, and both have to come out identical. Entries with an\n * empty name or an empty/non-printable value are dropped, since a card can do\n * nothing with them.\n *\n * Returns `undefined` (not `[]`) when there is nothing to show, so callers can\n * treat it like every other optional field on the entity.\n */\nexport function toMatchedOptions(raw: unknown): MatchedOption[] | undefined {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return undefined;\n }\n\n const pairs: MatchedOption[] = [];\n for (const [rawName, rawValue] of Object.entries(\n raw as Record<string, unknown>,\n )) {\n const name = rawName.trim();\n const value = readText(rawValue);\n if (name && value) {\n pairs.push({ name, value });\n }\n }\n\n return pairs.length ? pairs : undefined;\n}\n\n/**\n * Render one amount in the document's currency.\n *\n * With no currency code on the document the amount is printed bare: assuming\n * \"$\" prints a false fact on every store that doesn't sell in dollars. Whole\n * amounts stay whole (\"107\" not \"107.00\"); a real fractional amount keeps its\n * minor units.\n */\nexport function formatMoney(amount: number, currency?: string): string {\n if (!currency) {\n return String(amount);\n }\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency,\n minimumFractionDigits: Number.isInteger(amount) ? 0 : 2,\n }).format(amount);\n } catch {\n // A well-formed but unknown code: show it rather than dropping it.\n return `${amount} ${currency}`;\n }\n}\n\n/** Normalize a document currency to an ISO-4217 code, or drop it. */\nexport function readCurrencyCode(...candidates: unknown[]): string | undefined {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') {\n continue;\n }\n const code = candidate.trim().toUpperCase();\n if (/^[A-Z]{3}$/.test(code)) {\n return code;\n }\n }\n return undefined;\n}\n\n/**\n * Format one of a document's price fields for display. Numbers are rendered in\n * the document's currency; an already-formatted string is passed through\n * untouched; anything else yields `undefined`.\n */\nexport function formatPriceField(\n value: unknown,\n currency?: string,\n): string | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return formatMoney(value, currency);\n }\n if (typeof value === 'string' && value.trim()) {\n return value.trim();\n }\n return undefined;\n}\n\n/** The already-formatted price fields a card reads. */\nexport interface ProductPriceFields {\n /** Price of the one variant this result matched. */\n price?: string;\n /** Lowest price across the whole product's variants. */\n priceMin?: string;\n /** Highest price across the whole product's variants. */\n priceMax?: string;\n /** Id of the variant this row was resolved down to, when there was one. */\n variantId?: string;\n /** The pairs naming that variant — what the card prints above the price. */\n matchedOptions?: MatchedOption[];\n}\n\n/**\n * Whether this row stands for one variant rather than for the whole product.\n *\n * Either mark is enough. `variantId` is the machine answer and `matchedOptions`\n * the human one, and a row can arrive with only one of them: retrieval that\n * resolved a document down to a child sends the pairs, while a row whose\n * identity came from the `?variant=` on its own URL has only the id. Both mean\n * the same thing — the card names a child, so the card must price that child.\n */\nfunction namesOneVariant(fields: ProductPriceFields): boolean {\n return Boolean(fields.variantId) || Boolean(fields.matchedOptions?.length);\n}\n\n/**\n * What a product card should print as its price.\n *\n * A card that names a variant prices THAT variant. `price` belongs to the\n * single child the search resolved to — the same child the card's image, its\n * `?variant=` link and the pairs printed above the price all describe — so it\n * is the only number that can follow them without contradicting them. \"from\"\n * is wrong there twice over: it claims a range where the card named one thing,\n * and the floor it points at is a different child's price wearing this one's\n * name. That is what shipped: five colourways of one shoe, each naming its own\n * colour and each printing the cheapest colour's price.\n *\n * `priceMin`/`priceMax` describe the whole product, and they are the answer\n * only when the row IS the whole product. Then the span's floor prints\n * prefixed, because no single number can stand for a product that has several.\n *\n * With no span — the ends equal, or only one of them known — the row's own\n * price prints as it always did.\n */\nexport function formatProductPriceLabel(\n fields: ProductPriceFields,\n): string | undefined {\n const { price, priceMin, priceMax } = fields;\n if (price && namesOneVariant(fields)) {\n return price;\n }\n if (priceMin && priceMax && priceMin !== priceMax) {\n return `from ${priceMin}`;\n }\n return price;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -101,6 +101,12 @@ export function mergeShopifyEntityData(existing, resolved) {
101
101
  url: existing.url || resolved.url,
102
102
  title: existing.title || resolved.title,
103
103
  imageUrl: existing.imageUrl || resolved.imageUrl,
104
+ // The live fetch withholds a price when it was asked about a variant it
105
+ // could not find, rather than answering with the product's floor. Its
106
+ // silence must not erase the turn's number, which was about the right
107
+ // child; a spread would overwrite with `undefined` and leave the card
108
+ // priceless.
109
+ price: resolved.price ?? existing.price,
104
110
  variantId: existing.variantId || resolved.variantId,
105
111
  matchedOptions: (_existing$matchedOpti = existing.matchedOptions) != null && _existing$matchedOpti.length ? existing.matchedOptions : resolved.matchedOptions
106
112
  };
@@ -1 +1 @@
1
- {"version":3,"names":["useState","useEffect","useMemo","useRef","ShopifyStorefrontClient","resolveShopifyEntity","transformShopifyEntityToItemData","shopifyEntityCache","Map","cacheKey","entityType","entityId","variantId","normalizeShopifyEntityType","lower","toLowerCase","includes","extractHandleFromUrl","url","trimmed","trim","undefined","pathname","test","URL","withoutQuery","split","withoutLeadingSlash","replace","productsIndex","indexOf","slice","length","collectionsIndex","variantIdFromUrl","_URLSearchParams$get","query","URLSearchParams","get","getShopifyLookup","entity","_entity$data","_entity$data2","_entity$data3","urlHandle","data","mergeShopifyEntityData","existing","resolved","_existing$matchedOpti","title","imageUrl","matchedOptions","useResolveShopifyEntityData","entities","shopifyConfig","resolvedMap","setResolvedMap","settled","setSettled","clientRef","configKeyRef","configKey","storeDomain","apiVersion","current","entityIds","map","e","lookup","join","ignore","client","entityLookups","key","toFetch","filter","_ref","has","cached","forEach","_ref2","item","set","fetchAll","Promise","allSettled","_ref3","itemData","finalMap","_ref4","enrichedEntities","loading"],"sources":["../../../src/hooks/useResolveShopifyEntityData.ts"],"sourcesContent":["import { useState, useEffect, useMemo, useRef } from 'react';\nimport { ShopifyStorefrontClient } from '../services/shopify/shopifyStorefrontClient';\nimport { resolveShopifyEntity } from '../services/shopify/shopifyEntityResolver';\nimport { transformShopifyEntityToItemData } from '../services/shopify/transformShopifyEntity';\nimport type { ShopifyStorefrontConfig } from '../services/shopify/types';\nimport type { EntityItemData } from '../component/types';\n\n// Session-level cache: entityType:entityId:variantId → resolved EntityItemData\nconst shopifyEntityCache = new Map<string, EntityItemData>();\n\nfunction cacheKey(\n entityType: string,\n entityId: string,\n variantId?: string,\n): string {\n return `${entityType}:${entityId}:${variantId || ''}`;\n}\n\nfunction normalizeShopifyEntityType(entityType: string): string {\n const lower = entityType.toLowerCase();\n // A variant is not a Storefront resource of its own — there is no\n // `variantByHandle`. It is fetched as its parent product and then priced at\n // the variant the lookup carries alongside, which is why `variant` and\n // `product` normalize to the same query here rather than to two.\n if (lower.includes('product') || lower.includes('variant')) {\n return 'product';\n }\n if (lower.includes('collection')) {\n return 'collection';\n }\n if (lower.includes('article') || lower.includes('blog')) {\n return 'article';\n }\n return entityType;\n}\n\nfunction extractHandleFromUrl(url: string | undefined): string | undefined {\n const trimmed = url?.trim();\n if (!trimmed) {\n return undefined;\n }\n\n let pathname = trimmed;\n try {\n if (/^https?:\\/\\//i.test(trimmed)) {\n pathname = new URL(trimmed).pathname;\n }\n } catch {\n pathname = trimmed;\n }\n\n const withoutQuery = pathname.split(/[?#]/)[0];\n const withoutLeadingSlash = withoutQuery.replace(/^\\/+/, '');\n const productsIndex = withoutLeadingSlash.indexOf('products/');\n if (productsIndex >= 0) {\n return withoutLeadingSlash.slice(productsIndex + 'products/'.length);\n }\n\n const collectionsIndex = withoutLeadingSlash.indexOf('collections/');\n if (collectionsIndex >= 0) {\n return withoutLeadingSlash.slice(\n collectionsIndex + 'collections/'.length,\n );\n }\n\n return withoutLeadingSlash || undefined;\n}\n\n/**\n * The `?variant=` the row's own URL already carries.\n *\n * A row that names a variant — whether the turn overlaid one onto a product or\n * sent the variant as the entity itself — always links to it, because that is\n * the URL a shopper has to land on. So when the row omits `variantId` as a\n * field, the URL still says which child it means, and pricing the parent's\n * default instead would contradict the link on the same card.\n */\nfunction variantIdFromUrl(url: string | undefined): string | undefined {\n const query = url?.split('#')[0].split('?')[1];\n if (!query) {\n return undefined;\n }\n return new URLSearchParams(query).get('variant')?.trim() || undefined;\n}\n\nexport function getShopifyLookup(entity: {\n entityId: string;\n entityType: string;\n data?: EntityItemData;\n}) {\n const entityType = normalizeShopifyEntityType(entity.entityType);\n const urlHandle = extractHandleFromUrl(entity.data?.url);\n const entityId =\n (entityType === 'product' || entityType === 'collection') && urlHandle\n ? urlHandle\n : entity.entityId;\n return {\n entityType,\n entityId,\n variantId: entity.data?.variantId ?? variantIdFromUrl(entity.data?.url),\n };\n}\n\n/**\n * The turn owns WHICH thing to show; the live fetch owns VOLATILE FACTS about\n * it.\n *\n * Only the turn saw the shopper's question, so only it knows that the brown\n * variant was the one asked for — its `title`, `imageUrl`, `url`,\n * `matchedOptions` and `variantId` all describe that one variant and must\n * survive enrichment. Everything the Storefront answers with that can go stale\n * between the turn and the render — `price`, `compareAtPrice`, `available`,\n * `priceMin`, `priceMax` — comes from `resolved`.\n */\nexport function mergeShopifyEntityData(\n existing: EntityItemData | undefined,\n resolved: EntityItemData,\n): EntityItemData {\n if (!existing) {\n return resolved;\n }\n\n return {\n ...existing,\n ...resolved,\n url: existing.url || resolved.url,\n title: existing.title || resolved.title,\n imageUrl: existing.imageUrl || resolved.imageUrl,\n variantId: existing.variantId || resolved.variantId,\n matchedOptions: existing.matchedOptions?.length\n ? existing.matchedOptions\n : resolved.matchedOptions,\n };\n}\n\n/**\n * Shopify-specific implementation of the entity resolution pattern.\n *\n * Fetches entity data from the Shopify Storefront API (tokenless) and\n * transforms it to the standard EntityItemData shape.\n *\n * Designed to be plugged into ComponentDependencies.useResolveGenericEntityData.\n */\nexport function useResolveShopifyEntityData<\n TEntity extends {\n entityId: string;\n entityUrl: string;\n entityType: string;\n data?: EntityItemData;\n },\n>(\n entities: TEntity[],\n shopifyConfig: ShopifyStorefrontConfig,\n): { enrichedEntities: TEntity[]; loading: boolean } {\n const [resolvedMap, setResolvedMap] = useState<Map<string, EntityItemData>>(\n new Map(),\n );\n const [settled, setSettled] = useState(false);\n\n // Stable client reference — recreated only when config changes\n const clientRef = useRef<ShopifyStorefrontClient | null>(null);\n const configKeyRef = useRef('');\n const configKey = `${shopifyConfig.storeDomain}|${\n shopifyConfig.apiVersion || ''\n }`;\n\n if (!clientRef.current || configKeyRef.current !== configKey) {\n clientRef.current = new ShopifyStorefrontClient(shopifyConfig);\n configKeyRef.current = configKey;\n }\n\n const entityIds = useMemo(\n () =>\n entities\n .map((e) => {\n const lookup = getShopifyLookup(e);\n return `${e.entityType}:${e.entityId}:${cacheKey(\n lookup.entityType,\n lookup.entityId,\n lookup.variantId,\n )}`;\n })\n .join(','),\n [entities],\n );\n\n useEffect(() => {\n let ignore = false;\n const client = clientRef.current;\n\n if (!entities.length || !client || !shopifyConfig.storeDomain) {\n setSettled(true);\n return;\n }\n\n const entityLookups = entities.map((entity) => {\n const lookup = getShopifyLookup(entity);\n return {\n entity,\n lookup,\n // The variant is part of the key: the cached entry carries that\n // variant's live price, so two entities on the same product but\n // different variants must not share it.\n key: cacheKey(lookup.entityType, lookup.entityId, lookup.variantId),\n };\n });\n\n // Identify entities that need fetching. Payload data can provide the handle.\n const toFetch = entityLookups.filter(\n ({ key }) => !shopifyEntityCache.has(key),\n );\n\n // If everything is cached, resolve immediately\n if (toFetch.length === 0) {\n const cached = new Map<string, EntityItemData>();\n entityLookups.forEach(({ entity, key }) => {\n const item = shopifyEntityCache.get(key);\n if (item) {\n cached.set(entity.entityId, mergeShopifyEntityData(entity.data, item));\n }\n });\n setResolvedMap(cached);\n setSettled(true);\n return;\n }\n\n setSettled(false);\n\n const fetchAll = async () => {\n await Promise.allSettled(\n toFetch.map(async ({ entity, lookup, key }) => {\n const resolved = await resolveShopifyEntity(\n client,\n lookup.entityType,\n lookup.entityId,\n shopifyConfig,\n );\n if (resolved) {\n const itemData = transformShopifyEntityToItemData(\n resolved,\n lookup.entityType,\n lookup.variantId,\n );\n shopifyEntityCache.set(\n key,\n mergeShopifyEntityData(entity.data, itemData),\n );\n }\n }),\n );\n\n if (ignore) {\n return;\n }\n\n // Build the resolved map from cache\n const finalMap = new Map<string, EntityItemData>();\n entityLookups.forEach(({ entity, key }) => {\n const item = shopifyEntityCache.get(key);\n if (item) {\n finalMap.set(\n entity.entityId,\n mergeShopifyEntityData(entity.data, item),\n );\n }\n });\n\n setResolvedMap(finalMap);\n setSettled(true);\n };\n\n fetchAll();\n return () => {\n ignore = true;\n };\n }, [entityIds, shopifyConfig.storeDomain]);\n\n // Merge resolved data into entities\n const enrichedEntities = useMemo(() => {\n return entities.map((entity) => {\n const resolved = resolvedMap.get(entity.entityId);\n if (resolved) {\n return { ...entity, data: resolved };\n }\n return entity;\n });\n }, [entities, resolvedMap]);\n\n return { enrichedEntities, loading: !settled };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useState","useEffect","useMemo","useRef","ShopifyStorefrontClient","resolveShopifyEntity","transformShopifyEntityToItemData","shopifyEntityCache","Map","cacheKey","entityType","entityId","variantId","normalizeShopifyEntityType","lower","toLowerCase","includes","extractHandleFromUrl","url","trimmed","trim","undefined","pathname","test","URL","withoutQuery","split","withoutLeadingSlash","replace","productsIndex","indexOf","slice","length","collectionsIndex","variantIdFromUrl","_URLSearchParams$get","query","URLSearchParams","get","getShopifyLookup","entity","_entity$data","_entity$data2","_entity$data3","urlHandle","data","mergeShopifyEntityData","existing","resolved","_existing$matchedOpti","title","imageUrl","price","matchedOptions","useResolveShopifyEntityData","entities","shopifyConfig","resolvedMap","setResolvedMap","settled","setSettled","clientRef","configKeyRef","configKey","storeDomain","apiVersion","current","entityIds","map","e","lookup","join","ignore","client","entityLookups","key","toFetch","filter","_ref","has","cached","forEach","_ref2","item","set","fetchAll","Promise","allSettled","_ref3","itemData","finalMap","_ref4","enrichedEntities","loading"],"sources":["../../../src/hooks/useResolveShopifyEntityData.ts"],"sourcesContent":["import { useState, useEffect, useMemo, useRef } from 'react';\nimport { ShopifyStorefrontClient } from '../services/shopify/shopifyStorefrontClient';\nimport { resolveShopifyEntity } from '../services/shopify/shopifyEntityResolver';\nimport { transformShopifyEntityToItemData } from '../services/shopify/transformShopifyEntity';\nimport type { ShopifyStorefrontConfig } from '../services/shopify/types';\nimport type { EntityItemData } from '../component/types';\n\n// Session-level cache: entityType:entityId:variantId → resolved EntityItemData\nconst shopifyEntityCache = new Map<string, EntityItemData>();\n\nfunction cacheKey(\n entityType: string,\n entityId: string,\n variantId?: string,\n): string {\n return `${entityType}:${entityId}:${variantId || ''}`;\n}\n\nfunction normalizeShopifyEntityType(entityType: string): string {\n const lower = entityType.toLowerCase();\n // A variant is not a Storefront resource of its own — there is no\n // `variantByHandle`. It is fetched as its parent product and then priced at\n // the variant the lookup carries alongside, which is why `variant` and\n // `product` normalize to the same query here rather than to two.\n if (lower.includes('product') || lower.includes('variant')) {\n return 'product';\n }\n if (lower.includes('collection')) {\n return 'collection';\n }\n if (lower.includes('article') || lower.includes('blog')) {\n return 'article';\n }\n return entityType;\n}\n\nfunction extractHandleFromUrl(url: string | undefined): string | undefined {\n const trimmed = url?.trim();\n if (!trimmed) {\n return undefined;\n }\n\n let pathname = trimmed;\n try {\n if (/^https?:\\/\\//i.test(trimmed)) {\n pathname = new URL(trimmed).pathname;\n }\n } catch {\n pathname = trimmed;\n }\n\n const withoutQuery = pathname.split(/[?#]/)[0];\n const withoutLeadingSlash = withoutQuery.replace(/^\\/+/, '');\n const productsIndex = withoutLeadingSlash.indexOf('products/');\n if (productsIndex >= 0) {\n return withoutLeadingSlash.slice(productsIndex + 'products/'.length);\n }\n\n const collectionsIndex = withoutLeadingSlash.indexOf('collections/');\n if (collectionsIndex >= 0) {\n return withoutLeadingSlash.slice(\n collectionsIndex + 'collections/'.length,\n );\n }\n\n return withoutLeadingSlash || undefined;\n}\n\n/**\n * The `?variant=` the row's own URL already carries.\n *\n * A row that names a variant — whether the turn overlaid one onto a product or\n * sent the variant as the entity itself — always links to it, because that is\n * the URL a shopper has to land on. So when the row omits `variantId` as a\n * field, the URL still says which child it means, and pricing the parent's\n * default instead would contradict the link on the same card.\n */\nfunction variantIdFromUrl(url: string | undefined): string | undefined {\n const query = url?.split('#')[0].split('?')[1];\n if (!query) {\n return undefined;\n }\n return new URLSearchParams(query).get('variant')?.trim() || undefined;\n}\n\nexport function getShopifyLookup(entity: {\n entityId: string;\n entityType: string;\n data?: EntityItemData;\n}) {\n const entityType = normalizeShopifyEntityType(entity.entityType);\n const urlHandle = extractHandleFromUrl(entity.data?.url);\n const entityId =\n (entityType === 'product' || entityType === 'collection') && urlHandle\n ? urlHandle\n : entity.entityId;\n return {\n entityType,\n entityId,\n variantId: entity.data?.variantId ?? variantIdFromUrl(entity.data?.url),\n };\n}\n\n/**\n * The turn owns WHICH thing to show; the live fetch owns VOLATILE FACTS about\n * it.\n *\n * Only the turn saw the shopper's question, so only it knows that the brown\n * variant was the one asked for — its `title`, `imageUrl`, `url`,\n * `matchedOptions` and `variantId` all describe that one variant and must\n * survive enrichment. Everything the Storefront answers with that can go stale\n * between the turn and the render — `price`, `compareAtPrice`, `available`,\n * `priceMin`, `priceMax` — comes from `resolved`.\n */\nexport function mergeShopifyEntityData(\n existing: EntityItemData | undefined,\n resolved: EntityItemData,\n): EntityItemData {\n if (!existing) {\n return resolved;\n }\n\n return {\n ...existing,\n ...resolved,\n url: existing.url || resolved.url,\n title: existing.title || resolved.title,\n imageUrl: existing.imageUrl || resolved.imageUrl,\n // The live fetch withholds a price when it was asked about a variant it\n // could not find, rather than answering with the product's floor. Its\n // silence must not erase the turn's number, which was about the right\n // child; a spread would overwrite with `undefined` and leave the card\n // priceless.\n price: resolved.price ?? existing.price,\n variantId: existing.variantId || resolved.variantId,\n matchedOptions: existing.matchedOptions?.length\n ? existing.matchedOptions\n : resolved.matchedOptions,\n };\n}\n\n/**\n * Shopify-specific implementation of the entity resolution pattern.\n *\n * Fetches entity data from the Shopify Storefront API (tokenless) and\n * transforms it to the standard EntityItemData shape.\n *\n * Designed to be plugged into ComponentDependencies.useResolveGenericEntityData.\n */\nexport function useResolveShopifyEntityData<\n TEntity extends {\n entityId: string;\n entityUrl: string;\n entityType: string;\n data?: EntityItemData;\n },\n>(\n entities: TEntity[],\n shopifyConfig: ShopifyStorefrontConfig,\n): { enrichedEntities: TEntity[]; loading: boolean } {\n const [resolvedMap, setResolvedMap] = useState<Map<string, EntityItemData>>(\n new Map(),\n );\n const [settled, setSettled] = useState(false);\n\n // Stable client reference — recreated only when config changes\n const clientRef = useRef<ShopifyStorefrontClient | null>(null);\n const configKeyRef = useRef('');\n const configKey = `${shopifyConfig.storeDomain}|${\n shopifyConfig.apiVersion || ''\n }`;\n\n if (!clientRef.current || configKeyRef.current !== configKey) {\n clientRef.current = new ShopifyStorefrontClient(shopifyConfig);\n configKeyRef.current = configKey;\n }\n\n const entityIds = useMemo(\n () =>\n entities\n .map((e) => {\n const lookup = getShopifyLookup(e);\n return `${e.entityType}:${e.entityId}:${cacheKey(\n lookup.entityType,\n lookup.entityId,\n lookup.variantId,\n )}`;\n })\n .join(','),\n [entities],\n );\n\n useEffect(() => {\n let ignore = false;\n const client = clientRef.current;\n\n if (!entities.length || !client || !shopifyConfig.storeDomain) {\n setSettled(true);\n return;\n }\n\n const entityLookups = entities.map((entity) => {\n const lookup = getShopifyLookup(entity);\n return {\n entity,\n lookup,\n // The variant is part of the key: the cached entry carries that\n // variant's live price, so two entities on the same product but\n // different variants must not share it.\n key: cacheKey(lookup.entityType, lookup.entityId, lookup.variantId),\n };\n });\n\n // Identify entities that need fetching. Payload data can provide the handle.\n const toFetch = entityLookups.filter(\n ({ key }) => !shopifyEntityCache.has(key),\n );\n\n // If everything is cached, resolve immediately\n if (toFetch.length === 0) {\n const cached = new Map<string, EntityItemData>();\n entityLookups.forEach(({ entity, key }) => {\n const item = shopifyEntityCache.get(key);\n if (item) {\n cached.set(entity.entityId, mergeShopifyEntityData(entity.data, item));\n }\n });\n setResolvedMap(cached);\n setSettled(true);\n return;\n }\n\n setSettled(false);\n\n const fetchAll = async () => {\n await Promise.allSettled(\n toFetch.map(async ({ entity, lookup, key }) => {\n const resolved = await resolveShopifyEntity(\n client,\n lookup.entityType,\n lookup.entityId,\n shopifyConfig,\n );\n if (resolved) {\n const itemData = transformShopifyEntityToItemData(\n resolved,\n lookup.entityType,\n lookup.variantId,\n );\n shopifyEntityCache.set(\n key,\n mergeShopifyEntityData(entity.data, itemData),\n );\n }\n }),\n );\n\n if (ignore) {\n return;\n }\n\n // Build the resolved map from cache\n const finalMap = new Map<string, EntityItemData>();\n entityLookups.forEach(({ entity, key }) => {\n const item = shopifyEntityCache.get(key);\n if (item) {\n finalMap.set(\n entity.entityId,\n mergeShopifyEntityData(entity.data, item),\n );\n }\n });\n\n setResolvedMap(finalMap);\n setSettled(true);\n };\n\n fetchAll();\n return () => {\n ignore = true;\n };\n }, [entityIds, shopifyConfig.storeDomain]);\n\n // Merge resolved data into entities\n const enrichedEntities = useMemo(() => {\n return entities.map((entity) => {\n const resolved = resolvedMap.get(entity.entityId);\n if (resolved) {\n return { ...entity, data: resolved };\n }\n return entity;\n });\n }, [entities, resolvedMap]);\n\n return { enrichedEntities, loading: !settled };\n}\n"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,QAAQ,OAAO;AAC5D,SAASC,uBAAuB,QAAQ,6CAA6C;AACrF,SAASC,oBAAoB,QAAQ,2CAA2C;AAChF,SAASC,gCAAgC,QAAQ,4CAA4C;AAI7F;AACA,MAAMC,kBAAkB,GAAG,IAAIC,GAAG,CAAyB,CAAC;AAE5D,SAASC,QAAQA,CACfC,UAAkB,EAClBC,QAAgB,EAChBC,SAAkB,EACV;EACR,OAAO,GAAGF,UAAU,IAAIC,QAAQ,IAAIC,SAAS,IAAI,EAAE,EAAE;AACvD;AAEA,SAASC,0BAA0BA,CAACH,UAAkB,EAAU;EAC9D,MAAMI,KAAK,GAAGJ,UAAU,CAACK,WAAW,CAAC,CAAC;EACtC;EACA;EACA;EACA;EACA,IAAID,KAAK,CAACE,QAAQ,CAAC,SAAS,CAAC,IAAIF,KAAK,CAACE,QAAQ,CAAC,SAAS,CAAC,EAAE;IAC1D,OAAO,SAAS;EAClB;EACA,IAAIF,KAAK,CAACE,QAAQ,CAAC,YAAY,CAAC,EAAE;IAChC,OAAO,YAAY;EACrB;EACA,IAAIF,KAAK,CAACE,QAAQ,CAAC,SAAS,CAAC,IAAIF,KAAK,CAACE,QAAQ,CAAC,MAAM,CAAC,EAAE;IACvD,OAAO,SAAS;EAClB;EACA,OAAON,UAAU;AACnB;AAEA,SAASO,oBAAoBA,CAACC,GAAuB,EAAsB;EACzE,MAAMC,OAAO,GAAGD,GAAG,oBAAHA,GAAG,CAAEE,IAAI,CAAC,CAAC;EAC3B,IAAI,CAACD,OAAO,EAAE;IACZ,OAAOE,SAAS;EAClB;EAEA,IAAIC,QAAQ,GAAGH,OAAO;EACtB,IAAI;IACF,IAAI,eAAe,CAACI,IAAI,CAACJ,OAAO,CAAC,EAAE;MACjCG,QAAQ,GAAG,IAAIE,GAAG,CAACL,OAAO,CAAC,CAACG,QAAQ;IACtC;EACF,CAAC,CAAC,MAAM;IACNA,QAAQ,GAAGH,OAAO;EACpB;EAEA,MAAMM,YAAY,GAAGH,QAAQ,CAACI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;EAC9C,MAAMC,mBAAmB,GAAGF,YAAY,CAACG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;EAC5D,MAAMC,aAAa,GAAGF,mBAAmB,CAACG,OAAO,CAAC,WAAW,CAAC;EAC9D,IAAID,aAAa,IAAI,CAAC,EAAE;IACtB,OAAOF,mBAAmB,CAACI,KAAK,CAACF,aAAa,GAAG,WAAW,CAACG,MAAM,CAAC;EACtE;EAEA,MAAMC,gBAAgB,GAAGN,mBAAmB,CAACG,OAAO,CAAC,cAAc,CAAC;EACpE,IAAIG,gBAAgB,IAAI,CAAC,EAAE;IACzB,OAAON,mBAAmB,CAACI,KAAK,CAC9BE,gBAAgB,GAAG,cAAc,CAACD,MACpC,CAAC;EACH;EAEA,OAAOL,mBAAmB,IAAIN,SAAS;AACzC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASa,gBAAgBA,CAAChB,GAAuB,EAAsB;EAAA,IAAAiB,oBAAA;EACrE,MAAMC,KAAK,GAAGlB,GAAG,oBAAHA,GAAG,CAAEQ,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAACA,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;EAC9C,IAAI,CAACU,KAAK,EAAE;IACV,OAAOf,SAAS;EAClB;EACA,OAAO,EAAAc,oBAAA,OAAIE,eAAe,CAACD,KAAK,CAAC,CAACE,GAAG,CAAC,SAAS,CAAC,qBAAzCH,oBAAA,CAA2Cf,IAAI,CAAC,CAAC,KAAIC,SAAS;AACvE;AAEA,OAAO,SAASkB,gBAAgBA,CAACC,MAIhC,EAAE;EAAA,IAAAC,YAAA,EAAAC,aAAA,EAAAC,aAAA;EACD,MAAMjC,UAAU,GAAGG,0BAA0B,CAAC2B,MAAM,CAAC9B,UAAU,CAAC;EAChE,MAAMkC,SAAS,GAAG3B,oBAAoB,EAAAwB,YAAA,GAACD,MAAM,CAACK,IAAI,qBAAXJ,YAAA,CAAavB,GAAG,CAAC;EACxD,MAAMP,QAAQ,GACZ,CAACD,UAAU,KAAK,SAAS,IAAIA,UAAU,KAAK,YAAY,KAAKkC,SAAS,GAClEA,SAAS,GACTJ,MAAM,CAAC7B,QAAQ;EACrB,OAAO;IACLD,UAAU;IACVC,QAAQ;IACRC,SAAS,EAAE,EAAA8B,aAAA,GAAAF,MAAM,CAACK,IAAI,qBAAXH,aAAA,CAAa9B,SAAS,KAAIsB,gBAAgB,EAAAS,aAAA,GAACH,MAAM,CAACK,IAAI,qBAAXF,aAAA,CAAazB,GAAG;EACxE,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAAS4B,sBAAsBA,CACpCC,QAAoC,EACpCC,QAAwB,EACR;EAAA,IAAAC,qBAAA;EAChB,IAAI,CAACF,QAAQ,EAAE;IACb,OAAOC,QAAQ;EACjB;EAEA,OAAO;IACL,GAAGD,QAAQ;IACX,GAAGC,QAAQ;IACX9B,GAAG,EAAE6B,QAAQ,CAAC7B,GAAG,IAAI8B,QAAQ,CAAC9B,GAAG;IACjCgC,KAAK,EAAEH,QAAQ,CAACG,KAAK,IAAIF,QAAQ,CAACE,KAAK;IACvCC,QAAQ,EAAEJ,QAAQ,CAACI,QAAQ,IAAIH,QAAQ,CAACG,QAAQ;IAChD;IACA;IACA;IACA;IACA;IACAC,KAAK,EAAEJ,QAAQ,CAACI,KAAK,IAAIL,QAAQ,CAACK,KAAK;IACvCxC,SAAS,EAAEmC,QAAQ,CAACnC,SAAS,IAAIoC,QAAQ,CAACpC,SAAS;IACnDyC,cAAc,EAAE,CAAAJ,qBAAA,GAAAF,QAAQ,CAACM,cAAc,aAAvBJ,qBAAA,CAAyBjB,MAAM,GAC3Ce,QAAQ,CAACM,cAAc,GACvBL,QAAQ,CAACK;EACf,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,2BAA2BA,CAQzCC,QAAmB,EACnBC,aAAsC,EACa;EACnD,MAAM,CAACC,WAAW,EAAEC,cAAc,CAAC,GAAG1D,QAAQ,CAC5C,IAAIQ,GAAG,CAAC,CACV,CAAC;EACD,MAAM,CAACmD,OAAO,EAAEC,UAAU,CAAC,GAAG5D,QAAQ,CAAC,KAAK,CAAC;;EAE7C;EACA,MAAM6D,SAAS,GAAG1D,MAAM,CAAiC,IAAI,CAAC;EAC9D,MAAM2D,YAAY,GAAG3D,MAAM,CAAC,EAAE,CAAC;EAC/B,MAAM4D,SAAS,GAAG,GAAGP,aAAa,CAACQ,WAAW,IAC5CR,aAAa,CAACS,UAAU,IAAI,EAAE,EAC9B;EAEF,IAAI,CAACJ,SAAS,CAACK,OAAO,IAAIJ,YAAY,CAACI,OAAO,KAAKH,SAAS,EAAE;IAC5DF,SAAS,CAACK,OAAO,GAAG,IAAI9D,uBAAuB,CAACoD,aAAa,CAAC;IAC9DM,YAAY,CAACI,OAAO,GAAGH,SAAS;EAClC;EAEA,MAAMI,SAAS,GAAGjE,OAAO,CACvB,MACEqD,QAAQ,CACLa,GAAG,CAAEC,CAAC,IAAK;IACV,MAAMC,MAAM,GAAG/B,gBAAgB,CAAC8B,CAAC,CAAC;IAClC,OAAO,GAAGA,CAAC,CAAC3D,UAAU,IAAI2D,CAAC,CAAC1D,QAAQ,IAAIF,QAAQ,CAC9C6D,MAAM,CAAC5D,UAAU,EACjB4D,MAAM,CAAC3D,QAAQ,EACf2D,MAAM,CAAC1D,SACT,CAAC,EAAE;EACL,CAAC,CAAC,CACD2D,IAAI,CAAC,GAAG,CAAC,EACd,CAAChB,QAAQ,CACX,CAAC;EAEDtD,SAAS,CAAC,MAAM;IACd,IAAIuE,MAAM,GAAG,KAAK;IAClB,MAAMC,MAAM,GAAGZ,SAAS,CAACK,OAAO;IAEhC,IAAI,CAACX,QAAQ,CAACvB,MAAM,IAAI,CAACyC,MAAM,IAAI,CAACjB,aAAa,CAACQ,WAAW,EAAE;MAC7DJ,UAAU,CAAC,IAAI,CAAC;MAChB;IACF;IAEA,MAAMc,aAAa,GAAGnB,QAAQ,CAACa,GAAG,CAAE5B,MAAM,IAAK;MAC7C,MAAM8B,MAAM,GAAG/B,gBAAgB,CAACC,MAAM,CAAC;MACvC,OAAO;QACLA,MAAM;QACN8B,MAAM;QACN;QACA;QACA;QACAK,GAAG,EAAElE,QAAQ,CAAC6D,MAAM,CAAC5D,UAAU,EAAE4D,MAAM,CAAC3D,QAAQ,EAAE2D,MAAM,CAAC1D,SAAS;MACpE,CAAC;IACH,CAAC,CAAC;;IAEF;IACA,MAAMgE,OAAO,GAAGF,aAAa,CAACG,MAAM,CAClCC,IAAA;MAAA,IAAC;QAAEH;MAAI,CAAC,GAAAG,IAAA;MAAA,OAAK,CAACvE,kBAAkB,CAACwE,GAAG,CAACJ,GAAG,CAAC;IAAA,CAC3C,CAAC;;IAED;IACA,IAAIC,OAAO,CAAC5C,MAAM,KAAK,CAAC,EAAE;MACxB,MAAMgD,MAAM,GAAG,IAAIxE,GAAG,CAAyB,CAAC;MAChDkE,aAAa,CAACO,OAAO,CAACC,KAAA,IAAqB;QAAA,IAApB;UAAE1C,MAAM;UAAEmC;QAAI,CAAC,GAAAO,KAAA;QACpC,MAAMC,IAAI,GAAG5E,kBAAkB,CAAC+B,GAAG,CAACqC,GAAG,CAAC;QACxC,IAAIQ,IAAI,EAAE;UACRH,MAAM,CAACI,GAAG,CAAC5C,MAAM,CAAC7B,QAAQ,EAAEmC,sBAAsB,CAACN,MAAM,CAACK,IAAI,EAAEsC,IAAI,CAAC,CAAC;QACxE;MACF,CAAC,CAAC;MACFzB,cAAc,CAACsB,MAAM,CAAC;MACtBpB,UAAU,CAAC,IAAI,CAAC;MAChB;IACF;IAEAA,UAAU,CAAC,KAAK,CAAC;IAEjB,MAAMyB,QAAQ,GAAG,MAAAA,CAAA,KAAY;MAC3B,MAAMC,OAAO,CAACC,UAAU,CACtBX,OAAO,CAACR,GAAG,CAAC,MAAAoB,KAAA,IAAmC;QAAA,IAA5B;UAAEhD,MAAM;UAAE8B,MAAM;UAAEK;QAAI,CAAC,GAAAa,KAAA;QACxC,MAAMxC,QAAQ,GAAG,MAAM3C,oBAAoB,CACzCoE,MAAM,EACNH,MAAM,CAAC5D,UAAU,EACjB4D,MAAM,CAAC3D,QAAQ,EACf6C,aACF,CAAC;QACD,IAAIR,QAAQ,EAAE;UACZ,MAAMyC,QAAQ,GAAGnF,gCAAgC,CAC/C0C,QAAQ,EACRsB,MAAM,CAAC5D,UAAU,EACjB4D,MAAM,CAAC1D,SACT,CAAC;UACDL,kBAAkB,CAAC6E,GAAG,CACpBT,GAAG,EACH7B,sBAAsB,CAACN,MAAM,CAACK,IAAI,EAAE4C,QAAQ,CAC9C,CAAC;QACH;MACF,CAAC,CACH,CAAC;MAED,IAAIjB,MAAM,EAAE;QACV;MACF;;MAEA;MACA,MAAMkB,QAAQ,GAAG,IAAIlF,GAAG,CAAyB,CAAC;MAClDkE,aAAa,CAACO,OAAO,CAACU,KAAA,IAAqB;QAAA,IAApB;UAAEnD,MAAM;UAAEmC;QAAI,CAAC,GAAAgB,KAAA;QACpC,MAAMR,IAAI,GAAG5E,kBAAkB,CAAC+B,GAAG,CAACqC,GAAG,CAAC;QACxC,IAAIQ,IAAI,EAAE;UACRO,QAAQ,CAACN,GAAG,CACV5C,MAAM,CAAC7B,QAAQ,EACfmC,sBAAsB,CAACN,MAAM,CAACK,IAAI,EAAEsC,IAAI,CAC1C,CAAC;QACH;MACF,CAAC,CAAC;MAEFzB,cAAc,CAACgC,QAAQ,CAAC;MACxB9B,UAAU,CAAC,IAAI,CAAC;IAClB,CAAC;IAEDyB,QAAQ,CAAC,CAAC;IACV,OAAO,MAAM;MACXb,MAAM,GAAG,IAAI;IACf,CAAC;EACH,CAAC,EAAE,CAACL,SAAS,EAAEX,aAAa,CAACQ,WAAW,CAAC,CAAC;;EAE1C;EACA,MAAM4B,gBAAgB,GAAG1F,OAAO,CAAC,MAAM;IACrC,OAAOqD,QAAQ,CAACa,GAAG,CAAE5B,MAAM,IAAK;MAC9B,MAAMQ,QAAQ,GAAGS,WAAW,CAACnB,GAAG,CAACE,MAAM,CAAC7B,QAAQ,CAAC;MACjD,IAAIqC,QAAQ,EAAE;QACZ,OAAO;UAAE,GAAGR,MAAM;UAAEK,IAAI,EAAEG;QAAS,CAAC;MACtC;MACA,OAAOR,MAAM;IACf,CAAC,CAAC;EACJ,CAAC,EAAE,CAACe,QAAQ,EAAEE,WAAW,CAAC,CAAC;EAE3B,OAAO;IAAEmC,gBAAgB;IAAEC,OAAO,EAAE,CAAClC;EAAQ,CAAC;AAChD","ignoreList":[]}
@@ -330,6 +330,13 @@ export function nodesToParts(nodes) {
330
330
  const parts = [];
331
331
  const typeCounts = {};
332
332
  const childCursors = new Map();
333
+ // A paragraph carrying an inline link is unwrapped into several block
334
+ // elements (`t`, `link`, `t`), so its parts are siblings with nothing to say
335
+ // they came from one paragraph. Mark every part after the first of a given
336
+ // AST node, so a consumer rebuilding prose can tell "same paragraph, keep
337
+ // going" from "next paragraph, break". Only split nodes carry the flag —
338
+ // plain paragraphs, headings and images stay meta-free.
339
+ const seenNodes = new Set();
333
340
  for (const block of enrichedBlocks) {
334
341
  const astNode = nodes[block.astNodeIndex];
335
342
  const type = blockElementToPartType(block.element);
@@ -353,6 +360,13 @@ export function nodesToParts(nodes) {
353
360
  markdown: source.markdown.trim()
354
361
  };
355
362
  }
363
+ if (seenNodes.has(block.astNodeIndex)) {
364
+ part.meta = {
365
+ ...part.meta,
366
+ continuesNode: true
367
+ };
368
+ }
369
+ seenNodes.add(block.astNodeIndex);
356
370
 
357
371
  // For lists whose items are all complex, the list-level content is
358
372
  // meaningless (just a concatenation of child text). Clear it so that