@wix/web5-core 1.63.11 → 1.63.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/component/componentDefinitions/CalloutSectionDefinition.js +13 -8
- package/dist/cjs/component/componentDefinitions/CalloutSectionDefinition.js.map +1 -1
- package/dist/cjs/hooks/useResolveShopifyEntityData.js +26 -3
- package/dist/cjs/hooks/useResolveShopifyEntityData.js.map +1 -1
- package/dist/cjs/types/callout.js +11 -1
- package/dist/cjs/types/callout.js.map +1 -1
- package/dist/esm/component/componentDefinitions/CalloutSectionDefinition.js +13 -8
- package/dist/esm/component/componentDefinitions/CalloutSectionDefinition.js.map +1 -1
- package/dist/esm/hooks/useResolveShopifyEntityData.js +26 -4
- package/dist/esm/hooks/useResolveShopifyEntityData.js.map +1 -1
- package/dist/esm/types/callout.js +11 -1
- package/dist/esm/types/callout.js.map +1 -1
- package/dist/types/hooks/useResolveShopifyEntityData.d.ts +9 -0
- package/dist/types/hooks/useResolveShopifyEntityData.d.ts.map +1 -1
- package/dist/types/types/callout.d.ts +11 -1
- package/dist/types/types/callout.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -6,7 +6,6 @@ exports.CalloutSectionDefinition = void 0;
|
|
|
6
6
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
7
7
|
var _parseUtils = require("./parse-utils");
|
|
8
8
|
var _parts = require("../../parts/parts");
|
|
9
|
-
var _callout = require("../../types/callout");
|
|
10
9
|
var _sectionDefinition = require("../section-definition");
|
|
11
10
|
/**
|
|
12
11
|
* Callout — the page's dialog layer, written by the orchestrator itself rather
|
|
@@ -148,16 +147,22 @@ class CalloutSectionDefinition {
|
|
|
148
147
|
/**
|
|
149
148
|
* `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the
|
|
150
149
|
* same vocabulary the legacy `[!TIP]` marker produced, which is what any
|
|
151
|
-
* kind-aware renderer already switches on.
|
|
152
|
-
*
|
|
150
|
+
* kind-aware renderer already switches on.
|
|
151
|
+
*
|
|
152
|
+
* Deliberately NOT validated against a list. The prompts own this vocabulary,
|
|
153
|
+
* and they live in the prompt catalog precisely so it can change without
|
|
154
|
+
* shipping code — an allowlist here would mean every new semantic needs a
|
|
155
|
+
* release before it resolves, which is backwards. Nothing gates on `kind`
|
|
156
|
+
* either: it is metadata for a renderer that wants to vary by it, and the one
|
|
157
|
+
* renderer that does falls back when it sees a value it doesn't know.
|
|
158
|
+
*
|
|
159
|
+
* `CALLOUT_SEMANTICS` therefore documents what the prompts emit today; it is
|
|
160
|
+
* not a filter.
|
|
153
161
|
*/
|
|
154
162
|
exports.CalloutSectionDefinition = CalloutSectionDefinition;
|
|
155
163
|
function semanticToKind(semantic) {
|
|
156
|
-
const value = semantic == null ? void 0 : semantic.trim()
|
|
157
|
-
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
return _callout.CALLOUT_SEMANTICS.includes(value) ? {
|
|
164
|
+
const value = semantic == null ? void 0 : semantic.trim();
|
|
165
|
+
return value ? {
|
|
161
166
|
kind: value.toUpperCase()
|
|
162
167
|
} : undefined;
|
|
163
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_parseUtils","require","_parts","_callout","_sectionDefinition","CalloutSectionDefinition","constructor","_defineProperty2","default","callouts","text","markdown","expected","kind","parse","parts","calloutParts","getPartsByType","length","parseBlockquote","parseProse","_parts$find","descriptor","find","p","type","meta","section","inlinePartsToMarkdown","filter","trim","DROP_SECTION","semanticToKind","semantic","relatedLinks","seenUrls","Set","c","_c$meta","_c$content","content","parsed","parseCalloutTag","push","link","getLinks","_link$meta","_link$content","href","has","add","url","undefined","exports","value","toLowerCase","CALLOUT_SEMANTICS","includes","toUpperCase"],"sources":["../../../../src/component/componentDefinitions/CalloutSectionDefinition.ts"],"sourcesContent":["import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils';\nimport { getPartsByType, getLinks, type Part } from '../../parts/parts';\nimport { CALLOUT_SEMANTICS, type Callout } from '../../types/callout';\nimport type {\n CalloutCompProps,\n CalloutSectionComponent,\n SectionFixture,\n} from '../types';\nimport {\n DROP_SECTION,\n type DropSection,\n type SectionDefinition,\n} from '../section-definition';\n\n/**\n * Callout — the page's dialog layer, written by the orchestrator itself rather\n * than by a per-section model.\n *\n * Two markdown forms reach this definition, and both must keep working:\n *\n * 1. **Descriptor + plain prose** (current). `semantic` carries the kind:\n *\n * <!-- section:callout, semantic:tip -->\n * Prefer a gentle daily ritual? The 4 oz size is the one to sample with.\n *\n * 2. **GFM alert blockquote** (legacy). Still emitted by sites pinned to an\n * older prompt, and present in every historical page, so it is parsed\n * verbatim rather than migrated:\n *\n * <!-- section:callout -->\n * > [!TIP] Prefer a gentle daily ritual? …\n *\n * The two forms differ in one way that matters. A blockquote collapses to a\n * single `callout` part carrying the whole line in `meta.markdown`, so inline\n * ask links survive for free and `getLinks` finds nothing to harvest. A\n * paragraph is *unwrapped* into its inline children (`markdownBlocks.ts`), so\n * prose with a link arrives as `t + link + t` — separate parts. Reading only\n * the `t` parts would delete the link's url and label (DL #134's list bug) and\n * `getLinks` would then re-surface it as a footer link, showing the visitor the\n * same link twice. `inlinePartsToMarkdown` is what keeps the link inline, and\n * the prose path deliberately harvests no `relatedLinks` at all.\n */\nexport class CalloutSectionDefinition\n implements SectionDefinition<CalloutSectionComponent>\n{\n sectionType = 'callout' as const;\n patterns = [\n // Descriptor-led prose. `link` must be in the alternation or a callout\n // carrying an ask link fails to match and falls through to the fallback.\n 'html_comment[section=\"callout\"] > (t | link)+',\n // Legacy blockquote.\n 'html_comment? > callout+',\n ];\n\n defaults: CalloutCompProps = {\n callouts: [{ text: 'This is a callout.' }],\n };\n\n fixtures: SectionFixture<CalloutCompProps>[] = [\n {\n markdown:\n '<!-- section:callout, semantic:tip -->\\nThis is a callout paragraph.',\n expected: {\n callouts: [{ text: 'This is a callout paragraph.', kind: 'TIP' }],\n },\n },\n {\n markdown: '> This is a callout blockquote.',\n expected: {\n callouts: [{ text: 'This is a callout blockquote.' }],\n },\n },\n ];\n\n parse(parts: Part[]): CalloutCompProps | null | DropSection {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n\n return calloutParts.length > 0\n ? this.parseBlockquote(calloutParts, parts)\n : this.parseProse(parts);\n }\n\n /**\n * Descriptor + plain prose. The kind comes from the descriptor's `semantic`,\n * and every link in the body is inline prose — never a `relatedLinks` footer.\n */\n private parseProse(parts: Part[]): CalloutCompProps | null | DropSection {\n const descriptor = parts.find((p) => p.type === 'htmlComment' && p.meta)\n ?.meta as Record<string, string> | undefined;\n\n // The pattern already gates on this; re-checking keeps a direct\n // `parse()` call (and any future pattern edit) from mislabelling a\n // different section's prose as a callout.\n if (descriptor?.section !== 'callout') {\n return null;\n }\n\n const text = inlinePartsToMarkdown(\n parts.filter((p) => p.type !== 'htmlComment'),\n ).trim();\n\n // Matched, but there is nothing to say. DROP_SECTION consumes the nodes\n // and renders nothing; returning null would hand the descriptor to the\n // fallback, which renders it as stray text.\n if (!text) {\n return DROP_SECTION;\n }\n\n return {\n callouts: [{ text, ...(semanticToKind(descriptor.semantic) ?? {}) }],\n };\n }\n\n /** Legacy GFM alert blockquote. */\n private parseBlockquote(\n calloutParts: Part[],\n parts: Part[],\n ): CalloutCompProps | null | DropSection {\n const callouts: Callout[] = [];\n const relatedLinks: { text: string; url: string }[] = [];\n const seenUrls = new Set<string>();\n\n for (const c of calloutParts) {\n const text = (c.meta?.markdown as string)?.trim() || c.content?.trim();\n if (text) {\n const parsed = parseCalloutTag(text);\n if (parsed.text) {\n callouts.push({ text: parsed.text, kind: parsed.kind });\n }\n }\n }\n\n if (callouts.length === 0) {\n return DROP_SECTION;\n }\n\n // A blockquote yields no sibling link parts, so anything found here arrived\n // alongside the quote and is a genuine footer link.\n for (const link of getLinks(parts)) {\n const href = link.meta?.href;\n const text = link.content?.trim();\n if (text && href && !seenUrls.has(href)) {\n seenUrls.add(href);\n relatedLinks.push({ text, url: href });\n }\n }\n\n return {\n callouts,\n relatedLinks: relatedLinks.length > 0 ? relatedLinks : undefined,\n };\n }\n}\n\n/**\n * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the\n * same vocabulary the legacy `[!TIP]` marker produced, which is what any\n * kind-aware renderer already switches on. An unrecognised value carries no\n * kind rather than an invented one.\n */\nfunction semanticToKind(semantic?: string): { kind: string } | undefined {\n const value = semantic?.trim().toLowerCase();\n if (!value) {\n return undefined;\n }\n return (CALLOUT_SEMANTICS as readonly string[]).includes(value)\n ? { kind: value.toUpperCase() }\n : undefined;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_parseUtils","require","_parts","_sectionDefinition","CalloutSectionDefinition","constructor","_defineProperty2","default","callouts","text","markdown","expected","kind","parse","parts","calloutParts","getPartsByType","length","parseBlockquote","parseProse","_parts$find","descriptor","find","p","type","meta","section","inlinePartsToMarkdown","filter","trim","DROP_SECTION","semanticToKind","semantic","relatedLinks","seenUrls","Set","c","_c$meta","_c$content","content","parsed","parseCalloutTag","push","link","getLinks","_link$meta","_link$content","href","has","add","url","undefined","exports","value","toUpperCase"],"sources":["../../../../src/component/componentDefinitions/CalloutSectionDefinition.ts"],"sourcesContent":["import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils';\nimport { getPartsByType, getLinks, type Part } from '../../parts/parts';\nimport type { Callout } from '../../types/callout';\nimport type {\n CalloutCompProps,\n CalloutSectionComponent,\n SectionFixture,\n} from '../types';\nimport {\n DROP_SECTION,\n type DropSection,\n type SectionDefinition,\n} from '../section-definition';\n\n/**\n * Callout — the page's dialog layer, written by the orchestrator itself rather\n * than by a per-section model.\n *\n * Two markdown forms reach this definition, and both must keep working:\n *\n * 1. **Descriptor + plain prose** (current). `semantic` carries the kind:\n *\n * <!-- section:callout, semantic:tip -->\n * Prefer a gentle daily ritual? The 4 oz size is the one to sample with.\n *\n * 2. **GFM alert blockquote** (legacy). Still emitted by sites pinned to an\n * older prompt, and present in every historical page, so it is parsed\n * verbatim rather than migrated:\n *\n * <!-- section:callout -->\n * > [!TIP] Prefer a gentle daily ritual? …\n *\n * The two forms differ in one way that matters. A blockquote collapses to a\n * single `callout` part carrying the whole line in `meta.markdown`, so inline\n * ask links survive for free and `getLinks` finds nothing to harvest. A\n * paragraph is *unwrapped* into its inline children (`markdownBlocks.ts`), so\n * prose with a link arrives as `t + link + t` — separate parts. Reading only\n * the `t` parts would delete the link's url and label (DL #134's list bug) and\n * `getLinks` would then re-surface it as a footer link, showing the visitor the\n * same link twice. `inlinePartsToMarkdown` is what keeps the link inline, and\n * the prose path deliberately harvests no `relatedLinks` at all.\n */\nexport class CalloutSectionDefinition\n implements SectionDefinition<CalloutSectionComponent>\n{\n sectionType = 'callout' as const;\n patterns = [\n // Descriptor-led prose. `link` must be in the alternation or a callout\n // carrying an ask link fails to match and falls through to the fallback.\n 'html_comment[section=\"callout\"] > (t | link)+',\n // Legacy blockquote.\n 'html_comment? > callout+',\n ];\n\n defaults: CalloutCompProps = {\n callouts: [{ text: 'This is a callout.' }],\n };\n\n fixtures: SectionFixture<CalloutCompProps>[] = [\n {\n markdown:\n '<!-- section:callout, semantic:tip -->\\nThis is a callout paragraph.',\n expected: {\n callouts: [{ text: 'This is a callout paragraph.', kind: 'TIP' }],\n },\n },\n {\n markdown: '> This is a callout blockquote.',\n expected: {\n callouts: [{ text: 'This is a callout blockquote.' }],\n },\n },\n ];\n\n parse(parts: Part[]): CalloutCompProps | null | DropSection {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n\n return calloutParts.length > 0\n ? this.parseBlockquote(calloutParts, parts)\n : this.parseProse(parts);\n }\n\n /**\n * Descriptor + plain prose. The kind comes from the descriptor's `semantic`,\n * and every link in the body is inline prose — never a `relatedLinks` footer.\n */\n private parseProse(parts: Part[]): CalloutCompProps | null | DropSection {\n const descriptor = parts.find((p) => p.type === 'htmlComment' && p.meta)\n ?.meta as Record<string, string> | undefined;\n\n // The pattern already gates on this; re-checking keeps a direct\n // `parse()` call (and any future pattern edit) from mislabelling a\n // different section's prose as a callout.\n if (descriptor?.section !== 'callout') {\n return null;\n }\n\n const text = inlinePartsToMarkdown(\n parts.filter((p) => p.type !== 'htmlComment'),\n ).trim();\n\n // Matched, but there is nothing to say. DROP_SECTION consumes the nodes\n // and renders nothing; returning null would hand the descriptor to the\n // fallback, which renders it as stray text.\n if (!text) {\n return DROP_SECTION;\n }\n\n return {\n callouts: [{ text, ...(semanticToKind(descriptor.semantic) ?? {}) }],\n };\n }\n\n /** Legacy GFM alert blockquote. */\n private parseBlockquote(\n calloutParts: Part[],\n parts: Part[],\n ): CalloutCompProps | null | DropSection {\n const callouts: Callout[] = [];\n const relatedLinks: { text: string; url: string }[] = [];\n const seenUrls = new Set<string>();\n\n for (const c of calloutParts) {\n const text = (c.meta?.markdown as string)?.trim() || c.content?.trim();\n if (text) {\n const parsed = parseCalloutTag(text);\n if (parsed.text) {\n callouts.push({ text: parsed.text, kind: parsed.kind });\n }\n }\n }\n\n if (callouts.length === 0) {\n return DROP_SECTION;\n }\n\n // A blockquote yields no sibling link parts, so anything found here arrived\n // alongside the quote and is a genuine footer link.\n for (const link of getLinks(parts)) {\n const href = link.meta?.href;\n const text = link.content?.trim();\n if (text && href && !seenUrls.has(href)) {\n seenUrls.add(href);\n relatedLinks.push({ text, url: href });\n }\n }\n\n return {\n callouts,\n relatedLinks: relatedLinks.length > 0 ? relatedLinks : undefined,\n };\n }\n}\n\n/**\n * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the\n * same vocabulary the legacy `[!TIP]` marker produced, which is what any\n * kind-aware renderer already switches on.\n *\n * Deliberately NOT validated against a list. The prompts own this vocabulary,\n * and they live in the prompt catalog precisely so it can change without\n * shipping code — an allowlist here would mean every new semantic needs a\n * release before it resolves, which is backwards. Nothing gates on `kind`\n * either: it is metadata for a renderer that wants to vary by it, and the one\n * renderer that does falls back when it sees a value it doesn't know.\n *\n * `CALLOUT_SEMANTICS` therefore documents what the prompts emit today; it is\n * not a filter.\n */\nfunction semanticToKind(semantic?: string): { kind: string } | undefined {\n const value = semantic?.trim();\n return value ? { kind: value.toUpperCase() } : undefined;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
+
exports.getShopifyLookup = getShopifyLookup;
|
|
4
5
|
exports.mergeShopifyEntityData = mergeShopifyEntityData;
|
|
5
6
|
exports.useResolveShopifyEntityData = useResolveShopifyEntityData;
|
|
6
7
|
var _react = require("react");
|
|
@@ -14,7 +15,11 @@ function cacheKey(entityType, entityId, variantId) {
|
|
|
14
15
|
}
|
|
15
16
|
function normalizeShopifyEntityType(entityType) {
|
|
16
17
|
const lower = entityType.toLowerCase();
|
|
17
|
-
|
|
18
|
+
// A variant is not a Storefront resource of its own — there is no
|
|
19
|
+
// `variantByHandle`. It is fetched as its parent product and then priced at
|
|
20
|
+
// the variant the lookup carries alongside, which is why `variant` and
|
|
21
|
+
// `product` normalize to the same query here rather than to two.
|
|
22
|
+
if (lower.includes('product') || lower.includes('variant')) {
|
|
18
23
|
return 'product';
|
|
19
24
|
}
|
|
20
25
|
if (lower.includes('collection')) {
|
|
@@ -50,15 +55,33 @@ function extractHandleFromUrl(url) {
|
|
|
50
55
|
}
|
|
51
56
|
return withoutLeadingSlash || undefined;
|
|
52
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The `?variant=` the row's own URL already carries.
|
|
61
|
+
*
|
|
62
|
+
* A row that names a variant — whether the turn overlaid one onto a product or
|
|
63
|
+
* sent the variant as the entity itself — always links to it, because that is
|
|
64
|
+
* the URL a shopper has to land on. So when the row omits `variantId` as a
|
|
65
|
+
* field, the URL still says which child it means, and pricing the parent's
|
|
66
|
+
* default instead would contradict the link on the same card.
|
|
67
|
+
*/
|
|
68
|
+
function variantIdFromUrl(url) {
|
|
69
|
+
var _URLSearchParams$get;
|
|
70
|
+
const query = url == null ? void 0 : url.split('#')[0].split('?')[1];
|
|
71
|
+
if (!query) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return ((_URLSearchParams$get = new URLSearchParams(query).get('variant')) == null ? void 0 : _URLSearchParams$get.trim()) || undefined;
|
|
75
|
+
}
|
|
53
76
|
function getShopifyLookup(entity) {
|
|
54
|
-
var _entity$data, _entity$data2;
|
|
77
|
+
var _entity$data, _entity$data2, _entity$data3;
|
|
55
78
|
const entityType = normalizeShopifyEntityType(entity.entityType);
|
|
56
79
|
const urlHandle = extractHandleFromUrl((_entity$data = entity.data) == null ? void 0 : _entity$data.url);
|
|
57
80
|
const entityId = (entityType === 'product' || entityType === 'collection') && urlHandle ? urlHandle : entity.entityId;
|
|
58
81
|
return {
|
|
59
82
|
entityType,
|
|
60
83
|
entityId,
|
|
61
|
-
variantId: (_entity$data2 = entity.data) == null ? void 0 : _entity$data2.variantId
|
|
84
|
+
variantId: ((_entity$data2 = entity.data) == null ? void 0 : _entity$data2.variantId) ?? variantIdFromUrl((_entity$data3 = entity.data) == null ? void 0 : _entity$data3.url)
|
|
62
85
|
};
|
|
63
86
|
}
|
|
64
87
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_shopifyStorefrontClient","_shopifyEntityResolver","_transformShopifyEntity","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","getShopifyLookup","entity","_entity$data","_entity$data2","urlHandle","data","mergeShopifyEntityData","existing","resolved","_existing$matchedOpti","title","imageUrl","matchedOptions","useResolveShopifyEntityData","entities","shopifyConfig","resolvedMap","setResolvedMap","useState","settled","setSettled","clientRef","useRef","configKeyRef","configKey","storeDomain","apiVersion","current","ShopifyStorefrontClient","entityIds","useMemo","map","e","lookup","join","useEffect","ignore","client","entityLookups","key","toFetch","filter","has","cached","forEach","item","get","set","fetchAll","Promise","allSettled","resolveShopifyEntity","itemData","transformShopifyEntityToItemData","finalMap","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 if (lower.includes('product')) {\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\nfunction 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 { entityType, entityId, variantId: entity.data?.variantId };\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":["_react","require","_shopifyStorefrontClient","_shopifyEntityResolver","_transformShopifyEntity","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","useState","settled","setSettled","clientRef","useRef","configKeyRef","configKey","storeDomain","apiVersion","current","ShopifyStorefrontClient","entityIds","useMemo","map","e","lookup","join","useEffect","ignore","client","entityLookups","key","toFetch","filter","has","cached","forEach","item","set","fetchAll","Promise","allSettled","resolveShopifyEntity","itemData","transformShopifyEntityToItemData","finalMap","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":[]}
|
|
@@ -11,6 +11,16 @@ const CALLOUT_KINDS = exports.CALLOUT_KINDS = ['NOTE', 'TIP', 'IMPORTANT', 'WARN
|
|
|
11
11
|
* the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever
|
|
12
12
|
* accepted, because historical pages and sites pinned to an older prompt still
|
|
13
13
|
* carry them. This list is what the current prompts are allowed to emit.
|
|
14
|
+
*
|
|
15
|
+
* **Documentation, not a filter.** Nothing validates against this list: the
|
|
16
|
+
* prompts own the vocabulary and live in the prompt catalog so it can change
|
|
17
|
+
* without a release. Adding a semantic here is a docs change, not a gate.
|
|
18
|
+
*
|
|
19
|
+
* `clarification` is the one whose meaning is load-bearing rather than
|
|
20
|
+
* cosmetic: the prompts define it as a callout that offers the visitor a branch,
|
|
21
|
+
* and therefore one that carries at least one `web5://ask/` link. Naming the
|
|
22
|
+
* intent in the descriptor is what makes that obligation checkable — a callout
|
|
23
|
+
* labelled `clarification` with nothing linked is wrong output.
|
|
14
24
|
*/
|
|
15
|
-
const CALLOUT_SEMANTICS = exports.CALLOUT_SEMANTICS = ['note', 'tip'];
|
|
25
|
+
const CALLOUT_SEMANTICS = exports.CALLOUT_SEMANTICS = ['note', 'tip', 'clarification'];
|
|
16
26
|
//# sourceMappingURL=callout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CALLOUT_KINDS","exports","CALLOUT_SEMANTICS"],"sources":["../../../src/types/callout.ts"],"sourcesContent":["export const CALLOUT_KINDS = [\n 'NOTE',\n 'TIP',\n 'IMPORTANT',\n 'WARNING',\n 'CAUTION',\n 'ECHO',\n] as const;\n\nexport type CalloutKind = (typeof CALLOUT_KINDS)[number];\n\n/**\n * The kinds a callout descriptor may declare as `semantic:`, lowercase, as the\n * orchestrator writes them: `<!-- section:callout, semantic:tip -->`.\n *\n * Deliberately narrower than `CALLOUT_KINDS`. That list is the vocabulary of\n * the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever\n * accepted, because historical pages and sites pinned to an older prompt still\n * carry them. This list is what the current prompts are allowed to emit.\n */\nexport const CALLOUT_SEMANTICS = ['note', 'tip'] as const;\n\nexport type CalloutSemantic = (typeof CALLOUT_SEMANTICS)[number];\n\nexport interface Callout {\n text: string;\n kind?: string;\n}\n"],"mappings":";;;;AAAO,MAAMA,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAG,CAC3B,MAAM,EACN,KAAK,EACL,WAAW,EACX,SAAS,EACT,SAAS,EACT,MAAM,CACE;AAIV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,iBAAiB,GAAAD,OAAA,CAAAC,iBAAA,GAAG,CAAC,MAAM,EAAE,KAAK,CAAU","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["CALLOUT_KINDS","exports","CALLOUT_SEMANTICS"],"sources":["../../../src/types/callout.ts"],"sourcesContent":["export const CALLOUT_KINDS = [\n 'NOTE',\n 'TIP',\n 'IMPORTANT',\n 'WARNING',\n 'CAUTION',\n 'ECHO',\n] as const;\n\nexport type CalloutKind = (typeof CALLOUT_KINDS)[number];\n\n/**\n * The kinds a callout descriptor may declare as `semantic:`, lowercase, as the\n * orchestrator writes them: `<!-- section:callout, semantic:tip -->`.\n *\n * Deliberately narrower than `CALLOUT_KINDS`. That list is the vocabulary of\n * the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever\n * accepted, because historical pages and sites pinned to an older prompt still\n * carry them. This list is what the current prompts are allowed to emit.\n *\n * **Documentation, not a filter.** Nothing validates against this list: the\n * prompts own the vocabulary and live in the prompt catalog so it can change\n * without a release. Adding a semantic here is a docs change, not a gate.\n *\n * `clarification` is the one whose meaning is load-bearing rather than\n * cosmetic: the prompts define it as a callout that offers the visitor a branch,\n * and therefore one that carries at least one `web5://ask/` link. Naming the\n * intent in the descriptor is what makes that obligation checkable — a callout\n * labelled `clarification` with nothing linked is wrong output.\n */\nexport const CALLOUT_SEMANTICS = ['note', 'tip', 'clarification'] as const;\n\nexport type CalloutSemantic = (typeof CALLOUT_SEMANTICS)[number];\n\nexport interface Callout {\n text: string;\n kind?: string;\n}\n"],"mappings":";;;;AAAO,MAAMA,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAG,CAC3B,MAAM,EACN,KAAK,EACL,WAAW,EACX,SAAS,EACT,SAAS,EACT,MAAM,CACE;AAIV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,iBAAiB,GAAAD,OAAA,CAAAC,iBAAA,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,eAAe,CAAU","ignoreList":[]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils.js';
|
|
3
3
|
import { getPartsByType, getLinks } from '../../parts/parts.js';
|
|
4
|
-
import { CALLOUT_SEMANTICS } from '../../types/callout.js';
|
|
5
4
|
import { DROP_SECTION } from '../section-definition.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -144,15 +143,21 @@ export class CalloutSectionDefinition {
|
|
|
144
143
|
/**
|
|
145
144
|
* `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the
|
|
146
145
|
* same vocabulary the legacy `[!TIP]` marker produced, which is what any
|
|
147
|
-
* kind-aware renderer already switches on.
|
|
148
|
-
*
|
|
146
|
+
* kind-aware renderer already switches on.
|
|
147
|
+
*
|
|
148
|
+
* Deliberately NOT validated against a list. The prompts own this vocabulary,
|
|
149
|
+
* and they live in the prompt catalog precisely so it can change without
|
|
150
|
+
* shipping code — an allowlist here would mean every new semantic needs a
|
|
151
|
+
* release before it resolves, which is backwards. Nothing gates on `kind`
|
|
152
|
+
* either: it is metadata for a renderer that wants to vary by it, and the one
|
|
153
|
+
* renderer that does falls back when it sees a value it doesn't know.
|
|
154
|
+
*
|
|
155
|
+
* `CALLOUT_SEMANTICS` therefore documents what the prompts emit today; it is
|
|
156
|
+
* not a filter.
|
|
149
157
|
*/
|
|
150
158
|
function semanticToKind(semantic) {
|
|
151
|
-
const value = semantic == null ? void 0 : semantic.trim()
|
|
152
|
-
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
return CALLOUT_SEMANTICS.includes(value) ? {
|
|
159
|
+
const value = semantic == null ? void 0 : semantic.trim();
|
|
160
|
+
return value ? {
|
|
156
161
|
kind: value.toUpperCase()
|
|
157
162
|
} : undefined;
|
|
158
163
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["inlinePartsToMarkdown","parseCalloutTag","getPartsByType","getLinks","CALLOUT_SEMANTICS","DROP_SECTION","CalloutSectionDefinition","constructor","_defineProperty","callouts","text","markdown","expected","kind","parse","parts","calloutParts","length","parseBlockquote","parseProse","_parts$find","descriptor","find","p","type","meta","section","filter","trim","semanticToKind","semantic","relatedLinks","seenUrls","Set","c","_c$meta","_c$content","content","parsed","push","link","_link$meta","_link$content","href","has","add","url","undefined","value","toLowerCase","includes","toUpperCase"],"sources":["../../../../src/component/componentDefinitions/CalloutSectionDefinition.ts"],"sourcesContent":["import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils';\nimport { getPartsByType, getLinks, type Part } from '../../parts/parts';\nimport { CALLOUT_SEMANTICS, type Callout } from '../../types/callout';\nimport type {\n CalloutCompProps,\n CalloutSectionComponent,\n SectionFixture,\n} from '../types';\nimport {\n DROP_SECTION,\n type DropSection,\n type SectionDefinition,\n} from '../section-definition';\n\n/**\n * Callout — the page's dialog layer, written by the orchestrator itself rather\n * than by a per-section model.\n *\n * Two markdown forms reach this definition, and both must keep working:\n *\n * 1. **Descriptor + plain prose** (current). `semantic` carries the kind:\n *\n * <!-- section:callout, semantic:tip -->\n * Prefer a gentle daily ritual? The 4 oz size is the one to sample with.\n *\n * 2. **GFM alert blockquote** (legacy). Still emitted by sites pinned to an\n * older prompt, and present in every historical page, so it is parsed\n * verbatim rather than migrated:\n *\n * <!-- section:callout -->\n * > [!TIP] Prefer a gentle daily ritual? …\n *\n * The two forms differ in one way that matters. A blockquote collapses to a\n * single `callout` part carrying the whole line in `meta.markdown`, so inline\n * ask links survive for free and `getLinks` finds nothing to harvest. A\n * paragraph is *unwrapped* into its inline children (`markdownBlocks.ts`), so\n * prose with a link arrives as `t + link + t` — separate parts. Reading only\n * the `t` parts would delete the link's url and label (DL #134's list bug) and\n * `getLinks` would then re-surface it as a footer link, showing the visitor the\n * same link twice. `inlinePartsToMarkdown` is what keeps the link inline, and\n * the prose path deliberately harvests no `relatedLinks` at all.\n */\nexport class CalloutSectionDefinition\n implements SectionDefinition<CalloutSectionComponent>\n{\n sectionType = 'callout' as const;\n patterns = [\n // Descriptor-led prose. `link` must be in the alternation or a callout\n // carrying an ask link fails to match and falls through to the fallback.\n 'html_comment[section=\"callout\"] > (t | link)+',\n // Legacy blockquote.\n 'html_comment? > callout+',\n ];\n\n defaults: CalloutCompProps = {\n callouts: [{ text: 'This is a callout.' }],\n };\n\n fixtures: SectionFixture<CalloutCompProps>[] = [\n {\n markdown:\n '<!-- section:callout, semantic:tip -->\\nThis is a callout paragraph.',\n expected: {\n callouts: [{ text: 'This is a callout paragraph.', kind: 'TIP' }],\n },\n },\n {\n markdown: '> This is a callout blockquote.',\n expected: {\n callouts: [{ text: 'This is a callout blockquote.' }],\n },\n },\n ];\n\n parse(parts: Part[]): CalloutCompProps | null | DropSection {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n\n return calloutParts.length > 0\n ? this.parseBlockquote(calloutParts, parts)\n : this.parseProse(parts);\n }\n\n /**\n * Descriptor + plain prose. The kind comes from the descriptor's `semantic`,\n * and every link in the body is inline prose — never a `relatedLinks` footer.\n */\n private parseProse(parts: Part[]): CalloutCompProps | null | DropSection {\n const descriptor = parts.find((p) => p.type === 'htmlComment' && p.meta)\n ?.meta as Record<string, string> | undefined;\n\n // The pattern already gates on this; re-checking keeps a direct\n // `parse()` call (and any future pattern edit) from mislabelling a\n // different section's prose as a callout.\n if (descriptor?.section !== 'callout') {\n return null;\n }\n\n const text = inlinePartsToMarkdown(\n parts.filter((p) => p.type !== 'htmlComment'),\n ).trim();\n\n // Matched, but there is nothing to say. DROP_SECTION consumes the nodes\n // and renders nothing; returning null would hand the descriptor to the\n // fallback, which renders it as stray text.\n if (!text) {\n return DROP_SECTION;\n }\n\n return {\n callouts: [{ text, ...(semanticToKind(descriptor.semantic) ?? {}) }],\n };\n }\n\n /** Legacy GFM alert blockquote. */\n private parseBlockquote(\n calloutParts: Part[],\n parts: Part[],\n ): CalloutCompProps | null | DropSection {\n const callouts: Callout[] = [];\n const relatedLinks: { text: string; url: string }[] = [];\n const seenUrls = new Set<string>();\n\n for (const c of calloutParts) {\n const text = (c.meta?.markdown as string)?.trim() || c.content?.trim();\n if (text) {\n const parsed = parseCalloutTag(text);\n if (parsed.text) {\n callouts.push({ text: parsed.text, kind: parsed.kind });\n }\n }\n }\n\n if (callouts.length === 0) {\n return DROP_SECTION;\n }\n\n // A blockquote yields no sibling link parts, so anything found here arrived\n // alongside the quote and is a genuine footer link.\n for (const link of getLinks(parts)) {\n const href = link.meta?.href;\n const text = link.content?.trim();\n if (text && href && !seenUrls.has(href)) {\n seenUrls.add(href);\n relatedLinks.push({ text, url: href });\n }\n }\n\n return {\n callouts,\n relatedLinks: relatedLinks.length > 0 ? relatedLinks : undefined,\n };\n }\n}\n\n/**\n * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the\n * same vocabulary the legacy `[!TIP]` marker produced, which is what any\n * kind-aware renderer already switches on. An unrecognised value carries no\n * kind rather than an invented one.\n */\nfunction semanticToKind(semantic?: string): { kind: string } | undefined {\n const value = semantic?.trim().toLowerCase();\n if (!value) {\n return undefined;\n }\n return (CALLOUT_SEMANTICS as readonly string[]).includes(value)\n ? { kind: value.toUpperCase() }\n : undefined;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["inlinePartsToMarkdown","parseCalloutTag","getPartsByType","getLinks","DROP_SECTION","CalloutSectionDefinition","constructor","_defineProperty","callouts","text","markdown","expected","kind","parse","parts","calloutParts","length","parseBlockquote","parseProse","_parts$find","descriptor","find","p","type","meta","section","filter","trim","semanticToKind","semantic","relatedLinks","seenUrls","Set","c","_c$meta","_c$content","content","parsed","push","link","_link$meta","_link$content","href","has","add","url","undefined","value","toUpperCase"],"sources":["../../../../src/component/componentDefinitions/CalloutSectionDefinition.ts"],"sourcesContent":["import { inlinePartsToMarkdown, parseCalloutTag } from './parse-utils';\nimport { getPartsByType, getLinks, type Part } from '../../parts/parts';\nimport type { Callout } from '../../types/callout';\nimport type {\n CalloutCompProps,\n CalloutSectionComponent,\n SectionFixture,\n} from '../types';\nimport {\n DROP_SECTION,\n type DropSection,\n type SectionDefinition,\n} from '../section-definition';\n\n/**\n * Callout — the page's dialog layer, written by the orchestrator itself rather\n * than by a per-section model.\n *\n * Two markdown forms reach this definition, and both must keep working:\n *\n * 1. **Descriptor + plain prose** (current). `semantic` carries the kind:\n *\n * <!-- section:callout, semantic:tip -->\n * Prefer a gentle daily ritual? The 4 oz size is the one to sample with.\n *\n * 2. **GFM alert blockquote** (legacy). Still emitted by sites pinned to an\n * older prompt, and present in every historical page, so it is parsed\n * verbatim rather than migrated:\n *\n * <!-- section:callout -->\n * > [!TIP] Prefer a gentle daily ritual? …\n *\n * The two forms differ in one way that matters. A blockquote collapses to a\n * single `callout` part carrying the whole line in `meta.markdown`, so inline\n * ask links survive for free and `getLinks` finds nothing to harvest. A\n * paragraph is *unwrapped* into its inline children (`markdownBlocks.ts`), so\n * prose with a link arrives as `t + link + t` — separate parts. Reading only\n * the `t` parts would delete the link's url and label (DL #134's list bug) and\n * `getLinks` would then re-surface it as a footer link, showing the visitor the\n * same link twice. `inlinePartsToMarkdown` is what keeps the link inline, and\n * the prose path deliberately harvests no `relatedLinks` at all.\n */\nexport class CalloutSectionDefinition\n implements SectionDefinition<CalloutSectionComponent>\n{\n sectionType = 'callout' as const;\n patterns = [\n // Descriptor-led prose. `link` must be in the alternation or a callout\n // carrying an ask link fails to match and falls through to the fallback.\n 'html_comment[section=\"callout\"] > (t | link)+',\n // Legacy blockquote.\n 'html_comment? > callout+',\n ];\n\n defaults: CalloutCompProps = {\n callouts: [{ text: 'This is a callout.' }],\n };\n\n fixtures: SectionFixture<CalloutCompProps>[] = [\n {\n markdown:\n '<!-- section:callout, semantic:tip -->\\nThis is a callout paragraph.',\n expected: {\n callouts: [{ text: 'This is a callout paragraph.', kind: 'TIP' }],\n },\n },\n {\n markdown: '> This is a callout blockquote.',\n expected: {\n callouts: [{ text: 'This is a callout blockquote.' }],\n },\n },\n ];\n\n parse(parts: Part[]): CalloutCompProps | null | DropSection {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n\n return calloutParts.length > 0\n ? this.parseBlockquote(calloutParts, parts)\n : this.parseProse(parts);\n }\n\n /**\n * Descriptor + plain prose. The kind comes from the descriptor's `semantic`,\n * and every link in the body is inline prose — never a `relatedLinks` footer.\n */\n private parseProse(parts: Part[]): CalloutCompProps | null | DropSection {\n const descriptor = parts.find((p) => p.type === 'htmlComment' && p.meta)\n ?.meta as Record<string, string> | undefined;\n\n // The pattern already gates on this; re-checking keeps a direct\n // `parse()` call (and any future pattern edit) from mislabelling a\n // different section's prose as a callout.\n if (descriptor?.section !== 'callout') {\n return null;\n }\n\n const text = inlinePartsToMarkdown(\n parts.filter((p) => p.type !== 'htmlComment'),\n ).trim();\n\n // Matched, but there is nothing to say. DROP_SECTION consumes the nodes\n // and renders nothing; returning null would hand the descriptor to the\n // fallback, which renders it as stray text.\n if (!text) {\n return DROP_SECTION;\n }\n\n return {\n callouts: [{ text, ...(semanticToKind(descriptor.semantic) ?? {}) }],\n };\n }\n\n /** Legacy GFM alert blockquote. */\n private parseBlockquote(\n calloutParts: Part[],\n parts: Part[],\n ): CalloutCompProps | null | DropSection {\n const callouts: Callout[] = [];\n const relatedLinks: { text: string; url: string }[] = [];\n const seenUrls = new Set<string>();\n\n for (const c of calloutParts) {\n const text = (c.meta?.markdown as string)?.trim() || c.content?.trim();\n if (text) {\n const parsed = parseCalloutTag(text);\n if (parsed.text) {\n callouts.push({ text: parsed.text, kind: parsed.kind });\n }\n }\n }\n\n if (callouts.length === 0) {\n return DROP_SECTION;\n }\n\n // A blockquote yields no sibling link parts, so anything found here arrived\n // alongside the quote and is a genuine footer link.\n for (const link of getLinks(parts)) {\n const href = link.meta?.href;\n const text = link.content?.trim();\n if (text && href && !seenUrls.has(href)) {\n seenUrls.add(href);\n relatedLinks.push({ text, url: href });\n }\n }\n\n return {\n callouts,\n relatedLinks: relatedLinks.length > 0 ? relatedLinks : undefined,\n };\n }\n}\n\n/**\n * `semantic:tip` → `{ kind: 'TIP' }`. Uppercased so the new form lands on the\n * same vocabulary the legacy `[!TIP]` marker produced, which is what any\n * kind-aware renderer already switches on.\n *\n * Deliberately NOT validated against a list. The prompts own this vocabulary,\n * and they live in the prompt catalog precisely so it can change without\n * shipping code — an allowlist here would mean every new semantic needs a\n * release before it resolves, which is backwards. Nothing gates on `kind`\n * either: it is metadata for a renderer that wants to vary by it, and the one\n * renderer that does falls back when it sees a value it doesn't know.\n *\n * `CALLOUT_SEMANTICS` therefore documents what the prompts emit today; it is\n * not a filter.\n */\nfunction semanticToKind(semantic?: string): { kind: string } | undefined {\n const value = semantic?.trim();\n return value ? { kind: value.toUpperCase() } : undefined;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -9,7 +9,11 @@ function cacheKey(entityType, entityId, variantId) {
|
|
|
9
9
|
}
|
|
10
10
|
function normalizeShopifyEntityType(entityType) {
|
|
11
11
|
const lower = entityType.toLowerCase();
|
|
12
|
-
|
|
12
|
+
// A variant is not a Storefront resource of its own — there is no
|
|
13
|
+
// `variantByHandle`. It is fetched as its parent product and then priced at
|
|
14
|
+
// the variant the lookup carries alongside, which is why `variant` and
|
|
15
|
+
// `product` normalize to the same query here rather than to two.
|
|
16
|
+
if (lower.includes('product') || lower.includes('variant')) {
|
|
13
17
|
return 'product';
|
|
14
18
|
}
|
|
15
19
|
if (lower.includes('collection')) {
|
|
@@ -45,15 +49,33 @@ function extractHandleFromUrl(url) {
|
|
|
45
49
|
}
|
|
46
50
|
return withoutLeadingSlash || undefined;
|
|
47
51
|
}
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The `?variant=` the row's own URL already carries.
|
|
55
|
+
*
|
|
56
|
+
* A row that names a variant — whether the turn overlaid one onto a product or
|
|
57
|
+
* sent the variant as the entity itself — always links to it, because that is
|
|
58
|
+
* the URL a shopper has to land on. So when the row omits `variantId` as a
|
|
59
|
+
* field, the URL still says which child it means, and pricing the parent's
|
|
60
|
+
* default instead would contradict the link on the same card.
|
|
61
|
+
*/
|
|
62
|
+
function variantIdFromUrl(url) {
|
|
63
|
+
var _URLSearchParams$get;
|
|
64
|
+
const query = url == null ? void 0 : url.split('#')[0].split('?')[1];
|
|
65
|
+
if (!query) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
return ((_URLSearchParams$get = new URLSearchParams(query).get('variant')) == null ? void 0 : _URLSearchParams$get.trim()) || undefined;
|
|
69
|
+
}
|
|
70
|
+
export function getShopifyLookup(entity) {
|
|
71
|
+
var _entity$data, _entity$data2, _entity$data3;
|
|
50
72
|
const entityType = normalizeShopifyEntityType(entity.entityType);
|
|
51
73
|
const urlHandle = extractHandleFromUrl((_entity$data = entity.data) == null ? void 0 : _entity$data.url);
|
|
52
74
|
const entityId = (entityType === 'product' || entityType === 'collection') && urlHandle ? urlHandle : entity.entityId;
|
|
53
75
|
return {
|
|
54
76
|
entityType,
|
|
55
77
|
entityId,
|
|
56
|
-
variantId: (_entity$data2 = entity.data) == null ? void 0 : _entity$data2.variantId
|
|
78
|
+
variantId: ((_entity$data2 = entity.data) == null ? void 0 : _entity$data2.variantId) ?? variantIdFromUrl((_entity$data3 = entity.data) == null ? void 0 : _entity$data3.url)
|
|
57
79
|
};
|
|
58
80
|
}
|
|
59
81
|
|
|
@@ -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","getShopifyLookup","entity","_entity$data","_entity$data2","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","get","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 if (lower.includes('product')) {\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\nfunction 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 { entityType, entityId, variantId: entity.data?.variantId };\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","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":[]}
|
|
@@ -7,6 +7,16 @@ export const CALLOUT_KINDS = ['NOTE', 'TIP', 'IMPORTANT', 'WARNING', 'CAUTION',
|
|
|
7
7
|
* the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever
|
|
8
8
|
* accepted, because historical pages and sites pinned to an older prompt still
|
|
9
9
|
* carry them. This list is what the current prompts are allowed to emit.
|
|
10
|
+
*
|
|
11
|
+
* **Documentation, not a filter.** Nothing validates against this list: the
|
|
12
|
+
* prompts own the vocabulary and live in the prompt catalog so it can change
|
|
13
|
+
* without a release. Adding a semantic here is a docs change, not a gate.
|
|
14
|
+
*
|
|
15
|
+
* `clarification` is the one whose meaning is load-bearing rather than
|
|
16
|
+
* cosmetic: the prompts define it as a callout that offers the visitor a branch,
|
|
17
|
+
* and therefore one that carries at least one `web5://ask/` link. Naming the
|
|
18
|
+
* intent in the descriptor is what makes that obligation checkable — a callout
|
|
19
|
+
* labelled `clarification` with nothing linked is wrong output.
|
|
10
20
|
*/
|
|
11
|
-
export const CALLOUT_SEMANTICS = ['note', 'tip'];
|
|
21
|
+
export const CALLOUT_SEMANTICS = ['note', 'tip', 'clarification'];
|
|
12
22
|
//# sourceMappingURL=callout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CALLOUT_KINDS","CALLOUT_SEMANTICS"],"sources":["../../../src/types/callout.ts"],"sourcesContent":["export const CALLOUT_KINDS = [\n 'NOTE',\n 'TIP',\n 'IMPORTANT',\n 'WARNING',\n 'CAUTION',\n 'ECHO',\n] as const;\n\nexport type CalloutKind = (typeof CALLOUT_KINDS)[number];\n\n/**\n * The kinds a callout descriptor may declare as `semantic:`, lowercase, as the\n * orchestrator writes them: `<!-- section:callout, semantic:tip -->`.\n *\n * Deliberately narrower than `CALLOUT_KINDS`. That list is the vocabulary of\n * the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever\n * accepted, because historical pages and sites pinned to an older prompt still\n * carry them. This list is what the current prompts are allowed to emit.\n */\nexport const CALLOUT_SEMANTICS = ['note', 'tip'] as const;\n\nexport type CalloutSemantic = (typeof CALLOUT_SEMANTICS)[number];\n\nexport interface Callout {\n text: string;\n kind?: string;\n}\n"],"mappings":"AAAA,OAAO,MAAMA,aAAa,GAAG,CAC3B,MAAM,EACN,KAAK,EACL,WAAW,EACX,SAAS,EACT,SAAS,EACT,MAAM,CACE;AAIV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,GAAG,CAAC,MAAM,EAAE,KAAK,CAAU","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["CALLOUT_KINDS","CALLOUT_SEMANTICS"],"sources":["../../../src/types/callout.ts"],"sourcesContent":["export const CALLOUT_KINDS = [\n 'NOTE',\n 'TIP',\n 'IMPORTANT',\n 'WARNING',\n 'CAUTION',\n 'ECHO',\n] as const;\n\nexport type CalloutKind = (typeof CALLOUT_KINDS)[number];\n\n/**\n * The kinds a callout descriptor may declare as `semantic:`, lowercase, as the\n * orchestrator writes them: `<!-- section:callout, semantic:tip -->`.\n *\n * Deliberately narrower than `CALLOUT_KINDS`. That list is the vocabulary of\n * the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever\n * accepted, because historical pages and sites pinned to an older prompt still\n * carry them. This list is what the current prompts are allowed to emit.\n *\n * **Documentation, not a filter.** Nothing validates against this list: the\n * prompts own the vocabulary and live in the prompt catalog so it can change\n * without a release. Adding a semantic here is a docs change, not a gate.\n *\n * `clarification` is the one whose meaning is load-bearing rather than\n * cosmetic: the prompts define it as a callout that offers the visitor a branch,\n * and therefore one that carries at least one `web5://ask/` link. Naming the\n * intent in the descriptor is what makes that obligation checkable — a callout\n * labelled `clarification` with nothing linked is wrong output.\n */\nexport const CALLOUT_SEMANTICS = ['note', 'tip', 'clarification'] as const;\n\nexport type CalloutSemantic = (typeof CALLOUT_SEMANTICS)[number];\n\nexport interface Callout {\n text: string;\n kind?: string;\n}\n"],"mappings":"AAAA,OAAO,MAAMA,aAAa,GAAG,CAC3B,MAAM,EACN,KAAK,EACL,WAAW,EACX,SAAS,EACT,SAAS,EACT,MAAM,CACE;AAIV;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,iBAAiB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,eAAe,CAAU","ignoreList":[]}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { ShopifyStorefrontConfig } from '../services/shopify/types';
|
|
2
2
|
import type { EntityItemData } from '../component/types';
|
|
3
|
+
export declare function getShopifyLookup(entity: {
|
|
4
|
+
entityId: string;
|
|
5
|
+
entityType: string;
|
|
6
|
+
data?: EntityItemData;
|
|
7
|
+
}): {
|
|
8
|
+
entityType: string;
|
|
9
|
+
entityId: string;
|
|
10
|
+
variantId: string | undefined;
|
|
11
|
+
};
|
|
3
12
|
/**
|
|
4
13
|
* The turn owns WHICH thing to show; the live fetch owns VOLATILE FACTS about
|
|
5
14
|
* it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResolveShopifyEntityData.d.ts","sourceRoot":"","sources":["../../../src/hooks/useResolveShopifyEntityData.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"useResolveShopifyEntityData.d.ts","sourceRoot":"","sources":["../../../src/hooks/useResolveShopifyEntityData.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgFzD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB;;;;EAYA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,QAAQ,EAAE,cAAc,GACvB,cAAc,CAgBhB;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,SAAS;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,EAED,QAAQ,EAAE,OAAO,EAAE,EACnB,aAAa,EAAE,uBAAuB,GACrC;IAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAwInD"}
|
|
@@ -8,8 +8,18 @@ export type CalloutKind = (typeof CALLOUT_KINDS)[number];
|
|
|
8
8
|
* the legacy `> [!TYPE]` blockquote marker and has to keep every value it ever
|
|
9
9
|
* accepted, because historical pages and sites pinned to an older prompt still
|
|
10
10
|
* carry them. This list is what the current prompts are allowed to emit.
|
|
11
|
+
*
|
|
12
|
+
* **Documentation, not a filter.** Nothing validates against this list: the
|
|
13
|
+
* prompts own the vocabulary and live in the prompt catalog so it can change
|
|
14
|
+
* without a release. Adding a semantic here is a docs change, not a gate.
|
|
15
|
+
*
|
|
16
|
+
* `clarification` is the one whose meaning is load-bearing rather than
|
|
17
|
+
* cosmetic: the prompts define it as a callout that offers the visitor a branch,
|
|
18
|
+
* and therefore one that carries at least one `web5://ask/` link. Naming the
|
|
19
|
+
* intent in the descriptor is what makes that obligation checkable — a callout
|
|
20
|
+
* labelled `clarification` with nothing linked is wrong output.
|
|
11
21
|
*/
|
|
12
|
-
export declare const CALLOUT_SEMANTICS: readonly ["note", "tip"];
|
|
22
|
+
export declare const CALLOUT_SEMANTICS: readonly ["note", "tip", "clarification"];
|
|
13
23
|
export type CalloutSemantic = (typeof CALLOUT_SEMANTICS)[number];
|
|
14
24
|
export interface Callout {
|
|
15
25
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callout.d.ts","sourceRoot":"","sources":["../../../src/types/callout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,qEAOhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD
|
|
1
|
+
{"version":3,"file":"callout.d.ts","sourceRoot":"","sources":["../../../src/types/callout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,qEAOhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,2CAA4C,CAAC;AAE3E,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/web5-core",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "1.63.
|
|
4
|
+
"version": "1.63.13",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tsachis",
|
|
7
7
|
"email": "tsachis@wix.com"
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"wallaby": {
|
|
101
101
|
"autoDetect": true
|
|
102
102
|
},
|
|
103
|
-
"falconPackageHash": "
|
|
103
|
+
"falconPackageHash": "b1dae343a7e5206a65e694799d53a16d16e1f8b9bfa9eebec43fad45"
|
|
104
104
|
}
|