@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.
- package/dist/cjs/component/componentDefinitions/TextBlockSectionDefinition.js +36 -32
- package/dist/cjs/component/componentDefinitions/TextBlockSectionDefinition.js.map +1 -1
- package/dist/cjs/component/componentDefinitions/index.js +5 -4
- package/dist/cjs/component/componentDefinitions/index.js.map +1 -1
- package/dist/cjs/component/componentDefinitions/parse-utils.js +43 -6
- package/dist/cjs/component/componentDefinitions/parse-utils.js.map +1 -1
- package/dist/cjs/entity/matchedVariant.js +31 -9
- package/dist/cjs/entity/matchedVariant.js.map +1 -1
- package/dist/cjs/hooks/useResolveShopifyEntityData.js +6 -0
- package/dist/cjs/hooks/useResolveShopifyEntityData.js.map +1 -1
- package/dist/cjs/match/astToParts.js +14 -0
- package/dist/cjs/match/astToParts.js.map +1 -1
- package/dist/cjs/services/shopify/transformShopifyEntity.js +17 -5
- package/dist/cjs/services/shopify/transformShopifyEntity.js.map +1 -1
- package/dist/esm/component/componentDefinitions/TextBlockSectionDefinition.js +37 -33
- package/dist/esm/component/componentDefinitions/TextBlockSectionDefinition.js.map +1 -1
- package/dist/esm/component/componentDefinitions/index.js +5 -4
- package/dist/esm/component/componentDefinitions/index.js.map +1 -1
- package/dist/esm/component/componentDefinitions/parse-utils.js +42 -6
- package/dist/esm/component/componentDefinitions/parse-utils.js.map +1 -1
- package/dist/esm/entity/matchedVariant.js +31 -9
- package/dist/esm/entity/matchedVariant.js.map +1 -1
- package/dist/esm/hooks/useResolveShopifyEntityData.js +6 -0
- package/dist/esm/hooks/useResolveShopifyEntityData.js.map +1 -1
- package/dist/esm/match/astToParts.js +14 -0
- package/dist/esm/match/astToParts.js.map +1 -1
- package/dist/esm/services/shopify/transformShopifyEntity.js +17 -5
- package/dist/esm/services/shopify/transformShopifyEntity.js.map +1 -1
- package/dist/types/component/componentDefinitions/TextBlockSectionDefinition.d.ts.map +1 -1
- package/dist/types/component/componentDefinitions/index.d.ts.map +1 -1
- package/dist/types/component/componentDefinitions/parse-utils.d.ts +21 -0
- package/dist/types/component/componentDefinitions/parse-utils.d.ts.map +1 -1
- package/dist/types/entity/matchedVariant.d.ts +18 -9
- package/dist/types/entity/matchedVariant.d.ts.map +1 -1
- package/dist/types/hooks/useResolveShopifyEntityData.d.ts.map +1 -1
- package/dist/types/match/astToParts.d.ts.map +1 -1
- package/dist/types/services/shopify/transformShopifyEntity.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -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","candidates","candidate","code","toUpperCase","test","formatPriceField","
|
|
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","candidates","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":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAMA,SAASA,QAAQA,CAACC,KAAc,EAAU;EACxC,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE;IAC7B,OAAOA,KAAK,CAACC,IAAI,CAAC,CAAC;EACrB;EACA,IAAI,OAAOD,KAAK,KAAK,QAAQ,IAAIE,MAAM,CAACC,QAAQ,CAACH,KAAK,CAAC,EAAE;IACvD,OAAOI,MAAM,CAACJ,KAAK,CAAC;EACtB;EACA,OAAO,EAAE;AACX;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASK,gBAAgBA,CAACC,GAAY,EAA+B;EAC1E,IAAI,CAACA,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ,IAAIC,KAAK,CAACC,OAAO,CAACF,GAAG,CAAC,EAAE;IACzD,OAAOG,SAAS;EAClB;EAEA,MAAMC,KAAsB,GAAG,EAAE;EACjC,KAAK,MAAM,CAACC,OAAO,EAAEC,QAAQ,CAAC,IAAIC,MAAM,CAACC,OAAO,CAC9CR,GACF,CAAC,EAAE;IACD,MAAMS,IAAI,GAAGJ,OAAO,CAACV,IAAI,CAAC,CAAC;IAC3B,MAAMD,KAAK,GAAGD,QAAQ,CAACa,QAAQ,CAAC;IAChC,IAAIG,IAAI,IAAIf,KAAK,EAAE;MACjBU,KAAK,CAACM,IAAI,CAAC;QAAED,IAAI;QAAEf;MAAM,CAAC,CAAC;IAC7B;EACF;EAEA,OAAOU,KAAK,CAACO,MAAM,GAAGP,KAAK,GAAGD,SAAS;AACzC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASS,WAAWA,CAACC,MAAc,EAAEC,QAAiB,EAAU;EACrE,IAAI,CAACA,QAAQ,EAAE;IACb,OAAOhB,MAAM,CAACe,MAAM,CAAC;EACvB;EACA,IAAI;IACF,OAAO,IAAIE,IAAI,CAACC,YAAY,CAACb,SAAS,EAAE;MACtCc,KAAK,EAAE,UAAU;MACjBH,QAAQ;MACRI,qBAAqB,EAAEtB,MAAM,CAACuB,SAAS,CAACN,MAAM,CAAC,GAAG,CAAC,GAAG;IACxD,CAAC,CAAC,CAACO,MAAM,CAACP,MAAM,CAAC;EACnB,CAAC,CAAC,MAAM;IACN;IACA,OAAO,GAAGA,MAAM,IAAIC,QAAQ,EAAE;EAChC;AACF;;AAEA;AACO,SAASO,gBAAgBA,CAAC,GAAGC,UAAqB,EAAsB;EAC7E,KAAK,MAAMC,SAAS,IAAID,UAAU,EAAE;IAClC,IAAI,OAAOC,SAAS,KAAK,QAAQ,EAAE;MACjC;IACF;IACA,MAAMC,IAAI,GAAGD,SAAS,CAAC5B,IAAI,CAAC,CAAC,CAAC8B,WAAW,CAAC,CAAC;IAC3C,IAAI,YAAY,CAACC,IAAI,CAACF,IAAI,CAAC,EAAE;MAC3B,OAAOA,IAAI;IACb;EACF;EACA,OAAOrB,SAAS;AAClB;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASwB,gBAAgBA,CAC9BjC,KAAc,EACdoB,QAAiB,EACG;EACpB,IAAI,OAAOpB,KAAK,KAAK,QAAQ,IAAIE,MAAM,CAACC,QAAQ,CAACH,KAAK,CAAC,EAAE;IACvD,OAAOkB,WAAW,CAAClB,KAAK,EAAEoB,QAAQ,CAAC;EACrC;EACA,IAAI,OAAOpB,KAAK,KAAK,QAAQ,IAAIA,KAAK,CAACC,IAAI,CAAC,CAAC,EAAE;IAC7C,OAAOD,KAAK,CAACC,IAAI,CAAC,CAAC;EACrB;EACA,OAAOQ,SAAS;AAClB;;AAEA;;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASyB,eAAeA,CAACC,MAA0B,EAAW;EAAA,IAAAC,qBAAA;EAC5D,OAAOC,OAAO,CAACF,MAAM,CAACG,SAAS,CAAC,IAAID,OAAO,EAAAD,qBAAA,GAACD,MAAM,CAACI,cAAc,qBAArBH,qBAAA,CAAuBnB,MAAM,CAAC;AAC5E;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASuB,uBAAuBA,CACrCL,MAA0B,EACN;EACpB,MAAM;IAAEM,KAAK;IAAEC,QAAQ;IAAEC;EAAS,CAAC,GAAGR,MAAM;EAC5C,IAAIM,KAAK,IAAIP,eAAe,CAACC,MAAM,CAAC,EAAE;IACpC,OAAOM,KAAK;EACd;EACA,IAAIC,QAAQ,IAAIC,QAAQ,IAAID,QAAQ,KAAKC,QAAQ,EAAE;IACjD,OAAO,QAAQD,QAAQ,EAAE;EAC3B;EACA,OAAOD,KAAK;AACd","ignoreList":[]}
|
|
@@ -107,6 +107,12 @@ function mergeShopifyEntityData(existing, resolved) {
|
|
|
107
107
|
url: existing.url || resolved.url,
|
|
108
108
|
title: existing.title || resolved.title,
|
|
109
109
|
imageUrl: existing.imageUrl || resolved.imageUrl,
|
|
110
|
+
// The live fetch withholds a price when it was asked about a variant it
|
|
111
|
+
// could not find, rather than answering with the product's floor. Its
|
|
112
|
+
// silence must not erase the turn's number, which was about the right
|
|
113
|
+
// child; a spread would overwrite with `undefined` and leave the card
|
|
114
|
+
// priceless.
|
|
115
|
+
price: resolved.price ?? existing.price,
|
|
110
116
|
variantId: existing.variantId || resolved.variantId,
|
|
111
117
|
matchedOptions: (_existing$matchedOpti = existing.matchedOptions) != null && _existing$matchedOpti.length ? existing.matchedOptions : resolved.matchedOptions
|
|
112
118
|
};
|
|
@@ -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","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":[]}
|
|
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","price","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 // 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":"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","ignoreList":[]}
|
|
@@ -333,6 +333,13 @@ function nodesToParts(nodes) {
|
|
|
333
333
|
const parts = [];
|
|
334
334
|
const typeCounts = {};
|
|
335
335
|
const childCursors = new Map();
|
|
336
|
+
// A paragraph carrying an inline link is unwrapped into several block
|
|
337
|
+
// elements (`t`, `link`, `t`), so its parts are siblings with nothing to say
|
|
338
|
+
// they came from one paragraph. Mark every part after the first of a given
|
|
339
|
+
// AST node, so a consumer rebuilding prose can tell "same paragraph, keep
|
|
340
|
+
// going" from "next paragraph, break". Only split nodes carry the flag —
|
|
341
|
+
// plain paragraphs, headings and images stay meta-free.
|
|
342
|
+
const seenNodes = new Set();
|
|
336
343
|
for (const block of enrichedBlocks) {
|
|
337
344
|
const astNode = nodes[block.astNodeIndex];
|
|
338
345
|
const type = blockElementToPartType(block.element);
|
|
@@ -356,6 +363,13 @@ function nodesToParts(nodes) {
|
|
|
356
363
|
markdown: source.markdown.trim()
|
|
357
364
|
};
|
|
358
365
|
}
|
|
366
|
+
if (seenNodes.has(block.astNodeIndex)) {
|
|
367
|
+
part.meta = {
|
|
368
|
+
...part.meta,
|
|
369
|
+
continuesNode: true
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
seenNodes.add(block.astNodeIndex);
|
|
359
373
|
|
|
360
374
|
// For lists whose items are all complex, the list-level content is
|
|
361
375
|
// meaningless (just a concatenation of child text). Clear it so that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_markdownBlocks","require","_parts","_parseUtils","_entityLinkParser","_linkTypes","blockElementToPartType","element","startsWith","findNode","node","type","Array","isArray","children","child","found","extractTableCellLabel","parsed","url","parseWeb5Url","Web5UrlType","ICON","alt","value","map","join","tableCellMeta","cell","label","trim","link","href","undefined","image","imageUrl","iconParsed","ENTITY","entityType","entityId","iconHint","path","resolveSource","block","astNode","cursors","n","content","extractContent","md","extractContentMarkdown","markdown","cursor","get","astNodeIndex","target","i","length","set","text","buildListItems","listNode","item","itemParts","nodesToParts","isComplex","part","role","meta","parts","buildMeta","sourceNode","level","depth","lm","linkMetadata","title","isEntity","img","src","iconImg","iconUrl","web5Match","match","items","ordered","rows","headerRow","headerCells","columnLabels","featureColumnLabel","dataRows","slice","row","cells","firstCell","values","feature","columns","raw","kind","parseCalloutTag","htmlCommentMetadata","nodes","enrichedBlocks","convertToBlockElements","typeCounts","childCursors","Map","count","source","deriveRole","allComplex","every","it","push"],"sources":["../../../src/match/astToParts.ts"],"sourcesContent":["import type { RootContent } from 'mdast';\nimport { convertToBlockElements } from '../markdownBlocks';\nimport type {\n EnrichedBlockElement,\n BlockElement,\n} from '../patternValidator/types';\nimport {\n type Part,\n type PartType,\n extractContent,\n extractContentMarkdown,\n deriveRole,\n} from '../parts/parts';\nimport { parseCalloutTag } from '../component/componentDefinitions/parse-utils';\nimport { parseWeb5Url } from '../utils/entityLinkParser';\nimport { Web5UrlType } from '../types/link-types';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction blockElementToPartType(element: BlockElement | string): PartType {\n if (typeof element === 'string' && element.startsWith('list{')) {\n return 'list';\n }\n switch (element) {\n case 'h1':\n case 'h2':\n case 'h3':\n case 'h4':\n case 'h5':\n case 'h6':\n return 'heading';\n case 't':\n return 'text';\n case 'link':\n return 'link';\n case 'image':\n return 'image';\n case 'icon':\n return 'icon';\n case 'list':\n return 'list';\n case 'table':\n return 'table';\n case 'blockquote':\n return 'blockquote';\n case 'callout':\n return 'callout';\n case 'html_comment':\n return 'htmlComment';\n case 'thematicBreak':\n return 'thematicBreak';\n default:\n return 'text';\n }\n}\n\n/** Find a descendant node of a given type */\nfunction findNode(node: any, type: string): any {\n if (!node) {\n return null;\n }\n if (node.type === type) {\n return node;\n }\n if (Array.isArray(node.children)) {\n for (const child of node.children) {\n const found = findNode(child, type);\n if (found) {\n return found;\n }\n }\n }\n return null;\n}\n\nfunction extractTableCellLabel(node: any): string {\n if (!node) {\n return '';\n }\n if (node.type === 'image') {\n const parsed =\n typeof node.url === 'string' ? parseWeb5Url(node.url) : null;\n return parsed?.type === Web5UrlType.ICON ? '' : node.alt || '';\n }\n if (typeof node.value === 'string') {\n return node.value;\n }\n if (Array.isArray(node.children)) {\n return node.children.map(extractTableCellLabel).join('');\n }\n return '';\n}\n\nfunction tableCellMeta(cell: any): {\n label: string;\n href?: string;\n entityType?: string;\n entityId?: string;\n iconHint?: string;\n} {\n const label = extractTableCellLabel(cell).trim();\n const link = findNode(cell, 'link');\n const href = typeof link?.url === 'string' ? link.url : undefined;\n const parsed = href ? parseWeb5Url(href) : null;\n const image = findNode(cell, 'image');\n const imageUrl = typeof image?.url === 'string' ? image.url : '';\n const iconParsed = imageUrl ? parseWeb5Url(imageUrl) : null;\n\n return {\n label,\n ...(href ? { href } : {}),\n ...(parsed?.type === Web5UrlType.ENTITY\n ? {\n entityType: parsed.entityType,\n entityId: parsed.entityId,\n }\n : {}),\n ...(iconParsed?.type === Web5UrlType.ICON\n ? { iconHint: iconParsed.path }\n : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Source resolution — maps each enriched block back to its specific AST child\n// ---------------------------------------------------------------------------\n\ninterface ResolvedSource {\n content: string;\n markdown?: string;\n node: any;\n}\n\n/**\n * For non-paragraph nodes the whole AST node is the source.\n * For paragraphs, walk children with a cursor so that each block\n * (link, image, text-run) resolves to its own specific child(ren).\n */\nfunction resolveSource(\n block: EnrichedBlockElement,\n astNode: RootContent,\n cursors: Map<number, number>,\n): ResolvedSource {\n const n = astNode as any;\n\n if (n.type !== 'paragraph') {\n const content = extractContent(astNode);\n const md = extractContentMarkdown(astNode);\n return { content, markdown: md !== content ? md : undefined, node: n };\n }\n\n const children: any[] = n.children || [];\n const cursor = cursors.get(block.astNodeIndex) || 0;\n\n if (\n block.element === 'link' ||\n block.element === 'image' ||\n block.element === 'icon'\n ) {\n const target = block.element === 'icon' ? 'image' : block.element;\n for (let i = cursor; i < children.length; i++) {\n const found =\n children[i].type === target\n ? children[i]\n : findNode(children[i], target);\n if (found) {\n cursors.set(block.astNodeIndex, i + 1);\n return { content: extractContent(found), node: found };\n }\n }\n return { content: '', node: n };\n }\n\n if (block.element === 't') {\n let text = '';\n let md = '';\n let i = cursor;\n for (; i < children.length; i++) {\n const child = children[i];\n if (child.type === 'link' || child.type === 'image') {\n break;\n }\n // Also stop at formatting wrappers that contain links/images\n if (findNode(child, 'link') || findNode(child, 'image')) {\n break;\n }\n text += extractContent(child);\n md += extractContentMarkdown(child);\n }\n cursors.set(block.astNodeIndex, i);\n // Preserve markdown when it differs from plain text (has inline formatting)\n return { content: text, markdown: md !== text ? md : undefined, node: n };\n }\n\n return { content: extractContent(astNode), node: n };\n}\n\n// ---------------------------------------------------------------------------\n// Meta extraction — uses the resolved source node, not the whole parent\n// ---------------------------------------------------------------------------\n\n/**\n * Decompose list items into ListItemParts, each carrying nested Part[] in meta.\n * Reuses nodesToParts to resolve each item's inline content (links, images, text, etc.).\n */\nfunction buildListItems(listNode: any): Part[] {\n return (listNode.children || []).map((item: any) => {\n const itemParts = nodesToParts(item.children || []);\n\n // A \"complex\" item has more than plain text (e.g. links, images, mixed\n // inline elements). In that case content is meaningless — the real data\n // lives inside the nested parts.\n const isComplex =\n itemParts.length > 1 ||\n (itemParts.length === 1 && itemParts[0].type !== 'text');\n\n const part: Part = {\n type: 'listItem',\n role: 'list-item',\n content: isComplex ? '' : extractContent(item).trim(),\n };\n if (itemParts.length > 0) {\n const markdown = extractContentMarkdown(item).trim();\n part.meta = {\n parts: itemParts,\n // Always store markdown so consumers can access the original formatting\n // (e.g. **bold** titles). For simple items, content already has plain\n // text; markdown preserves inline formatting.\n ...(markdown ? { markdown } : {}),\n };\n }\n return part;\n });\n}\n\nfunction buildMeta(\n type: PartType,\n sourceNode: any,\n block: EnrichedBlockElement,\n): Record<string, unknown> | undefined {\n if (!sourceNode) {\n return undefined;\n }\n switch (type) {\n case 'heading':\n return { level: sourceNode.depth };\n case 'link': {\n const link =\n sourceNode.type === 'link' ? sourceNode : findNode(sourceNode, 'link');\n const lm = block.linkMetadata;\n return {\n href: lm?.url || link?.url || '',\n ...(link?.title ? { title: link.title } : {}),\n ...(lm?.entityType\n ? { isEntity: true, entityType: lm.entityType }\n : {}),\n };\n }\n case 'image': {\n const img =\n sourceNode.type === 'image'\n ? sourceNode\n : findNode(sourceNode, 'image');\n return img\n ? { src: img.url || '', ...(img.alt ? { alt: img.alt } : {}) }\n : undefined;\n }\n case 'icon': {\n const iconImg =\n sourceNode.type === 'image'\n ? sourceNode\n : findNode(sourceNode, 'image');\n const iconUrl: string = iconImg?.url || '';\n const web5Match = iconUrl.match(/^web5:\\/\\/icon\\/(.+)/);\n const iconHint = web5Match?.[1] || '';\n return iconHint ? { iconHint } : undefined;\n }\n case 'list': {\n const items = buildListItems(sourceNode);\n return { ordered: !!sourceNode.ordered, items };\n }\n case 'table': {\n const rows: any[] = sourceNode.children || [];\n if (rows.length === 0) {\n return undefined;\n }\n const headerRow = rows[0];\n const headerCells: ReturnType<typeof tableCellMeta>[] = (\n headerRow?.children || []\n ).map((cell: any) => tableCellMeta(cell));\n const columnLabels = headerCells.map((cell) => cell.label);\n const featureColumnLabel = columnLabels[0] || 'Feature';\n const dataRows = rows.slice(1).map((row: any) => {\n const cells = row.children || [];\n const firstCell = tableCellMeta(cells[0]);\n const values = cells\n .slice(1)\n .map((cell: any) => extractContent(cell).trim());\n return {\n feature: firstCell.label || '',\n ...(firstCell.iconHint ? { iconHint: firstCell.iconHint } : {}),\n values,\n };\n });\n return {\n columnLabels: columnLabels.slice(1),\n columns: headerCells.slice(1),\n featureColumnLabel,\n rows: dataRows,\n };\n }\n case 'callout': {\n const raw = extractContent(sourceNode).trim();\n const { kind } = parseCalloutTag(raw);\n return kind ? { kind } : undefined;\n }\n case 'htmlComment':\n return block.htmlCommentMetadata\n ? { ...block.htmlCommentMetadata }\n : undefined;\n default:\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Convert markdown AST nodes into a flat array of `Part` objects.\n *\n * Each enriched block element becomes one `Part`. When a single AST node\n * (e.g. a paragraph) produces multiple blocks (link + image + text), each\n * block becomes a separate Part with content resolved from its specific\n * child within the paragraph.\n *\n * @param nodes - Markdown AST nodes (typically a slice of `root.children`)\n * @returns Ordered array of Part objects\n */\nexport function nodesToParts(nodes: RootContent[]): Part[] {\n if (!nodes || nodes.length === 0) {\n return [];\n }\n\n const enrichedBlocks = convertToBlockElements(nodes);\n const parts: Part[] = [];\n const typeCounts: Record<string, number> = {};\n const childCursors = new Map<number, number>();\n\n for (const block of enrichedBlocks) {\n const astNode = nodes[block.astNodeIndex];\n const type = blockElementToPartType(block.element);\n const count = (typeCounts[type] || 0) + 1;\n typeCounts[type] = count;\n\n const source = resolveSource(block, astNode, childCursors);\n\n const part: Part = {\n type,\n role: deriveRole(type, count),\n content: source.content.trim(),\n };\n const meta = buildMeta(type, source.node, block);\n if (meta) {\n part.meta = meta;\n }\n\n // Preserve original markdown when inline formatting exists (bold, italic, etc.)\n if (source.markdown) {\n part.meta = { ...part.meta, markdown: source.markdown.trim() };\n }\n\n // For lists whose items are all complex, the list-level content is\n // meaningless (just a concatenation of child text). Clear it so that\n // consumers rely on the structured items in meta instead.\n if (type === 'list' && meta) {\n const items = (meta.items as Part[]) || [];\n const allComplex =\n items.length > 0 && items.every((it) => it.content === '');\n if (allComplex) {\n part.content = '';\n }\n }\n\n parts.push(part);\n }\n\n return parts;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_markdownBlocks","require","_parts","_parseUtils","_entityLinkParser","_linkTypes","blockElementToPartType","element","startsWith","findNode","node","type","Array","isArray","children","child","found","extractTableCellLabel","parsed","url","parseWeb5Url","Web5UrlType","ICON","alt","value","map","join","tableCellMeta","cell","label","trim","link","href","undefined","image","imageUrl","iconParsed","ENTITY","entityType","entityId","iconHint","path","resolveSource","block","astNode","cursors","n","content","extractContent","md","extractContentMarkdown","markdown","cursor","get","astNodeIndex","target","i","length","set","text","buildListItems","listNode","item","itemParts","nodesToParts","isComplex","part","role","meta","parts","buildMeta","sourceNode","level","depth","lm","linkMetadata","title","isEntity","img","src","iconImg","iconUrl","web5Match","match","items","ordered","rows","headerRow","headerCells","columnLabels","featureColumnLabel","dataRows","slice","row","cells","firstCell","values","feature","columns","raw","kind","parseCalloutTag","htmlCommentMetadata","nodes","enrichedBlocks","convertToBlockElements","typeCounts","childCursors","Map","seenNodes","Set","count","source","deriveRole","has","continuesNode","add","allComplex","every","it","push"],"sources":["../../../src/match/astToParts.ts"],"sourcesContent":["import type { RootContent } from 'mdast';\nimport { convertToBlockElements } from '../markdownBlocks';\nimport type {\n EnrichedBlockElement,\n BlockElement,\n} from '../patternValidator/types';\nimport {\n type Part,\n type PartType,\n extractContent,\n extractContentMarkdown,\n deriveRole,\n} from '../parts/parts';\nimport { parseCalloutTag } from '../component/componentDefinitions/parse-utils';\nimport { parseWeb5Url } from '../utils/entityLinkParser';\nimport { Web5UrlType } from '../types/link-types';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction blockElementToPartType(element: BlockElement | string): PartType {\n if (typeof element === 'string' && element.startsWith('list{')) {\n return 'list';\n }\n switch (element) {\n case 'h1':\n case 'h2':\n case 'h3':\n case 'h4':\n case 'h5':\n case 'h6':\n return 'heading';\n case 't':\n return 'text';\n case 'link':\n return 'link';\n case 'image':\n return 'image';\n case 'icon':\n return 'icon';\n case 'list':\n return 'list';\n case 'table':\n return 'table';\n case 'blockquote':\n return 'blockquote';\n case 'callout':\n return 'callout';\n case 'html_comment':\n return 'htmlComment';\n case 'thematicBreak':\n return 'thematicBreak';\n default:\n return 'text';\n }\n}\n\n/** Find a descendant node of a given type */\nfunction findNode(node: any, type: string): any {\n if (!node) {\n return null;\n }\n if (node.type === type) {\n return node;\n }\n if (Array.isArray(node.children)) {\n for (const child of node.children) {\n const found = findNode(child, type);\n if (found) {\n return found;\n }\n }\n }\n return null;\n}\n\nfunction extractTableCellLabel(node: any): string {\n if (!node) {\n return '';\n }\n if (node.type === 'image') {\n const parsed =\n typeof node.url === 'string' ? parseWeb5Url(node.url) : null;\n return parsed?.type === Web5UrlType.ICON ? '' : node.alt || '';\n }\n if (typeof node.value === 'string') {\n return node.value;\n }\n if (Array.isArray(node.children)) {\n return node.children.map(extractTableCellLabel).join('');\n }\n return '';\n}\n\nfunction tableCellMeta(cell: any): {\n label: string;\n href?: string;\n entityType?: string;\n entityId?: string;\n iconHint?: string;\n} {\n const label = extractTableCellLabel(cell).trim();\n const link = findNode(cell, 'link');\n const href = typeof link?.url === 'string' ? link.url : undefined;\n const parsed = href ? parseWeb5Url(href) : null;\n const image = findNode(cell, 'image');\n const imageUrl = typeof image?.url === 'string' ? image.url : '';\n const iconParsed = imageUrl ? parseWeb5Url(imageUrl) : null;\n\n return {\n label,\n ...(href ? { href } : {}),\n ...(parsed?.type === Web5UrlType.ENTITY\n ? {\n entityType: parsed.entityType,\n entityId: parsed.entityId,\n }\n : {}),\n ...(iconParsed?.type === Web5UrlType.ICON\n ? { iconHint: iconParsed.path }\n : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Source resolution — maps each enriched block back to its specific AST child\n// ---------------------------------------------------------------------------\n\ninterface ResolvedSource {\n content: string;\n markdown?: string;\n node: any;\n}\n\n/**\n * For non-paragraph nodes the whole AST node is the source.\n * For paragraphs, walk children with a cursor so that each block\n * (link, image, text-run) resolves to its own specific child(ren).\n */\nfunction resolveSource(\n block: EnrichedBlockElement,\n astNode: RootContent,\n cursors: Map<number, number>,\n): ResolvedSource {\n const n = astNode as any;\n\n if (n.type !== 'paragraph') {\n const content = extractContent(astNode);\n const md = extractContentMarkdown(astNode);\n return { content, markdown: md !== content ? md : undefined, node: n };\n }\n\n const children: any[] = n.children || [];\n const cursor = cursors.get(block.astNodeIndex) || 0;\n\n if (\n block.element === 'link' ||\n block.element === 'image' ||\n block.element === 'icon'\n ) {\n const target = block.element === 'icon' ? 'image' : block.element;\n for (let i = cursor; i < children.length; i++) {\n const found =\n children[i].type === target\n ? children[i]\n : findNode(children[i], target);\n if (found) {\n cursors.set(block.astNodeIndex, i + 1);\n return { content: extractContent(found), node: found };\n }\n }\n return { content: '', node: n };\n }\n\n if (block.element === 't') {\n let text = '';\n let md = '';\n let i = cursor;\n for (; i < children.length; i++) {\n const child = children[i];\n if (child.type === 'link' || child.type === 'image') {\n break;\n }\n // Also stop at formatting wrappers that contain links/images\n if (findNode(child, 'link') || findNode(child, 'image')) {\n break;\n }\n text += extractContent(child);\n md += extractContentMarkdown(child);\n }\n cursors.set(block.astNodeIndex, i);\n // Preserve markdown when it differs from plain text (has inline formatting)\n return { content: text, markdown: md !== text ? md : undefined, node: n };\n }\n\n return { content: extractContent(astNode), node: n };\n}\n\n// ---------------------------------------------------------------------------\n// Meta extraction — uses the resolved source node, not the whole parent\n// ---------------------------------------------------------------------------\n\n/**\n * Decompose list items into ListItemParts, each carrying nested Part[] in meta.\n * Reuses nodesToParts to resolve each item's inline content (links, images, text, etc.).\n */\nfunction buildListItems(listNode: any): Part[] {\n return (listNode.children || []).map((item: any) => {\n const itemParts = nodesToParts(item.children || []);\n\n // A \"complex\" item has more than plain text (e.g. links, images, mixed\n // inline elements). In that case content is meaningless — the real data\n // lives inside the nested parts.\n const isComplex =\n itemParts.length > 1 ||\n (itemParts.length === 1 && itemParts[0].type !== 'text');\n\n const part: Part = {\n type: 'listItem',\n role: 'list-item',\n content: isComplex ? '' : extractContent(item).trim(),\n };\n if (itemParts.length > 0) {\n const markdown = extractContentMarkdown(item).trim();\n part.meta = {\n parts: itemParts,\n // Always store markdown so consumers can access the original formatting\n // (e.g. **bold** titles). For simple items, content already has plain\n // text; markdown preserves inline formatting.\n ...(markdown ? { markdown } : {}),\n };\n }\n return part;\n });\n}\n\nfunction buildMeta(\n type: PartType,\n sourceNode: any,\n block: EnrichedBlockElement,\n): Record<string, unknown> | undefined {\n if (!sourceNode) {\n return undefined;\n }\n switch (type) {\n case 'heading':\n return { level: sourceNode.depth };\n case 'link': {\n const link =\n sourceNode.type === 'link' ? sourceNode : findNode(sourceNode, 'link');\n const lm = block.linkMetadata;\n return {\n href: lm?.url || link?.url || '',\n ...(link?.title ? { title: link.title } : {}),\n ...(lm?.entityType\n ? { isEntity: true, entityType: lm.entityType }\n : {}),\n };\n }\n case 'image': {\n const img =\n sourceNode.type === 'image'\n ? sourceNode\n : findNode(sourceNode, 'image');\n return img\n ? { src: img.url || '', ...(img.alt ? { alt: img.alt } : {}) }\n : undefined;\n }\n case 'icon': {\n const iconImg =\n sourceNode.type === 'image'\n ? sourceNode\n : findNode(sourceNode, 'image');\n const iconUrl: string = iconImg?.url || '';\n const web5Match = iconUrl.match(/^web5:\\/\\/icon\\/(.+)/);\n const iconHint = web5Match?.[1] || '';\n return iconHint ? { iconHint } : undefined;\n }\n case 'list': {\n const items = buildListItems(sourceNode);\n return { ordered: !!sourceNode.ordered, items };\n }\n case 'table': {\n const rows: any[] = sourceNode.children || [];\n if (rows.length === 0) {\n return undefined;\n }\n const headerRow = rows[0];\n const headerCells: ReturnType<typeof tableCellMeta>[] = (\n headerRow?.children || []\n ).map((cell: any) => tableCellMeta(cell));\n const columnLabels = headerCells.map((cell) => cell.label);\n const featureColumnLabel = columnLabels[0] || 'Feature';\n const dataRows = rows.slice(1).map((row: any) => {\n const cells = row.children || [];\n const firstCell = tableCellMeta(cells[0]);\n const values = cells\n .slice(1)\n .map((cell: any) => extractContent(cell).trim());\n return {\n feature: firstCell.label || '',\n ...(firstCell.iconHint ? { iconHint: firstCell.iconHint } : {}),\n values,\n };\n });\n return {\n columnLabels: columnLabels.slice(1),\n columns: headerCells.slice(1),\n featureColumnLabel,\n rows: dataRows,\n };\n }\n case 'callout': {\n const raw = extractContent(sourceNode).trim();\n const { kind } = parseCalloutTag(raw);\n return kind ? { kind } : undefined;\n }\n case 'htmlComment':\n return block.htmlCommentMetadata\n ? { ...block.htmlCommentMetadata }\n : undefined;\n default:\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Convert markdown AST nodes into a flat array of `Part` objects.\n *\n * Each enriched block element becomes one `Part`. When a single AST node\n * (e.g. a paragraph) produces multiple blocks (link + image + text), each\n * block becomes a separate Part with content resolved from its specific\n * child within the paragraph.\n *\n * @param nodes - Markdown AST nodes (typically a slice of `root.children`)\n * @returns Ordered array of Part objects\n */\nexport function nodesToParts(nodes: RootContent[]): Part[] {\n if (!nodes || nodes.length === 0) {\n return [];\n }\n\n const enrichedBlocks = convertToBlockElements(nodes);\n const parts: Part[] = [];\n const typeCounts: Record<string, number> = {};\n const childCursors = new Map<number, number>();\n // A paragraph carrying an inline link is unwrapped into several block\n // elements (`t`, `link`, `t`), so its parts are siblings with nothing to say\n // they came from one paragraph. Mark every part after the first of a given\n // AST node, so a consumer rebuilding prose can tell \"same paragraph, keep\n // going\" from \"next paragraph, break\". Only split nodes carry the flag —\n // plain paragraphs, headings and images stay meta-free.\n const seenNodes = new Set<number>();\n\n for (const block of enrichedBlocks) {\n const astNode = nodes[block.astNodeIndex];\n const type = blockElementToPartType(block.element);\n const count = (typeCounts[type] || 0) + 1;\n typeCounts[type] = count;\n\n const source = resolveSource(block, astNode, childCursors);\n\n const part: Part = {\n type,\n role: deriveRole(type, count),\n content: source.content.trim(),\n };\n const meta = buildMeta(type, source.node, block);\n if (meta) {\n part.meta = meta;\n }\n\n // Preserve original markdown when inline formatting exists (bold, italic, etc.)\n if (source.markdown) {\n part.meta = { ...part.meta, markdown: source.markdown.trim() };\n }\n\n if (seenNodes.has(block.astNodeIndex)) {\n part.meta = { ...part.meta, continuesNode: true };\n }\n seenNodes.add(block.astNodeIndex);\n\n // For lists whose items are all complex, the list-level content is\n // meaningless (just a concatenation of child text). Clear it so that\n // consumers rely on the structured items in meta instead.\n if (type === 'list' && meta) {\n const items = (meta.items as Part[]) || [];\n const allComplex =\n items.length > 0 && items.every((it) => it.content === '');\n if (allComplex) {\n part.content = '';\n }\n }\n\n parts.push(part);\n }\n\n return parts;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -87,9 +87,16 @@ function transformShopifyProduct(product, variantId) {
|
|
|
87
87
|
var _product$featuredImag, _product$options;
|
|
88
88
|
const variants = extractVariants(product);
|
|
89
89
|
// The turn decided WHICH variant to show; the live fetch is here for what is
|
|
90
|
-
// true about it right now.
|
|
91
|
-
// since dropped — the product-level range is still the only answer we have.
|
|
90
|
+
// true about it right now.
|
|
92
91
|
const matchedVariant = findLiveVariant(variants, variantId);
|
|
92
|
+
// Whether this fetch is answering about the whole product or about one
|
|
93
|
+
// child of it. A row that named a child but whose child the store no longer
|
|
94
|
+
// returns — dropped since the turn, or past the `variants(first: 100)` page
|
|
95
|
+
// — is NOT a whole-product row: answering it with the product's floor would
|
|
96
|
+
// put a different variant's price under this variant's name, which is the
|
|
97
|
+
// bug the row's own `variantId` exists to prevent. So the live price is
|
|
98
|
+
// simply withheld and the turn's, which was right about this child, stands.
|
|
99
|
+
const pricesWholeProduct = !variantId;
|
|
93
100
|
return {
|
|
94
101
|
url: product.onlineStoreUrl || '',
|
|
95
102
|
title: product.title,
|
|
@@ -98,11 +105,16 @@ function transformShopifyProduct(product, variantId) {
|
|
|
98
105
|
imageUrl: (_product$featuredImag = product.featuredImage) == null ? void 0 : _product$featuredImag.url,
|
|
99
106
|
secondaryImageUrl: getSecondaryImage(product),
|
|
100
107
|
vendor: product.vendor,
|
|
101
|
-
price: (matchedVariant == null ? void 0 : matchedVariant.price)
|
|
102
|
-
//
|
|
108
|
+
price: (matchedVariant == null ? void 0 : matchedVariant.price) ?? (pricesWholeProduct ? formatPrice(product.priceRange.minVariantPrice) : undefined),
|
|
109
|
+
// The span the whole product covers, in the store's live currency. A card
|
|
110
|
+
// reads it to decide whether "from" is honest; it is the product's either
|
|
111
|
+
// way, so it is reported whether or not a child matched.
|
|
112
|
+
priceMin: formatPrice(product.priceRange.minVariantPrice),
|
|
113
|
+
priceMax: formatPrice(product.priceRange.maxVariantPrice),
|
|
114
|
+
// Keyed on the variant, not on its value: a whole-product row falls back
|
|
103
115
|
// to the product-level compare-at, but a matched variant that is simply
|
|
104
116
|
// not on sale must not inherit one and print a false strikethrough.
|
|
105
|
-
compareAtPrice: matchedVariant ? matchedVariant.compareAtPrice : getCompareAtPrice(product),
|
|
117
|
+
compareAtPrice: matchedVariant ? matchedVariant.compareAtPrice : pricesWholeProduct ? getCompareAtPrice(product) : undefined,
|
|
106
118
|
available: matchedVariant == null ? void 0 : matchedVariant.available,
|
|
107
119
|
sizes: extractSizes(product),
|
|
108
120
|
variants,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["formatShopifyDate","isoDate","date","Date","isNaN","getTime","undefined","day","getDate","month","toLocaleString","year","getFullYear","formatPrice","money","amount","parseFloat","formatted","toFixed","currencyCode","extractSizes","product","sizes","variants","edges","flatMap","e","node","selectedOptions","filter","o","name","toLowerCase","map","value","unique","Set","length","extractVariants","sizeOption","find","numericId","id","replace","label","title","variantId","available","availableForSale","price","compareAtPrice","getSecondaryImage","images","url","getAllImages","getCompareAtPrice","_product$compareAtPri","compare","compareAtPriceRange","minVariantPrice","min","priceRange","findLiveVariant","variant","transformShopifyProduct","_product$featuredImag","_product$options","matchedVariant","onlineStoreUrl","type","description","imageUrl","featuredImage","secondaryImageUrl","vendor","allImages","options","tags","transformShopifyCollection","collection","_collection$image","image","transformShopifyArticle","article","_article$image","_article$authorV","excerpt","publishDate","publishedAt","author","authorV2","transformShopifyEntityToItemData","entity","entityType"],"sources":["../../../../src/services/shopify/transformShopifyEntity.ts"],"sourcesContent":["import type { EntityItemData } from '../../component/types';\nimport type {\n ShopifyProduct,\n ShopifyCollection,\n ShopifyArticle,\n} from './types';\n\nfunction formatShopifyDate(isoDate: string): string | undefined {\n const date = new Date(isoDate);\n if (isNaN(date.getTime())) {\n return undefined;\n }\n const day = date.getDate();\n const month = date.toLocaleString('en-US', { month: 'short' });\n const year = date.getFullYear();\n return `${day} ${month} ${year}`;\n}\n\nfunction formatPrice(\n money: ShopifyProduct['priceRange']['minVariantPrice'],\n): string {\n const amount = parseFloat(money.amount);\n const formatted = amount.toFixed(2);\n return `${formatted} ${money.currencyCode}`;\n}\n\nfunction extractSizes(product: ShopifyProduct): string[] | undefined {\n const sizes = product.variants.edges.flatMap((e) =>\n e.node.selectedOptions\n .filter((o) => o.name.toLowerCase() === 'size')\n .map((o) => o.value),\n );\n const unique = [...new Set(sizes)];\n return unique.length > 0 ? unique : undefined;\n}\n\n/**\n * Extract variant ID → size label mapping for add-to-cart.\n * Shopify global IDs look like \"gid://shopify/ProductVariant/12345\" —\n * we extract just the numeric part for the Ajax API.\n */\nfunction extractVariants(product: ShopifyProduct): EntityItemData['variants'] {\n const variants = product.variants.edges.map((e) => {\n const sizeOption = e.node.selectedOptions.find(\n (o) => o.name.toLowerCase() === 'size',\n );\n const numericId = e.node.id.replace(\n /^gid:\\/\\/shopify\\/ProductVariant\\//,\n '',\n );\n return {\n label: sizeOption?.value || e.node.title,\n variantId: numericId,\n available: e.node.availableForSale,\n selectedOptions: e.node.selectedOptions.map((o) => ({\n name: o.name,\n value: o.value,\n })),\n price: formatPrice(e.node.price),\n compareAtPrice: e.node.compareAtPrice\n ? formatPrice(e.node.compareAtPrice)\n : undefined,\n };\n });\n return variants.length > 0 ? variants : undefined;\n}\n\nfunction getSecondaryImage(product: ShopifyProduct): string | undefined {\n const images = product.images.edges.map((e) => e.node.url);\n return images.length > 1 ? images[1] : undefined;\n}\n\nfunction getAllImages(product: ShopifyProduct): string[] | undefined {\n const images = product.images.edges.map((e) => e.node.url);\n return images.length > 0 ? images : undefined;\n}\n\nfunction getCompareAtPrice(product: ShopifyProduct): string | undefined {\n const compare = product.compareAtPriceRange?.minVariantPrice;\n if (!compare) {\n return undefined;\n }\n const min = product.priceRange.minVariantPrice;\n if (parseFloat(compare.amount) <= parseFloat(min.amount)) {\n return undefined;\n }\n return formatPrice(compare);\n}\n\n/**\n * The live entry for the variant the turn resolved, if the store still has it.\n * Both sides carry the bare numeric id — `extractVariants` strips the\n * `gid://shopify/ProductVariant/` prefix, and the turn never adds one.\n */\nfunction findLiveVariant(\n variants: EntityItemData['variants'],\n variantId: string | undefined,\n): NonNullable<EntityItemData['variants']>[number] | undefined {\n if (!variantId) {\n return undefined;\n }\n return variants?.find((variant) => variant.variantId === variantId);\n}\n\nexport function transformShopifyProduct(\n product: ShopifyProduct,\n variantId?: string,\n): EntityItemData {\n const variants = extractVariants(product);\n // The turn decided WHICH variant to show; the live fetch is here for what is\n // true about it right now. With no matched variant — or one the store has\n // since dropped — the product-level range is still the only answer we have.\n const matchedVariant = findLiveVariant(variants, variantId);\n\n return {\n url: product.onlineStoreUrl || '',\n title: product.title,\n type: 'product',\n description: product.description,\n imageUrl: product.featuredImage?.url,\n secondaryImageUrl: getSecondaryImage(product),\n vendor: product.vendor,\n price:\n matchedVariant?.price || formatPrice(product.priceRange.minVariantPrice),\n // Keyed on the variant, not on its value: an unmatched product falls back\n // to the product-level compare-at, but a matched variant that is simply\n // not on sale must not inherit one and print a false strikethrough.\n compareAtPrice: matchedVariant\n ? matchedVariant.compareAtPrice\n : getCompareAtPrice(product),\n available: matchedVariant?.available,\n sizes: extractSizes(product),\n variants,\n allImages: getAllImages(product),\n options: product.options?.length ? product.options : undefined,\n tags: product.tags.length ? product.tags : undefined,\n };\n}\n\nexport function transformShopifyCollection(\n collection: ShopifyCollection,\n): EntityItemData {\n return {\n url: collection.onlineStoreUrl || '',\n title: collection.title,\n type: 'collection',\n description: collection.description,\n imageUrl: collection.image?.url,\n };\n}\n\nexport function transformShopifyArticle(\n article: ShopifyArticle,\n): EntityItemData {\n return {\n url: article.onlineStoreUrl || '',\n title: article.title,\n type: 'article',\n description: article.excerpt || undefined,\n imageUrl: article.image?.url,\n publishDate: formatShopifyDate(article.publishedAt),\n author: article.authorV2?.name || undefined,\n tags: article.tags.length ? article.tags : undefined,\n };\n}\n\n/**\n * Transform any resolved Shopify entity to the standard EntityItemData shape.\n * Detects the entity type by checking for type-specific fields.\n */\nexport function transformShopifyEntityToItemData(\n entity: ShopifyProduct | ShopifyCollection | ShopifyArticle,\n entityType: string,\n variantId?: string,\n): EntityItemData {\n switch (entityType.toLowerCase()) {\n case 'product':\n return transformShopifyProduct(entity as ShopifyProduct, variantId);\n case 'collection':\n return transformShopifyCollection(entity as ShopifyCollection);\n case 'article':\n return transformShopifyArticle(entity as ShopifyArticle);\n default:\n return {\n url: '',\n title: (entity as { title?: string }).title || '',\n type: entityType,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["formatShopifyDate","isoDate","date","Date","isNaN","getTime","undefined","day","getDate","month","toLocaleString","year","getFullYear","formatPrice","money","amount","parseFloat","formatted","toFixed","currencyCode","extractSizes","product","sizes","variants","edges","flatMap","e","node","selectedOptions","filter","o","name","toLowerCase","map","value","unique","Set","length","extractVariants","sizeOption","find","numericId","id","replace","label","title","variantId","available","availableForSale","price","compareAtPrice","getSecondaryImage","images","url","getAllImages","getCompareAtPrice","_product$compareAtPri","compare","compareAtPriceRange","minVariantPrice","min","priceRange","findLiveVariant","variant","transformShopifyProduct","_product$featuredImag","_product$options","matchedVariant","pricesWholeProduct","onlineStoreUrl","type","description","imageUrl","featuredImage","secondaryImageUrl","vendor","priceMin","priceMax","maxVariantPrice","allImages","options","tags","transformShopifyCollection","collection","_collection$image","image","transformShopifyArticle","article","_article$image","_article$authorV","excerpt","publishDate","publishedAt","author","authorV2","transformShopifyEntityToItemData","entity","entityType"],"sources":["../../../../src/services/shopify/transformShopifyEntity.ts"],"sourcesContent":["import type { EntityItemData } from '../../component/types';\nimport type {\n ShopifyProduct,\n ShopifyCollection,\n ShopifyArticle,\n} from './types';\n\nfunction formatShopifyDate(isoDate: string): string | undefined {\n const date = new Date(isoDate);\n if (isNaN(date.getTime())) {\n return undefined;\n }\n const day = date.getDate();\n const month = date.toLocaleString('en-US', { month: 'short' });\n const year = date.getFullYear();\n return `${day} ${month} ${year}`;\n}\n\nfunction formatPrice(\n money: ShopifyProduct['priceRange']['minVariantPrice'],\n): string {\n const amount = parseFloat(money.amount);\n const formatted = amount.toFixed(2);\n return `${formatted} ${money.currencyCode}`;\n}\n\nfunction extractSizes(product: ShopifyProduct): string[] | undefined {\n const sizes = product.variants.edges.flatMap((e) =>\n e.node.selectedOptions\n .filter((o) => o.name.toLowerCase() === 'size')\n .map((o) => o.value),\n );\n const unique = [...new Set(sizes)];\n return unique.length > 0 ? unique : undefined;\n}\n\n/**\n * Extract variant ID → size label mapping for add-to-cart.\n * Shopify global IDs look like \"gid://shopify/ProductVariant/12345\" —\n * we extract just the numeric part for the Ajax API.\n */\nfunction extractVariants(product: ShopifyProduct): EntityItemData['variants'] {\n const variants = product.variants.edges.map((e) => {\n const sizeOption = e.node.selectedOptions.find(\n (o) => o.name.toLowerCase() === 'size',\n );\n const numericId = e.node.id.replace(\n /^gid:\\/\\/shopify\\/ProductVariant\\//,\n '',\n );\n return {\n label: sizeOption?.value || e.node.title,\n variantId: numericId,\n available: e.node.availableForSale,\n selectedOptions: e.node.selectedOptions.map((o) => ({\n name: o.name,\n value: o.value,\n })),\n price: formatPrice(e.node.price),\n compareAtPrice: e.node.compareAtPrice\n ? formatPrice(e.node.compareAtPrice)\n : undefined,\n };\n });\n return variants.length > 0 ? variants : undefined;\n}\n\nfunction getSecondaryImage(product: ShopifyProduct): string | undefined {\n const images = product.images.edges.map((e) => e.node.url);\n return images.length > 1 ? images[1] : undefined;\n}\n\nfunction getAllImages(product: ShopifyProduct): string[] | undefined {\n const images = product.images.edges.map((e) => e.node.url);\n return images.length > 0 ? images : undefined;\n}\n\nfunction getCompareAtPrice(product: ShopifyProduct): string | undefined {\n const compare = product.compareAtPriceRange?.minVariantPrice;\n if (!compare) {\n return undefined;\n }\n const min = product.priceRange.minVariantPrice;\n if (parseFloat(compare.amount) <= parseFloat(min.amount)) {\n return undefined;\n }\n return formatPrice(compare);\n}\n\n/**\n * The live entry for the variant the turn resolved, if the store still has it.\n * Both sides carry the bare numeric id — `extractVariants` strips the\n * `gid://shopify/ProductVariant/` prefix, and the turn never adds one.\n */\nfunction findLiveVariant(\n variants: EntityItemData['variants'],\n variantId: string | undefined,\n): NonNullable<EntityItemData['variants']>[number] | undefined {\n if (!variantId) {\n return undefined;\n }\n return variants?.find((variant) => variant.variantId === variantId);\n}\n\nexport function transformShopifyProduct(\n product: ShopifyProduct,\n variantId?: string,\n): EntityItemData {\n const variants = extractVariants(product);\n // The turn decided WHICH variant to show; the live fetch is here for what is\n // true about it right now.\n const matchedVariant = findLiveVariant(variants, variantId);\n // Whether this fetch is answering about the whole product or about one\n // child of it. A row that named a child but whose child the store no longer\n // returns — dropped since the turn, or past the `variants(first: 100)` page\n // — is NOT a whole-product row: answering it with the product's floor would\n // put a different variant's price under this variant's name, which is the\n // bug the row's own `variantId` exists to prevent. So the live price is\n // simply withheld and the turn's, which was right about this child, stands.\n const pricesWholeProduct = !variantId;\n\n return {\n url: product.onlineStoreUrl || '',\n title: product.title,\n type: 'product',\n description: product.description,\n imageUrl: product.featuredImage?.url,\n secondaryImageUrl: getSecondaryImage(product),\n vendor: product.vendor,\n price:\n matchedVariant?.price ??\n (pricesWholeProduct\n ? formatPrice(product.priceRange.minVariantPrice)\n : undefined),\n // The span the whole product covers, in the store's live currency. A card\n // reads it to decide whether \"from\" is honest; it is the product's either\n // way, so it is reported whether or not a child matched.\n priceMin: formatPrice(product.priceRange.minVariantPrice),\n priceMax: formatPrice(product.priceRange.maxVariantPrice),\n // Keyed on the variant, not on its value: a whole-product row falls back\n // to the product-level compare-at, but a matched variant that is simply\n // not on sale must not inherit one and print a false strikethrough.\n compareAtPrice: matchedVariant\n ? matchedVariant.compareAtPrice\n : pricesWholeProduct\n ? getCompareAtPrice(product)\n : undefined,\n available: matchedVariant?.available,\n sizes: extractSizes(product),\n variants,\n allImages: getAllImages(product),\n options: product.options?.length ? product.options : undefined,\n tags: product.tags.length ? product.tags : undefined,\n };\n}\n\nexport function transformShopifyCollection(\n collection: ShopifyCollection,\n): EntityItemData {\n return {\n url: collection.onlineStoreUrl || '',\n title: collection.title,\n type: 'collection',\n description: collection.description,\n imageUrl: collection.image?.url,\n };\n}\n\nexport function transformShopifyArticle(\n article: ShopifyArticle,\n): EntityItemData {\n return {\n url: article.onlineStoreUrl || '',\n title: article.title,\n type: 'article',\n description: article.excerpt || undefined,\n imageUrl: article.image?.url,\n publishDate: formatShopifyDate(article.publishedAt),\n author: article.authorV2?.name || undefined,\n tags: article.tags.length ? article.tags : undefined,\n };\n}\n\n/**\n * Transform any resolved Shopify entity to the standard EntityItemData shape.\n * Detects the entity type by checking for type-specific fields.\n */\nexport function transformShopifyEntityToItemData(\n entity: ShopifyProduct | ShopifyCollection | ShopifyArticle,\n entityType: string,\n variantId?: string,\n): EntityItemData {\n switch (entityType.toLowerCase()) {\n case 'product':\n return transformShopifyProduct(entity as ShopifyProduct, variantId);\n case 'collection':\n return transformShopifyCollection(entity as ShopifyCollection);\n case 'article':\n return transformShopifyArticle(entity as ShopifyArticle);\n default:\n return {\n url: '',\n title: (entity as { title?: string }).title || '',\n type: entityType,\n };\n }\n}\n"],"mappings":";;;;;;;AAOA,SAASA,iBAAiBA,CAACC,OAAe,EAAsB;EAC9D,MAAMC,IAAI,GAAG,IAAIC,IAAI,CAACF,OAAO,CAAC;EAC9B,IAAIG,KAAK,CAACF,IAAI,CAACG,OAAO,CAAC,CAAC,CAAC,EAAE;IACzB,OAAOC,SAAS;EAClB;EACA,MAAMC,GAAG,GAAGL,IAAI,CAACM,OAAO,CAAC,CAAC;EAC1B,MAAMC,KAAK,GAAGP,IAAI,CAACQ,cAAc,CAAC,OAAO,EAAE;IAAED,KAAK,EAAE;EAAQ,CAAC,CAAC;EAC9D,MAAME,IAAI,GAAGT,IAAI,CAACU,WAAW,CAAC,CAAC;EAC/B,OAAO,GAAGL,GAAG,IAAIE,KAAK,IAAIE,IAAI,EAAE;AAClC;AAEA,SAASE,WAAWA,CAClBC,KAAsD,EAC9C;EACR,MAAMC,MAAM,GAAGC,UAAU,CAACF,KAAK,CAACC,MAAM,CAAC;EACvC,MAAME,SAAS,GAAGF,MAAM,CAACG,OAAO,CAAC,CAAC,CAAC;EACnC,OAAO,GAAGD,SAAS,IAAIH,KAAK,CAACK,YAAY,EAAE;AAC7C;AAEA,SAASC,YAAYA,CAACC,OAAuB,EAAwB;EACnE,MAAMC,KAAK,GAAGD,OAAO,CAACE,QAAQ,CAACC,KAAK,CAACC,OAAO,CAAEC,CAAC,IAC7CA,CAAC,CAACC,IAAI,CAACC,eAAe,CACnBC,MAAM,CAAEC,CAAC,IAAKA,CAAC,CAACC,IAAI,CAACC,WAAW,CAAC,CAAC,KAAK,MAAM,CAAC,CAC9CC,GAAG,CAAEH,CAAC,IAAKA,CAAC,CAACI,KAAK,CACvB,CAAC;EACD,MAAMC,MAAM,GAAG,CAAC,GAAG,IAAIC,GAAG,CAACd,KAAK,CAAC,CAAC;EAClC,OAAOa,MAAM,CAACE,MAAM,GAAG,CAAC,GAAGF,MAAM,GAAG7B,SAAS;AAC/C;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASgC,eAAeA,CAACjB,OAAuB,EAA8B;EAC5E,MAAME,QAAQ,GAAGF,OAAO,CAACE,QAAQ,CAACC,KAAK,CAACS,GAAG,CAAEP,CAAC,IAAK;IACjD,MAAMa,UAAU,GAAGb,CAAC,CAACC,IAAI,CAACC,eAAe,CAACY,IAAI,CAC3CV,CAAC,IAAKA,CAAC,CAACC,IAAI,CAACC,WAAW,CAAC,CAAC,KAAK,MAClC,CAAC;IACD,MAAMS,SAAS,GAAGf,CAAC,CAACC,IAAI,CAACe,EAAE,CAACC,OAAO,CACjC,oCAAoC,EACpC,EACF,CAAC;IACD,OAAO;MACLC,KAAK,EAAE,CAAAL,UAAU,oBAAVA,UAAU,CAAEL,KAAK,KAAIR,CAAC,CAACC,IAAI,CAACkB,KAAK;MACxCC,SAAS,EAAEL,SAAS;MACpBM,SAAS,EAAErB,CAAC,CAACC,IAAI,CAACqB,gBAAgB;MAClCpB,eAAe,EAAEF,CAAC,CAACC,IAAI,CAACC,eAAe,CAACK,GAAG,CAAEH,CAAC,KAAM;QAClDC,IAAI,EAAED,CAAC,CAACC,IAAI;QACZG,KAAK,EAAEJ,CAAC,CAACI;MACX,CAAC,CAAC,CAAC;MACHe,KAAK,EAAEpC,WAAW,CAACa,CAAC,CAACC,IAAI,CAACsB,KAAK,CAAC;MAChCC,cAAc,EAAExB,CAAC,CAACC,IAAI,CAACuB,cAAc,GACjCrC,WAAW,CAACa,CAAC,CAACC,IAAI,CAACuB,cAAc,CAAC,GAClC5C;IACN,CAAC;EACH,CAAC,CAAC;EACF,OAAOiB,QAAQ,CAACc,MAAM,GAAG,CAAC,GAAGd,QAAQ,GAAGjB,SAAS;AACnD;AAEA,SAAS6C,iBAAiBA,CAAC9B,OAAuB,EAAsB;EACtE,MAAM+B,MAAM,GAAG/B,OAAO,CAAC+B,MAAM,CAAC5B,KAAK,CAACS,GAAG,CAAEP,CAAC,IAAKA,CAAC,CAACC,IAAI,CAAC0B,GAAG,CAAC;EAC1D,OAAOD,MAAM,CAACf,MAAM,GAAG,CAAC,GAAGe,MAAM,CAAC,CAAC,CAAC,GAAG9C,SAAS;AAClD;AAEA,SAASgD,YAAYA,CAACjC,OAAuB,EAAwB;EACnE,MAAM+B,MAAM,GAAG/B,OAAO,CAAC+B,MAAM,CAAC5B,KAAK,CAACS,GAAG,CAAEP,CAAC,IAAKA,CAAC,CAACC,IAAI,CAAC0B,GAAG,CAAC;EAC1D,OAAOD,MAAM,CAACf,MAAM,GAAG,CAAC,GAAGe,MAAM,GAAG9C,SAAS;AAC/C;AAEA,SAASiD,iBAAiBA,CAAClC,OAAuB,EAAsB;EAAA,IAAAmC,qBAAA;EACtE,MAAMC,OAAO,IAAAD,qBAAA,GAAGnC,OAAO,CAACqC,mBAAmB,qBAA3BF,qBAAA,CAA6BG,eAAe;EAC5D,IAAI,CAACF,OAAO,EAAE;IACZ,OAAOnD,SAAS;EAClB;EACA,MAAMsD,GAAG,GAAGvC,OAAO,CAACwC,UAAU,CAACF,eAAe;EAC9C,IAAI3C,UAAU,CAACyC,OAAO,CAAC1C,MAAM,CAAC,IAAIC,UAAU,CAAC4C,GAAG,CAAC7C,MAAM,CAAC,EAAE;IACxD,OAAOT,SAAS;EAClB;EACA,OAAOO,WAAW,CAAC4C,OAAO,CAAC;AAC7B;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASK,eAAeA,CACtBvC,QAAoC,EACpCuB,SAA6B,EACgC;EAC7D,IAAI,CAACA,SAAS,EAAE;IACd,OAAOxC,SAAS;EAClB;EACA,OAAOiB,QAAQ,oBAARA,QAAQ,CAAEiB,IAAI,CAAEuB,OAAO,IAAKA,OAAO,CAACjB,SAAS,KAAKA,SAAS,CAAC;AACrE;AAEO,SAASkB,uBAAuBA,CACrC3C,OAAuB,EACvByB,SAAkB,EACF;EAAA,IAAAmB,qBAAA,EAAAC,gBAAA;EAChB,MAAM3C,QAAQ,GAAGe,eAAe,CAACjB,OAAO,CAAC;EACzC;EACA;EACA,MAAM8C,cAAc,GAAGL,eAAe,CAACvC,QAAQ,EAAEuB,SAAS,CAAC;EAC3D;EACA;EACA;EACA;EACA;EACA;EACA;EACA,MAAMsB,kBAAkB,GAAG,CAACtB,SAAS;EAErC,OAAO;IACLO,GAAG,EAAEhC,OAAO,CAACgD,cAAc,IAAI,EAAE;IACjCxB,KAAK,EAAExB,OAAO,CAACwB,KAAK;IACpByB,IAAI,EAAE,SAAS;IACfC,WAAW,EAAElD,OAAO,CAACkD,WAAW;IAChCC,QAAQ,GAAAP,qBAAA,GAAE5C,OAAO,CAACoD,aAAa,qBAArBR,qBAAA,CAAuBZ,GAAG;IACpCqB,iBAAiB,EAAEvB,iBAAiB,CAAC9B,OAAO,CAAC;IAC7CsD,MAAM,EAAEtD,OAAO,CAACsD,MAAM;IACtB1B,KAAK,EACH,CAAAkB,cAAc,oBAAdA,cAAc,CAAElB,KAAK,MACpBmB,kBAAkB,GACfvD,WAAW,CAACQ,OAAO,CAACwC,UAAU,CAACF,eAAe,CAAC,GAC/CrD,SAAS,CAAC;IAChB;IACA;IACA;IACAsE,QAAQ,EAAE/D,WAAW,CAACQ,OAAO,CAACwC,UAAU,CAACF,eAAe,CAAC;IACzDkB,QAAQ,EAAEhE,WAAW,CAACQ,OAAO,CAACwC,UAAU,CAACiB,eAAe,CAAC;IACzD;IACA;IACA;IACA5B,cAAc,EAAEiB,cAAc,GAC1BA,cAAc,CAACjB,cAAc,GAC7BkB,kBAAkB,GAClBb,iBAAiB,CAAClC,OAAO,CAAC,GAC1Bf,SAAS;IACbyC,SAAS,EAAEoB,cAAc,oBAAdA,cAAc,CAAEpB,SAAS;IACpCzB,KAAK,EAAEF,YAAY,CAACC,OAAO,CAAC;IAC5BE,QAAQ;IACRwD,SAAS,EAAEzB,YAAY,CAACjC,OAAO,CAAC;IAChC2D,OAAO,EAAE,CAAAd,gBAAA,GAAA7C,OAAO,CAAC2D,OAAO,aAAfd,gBAAA,CAAiB7B,MAAM,GAAGhB,OAAO,CAAC2D,OAAO,GAAG1E,SAAS;IAC9D2E,IAAI,EAAE5D,OAAO,CAAC4D,IAAI,CAAC5C,MAAM,GAAGhB,OAAO,CAAC4D,IAAI,GAAG3E;EAC7C,CAAC;AACH;AAEO,SAAS4E,0BAA0BA,CACxCC,UAA6B,EACb;EAAA,IAAAC,iBAAA;EAChB,OAAO;IACL/B,GAAG,EAAE8B,UAAU,CAACd,cAAc,IAAI,EAAE;IACpCxB,KAAK,EAAEsC,UAAU,CAACtC,KAAK;IACvByB,IAAI,EAAE,YAAY;IAClBC,WAAW,EAAEY,UAAU,CAACZ,WAAW;IACnCC,QAAQ,GAAAY,iBAAA,GAAED,UAAU,CAACE,KAAK,qBAAhBD,iBAAA,CAAkB/B;EAC9B,CAAC;AACH;AAEO,SAASiC,uBAAuBA,CACrCC,OAAuB,EACP;EAAA,IAAAC,cAAA,EAAAC,gBAAA;EAChB,OAAO;IACLpC,GAAG,EAAEkC,OAAO,CAAClB,cAAc,IAAI,EAAE;IACjCxB,KAAK,EAAE0C,OAAO,CAAC1C,KAAK;IACpByB,IAAI,EAAE,SAAS;IACfC,WAAW,EAAEgB,OAAO,CAACG,OAAO,IAAIpF,SAAS;IACzCkE,QAAQ,GAAAgB,cAAA,GAAED,OAAO,CAACF,KAAK,qBAAbG,cAAA,CAAenC,GAAG;IAC5BsC,WAAW,EAAE3F,iBAAiB,CAACuF,OAAO,CAACK,WAAW,CAAC;IACnDC,MAAM,EAAE,EAAAJ,gBAAA,GAAAF,OAAO,CAACO,QAAQ,qBAAhBL,gBAAA,CAAkB1D,IAAI,KAAIzB,SAAS;IAC3C2E,IAAI,EAAEM,OAAO,CAACN,IAAI,CAAC5C,MAAM,GAAGkD,OAAO,CAACN,IAAI,GAAG3E;EAC7C,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACO,SAASyF,gCAAgCA,CAC9CC,MAA2D,EAC3DC,UAAkB,EAClBnD,SAAkB,EACF;EAChB,QAAQmD,UAAU,CAACjE,WAAW,CAAC,CAAC;IAC9B,KAAK,SAAS;MACZ,OAAOgC,uBAAuB,CAACgC,MAAM,EAAoBlD,SAAS,CAAC;IACrE,KAAK,YAAY;MACf,OAAOoC,0BAA0B,CAACc,MAA2B,CAAC;IAChE,KAAK,SAAS;MACZ,OAAOV,uBAAuB,CAACU,MAAwB,CAAC;IAC1D;MACE,OAAO;QACL3C,GAAG,EAAE,EAAE;QACPR,KAAK,EAAGmD,MAAM,CAAwBnD,KAAK,IAAI,EAAE;QACjDyB,IAAI,EAAE2B;MACR,CAAC;EACL;AACF","ignoreList":[]}
|
|
@@ -1,60 +1,67 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
import { headingMarkdown,
|
|
2
|
+
import { headingMarkdown, bodyMarkdown } from './parse-utils.js';
|
|
3
3
|
import { DIAGNOSTIC_TYPES } from '../diagnosticTypes.js';
|
|
4
4
|
export class TextBlockSectionDefinition {
|
|
5
5
|
constructor() {
|
|
6
6
|
_defineProperty(this, "sectionType", 'textBlock');
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
7
|
+
// A repeated body, not a single `t` — a heading owns ALL the running copy
|
|
8
|
+
// beneath it. With one `t` the pattern took the heading and the FIRST
|
|
9
|
+
// paragraph only;
|
|
10
|
+
// because a pattern matches a PREFIX of the block, every later paragraph fell
|
|
11
|
+
// through to `FallbackSectionDefinition` and rendered as undifferentiated
|
|
12
|
+
// prose with no section treatment. Multi-paragraph explainer copy is the
|
|
13
|
+
// shape stores emit constantly, so most of the block was landing unstyled.
|
|
13
14
|
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
15
|
+
// No callout in the grammar. A trailing blockquote is
|
|
16
|
+
// `CalloutSectionDefinition`'s (`html_comment? > callout+`) — it sits above
|
|
17
|
+
// `textBlock` in `createSdkRegistry` and in every client registry, so once
|
|
18
|
+
// this pattern stops before the quote the parser's next iteration claims it
|
|
19
|
+
// as its own section. Keeping it here only duplicated that ownership.
|
|
20
|
+
//
|
|
21
|
+
// `link` is in the alternation because `convertToBlockElements` unwraps a
|
|
22
|
+
// paragraph into its inline nodes: `Try narrowing to [men](web5://ask/…)
|
|
23
|
+
// instead` emits `t link t`. With `t+` alone the run ended at that `link`,
|
|
24
|
+
// truncating the section at the first paragraph carrying an ask link — the
|
|
25
|
+
// exact shape DL #134 made the prompts emit. `parse()` uses `bodyMarkdown`,
|
|
26
|
+
// which keeps the link inline rather than deleting it the way `bodyText`
|
|
27
|
+
// does. Entity blocks are unaffected: their links are `link[entityType]` and
|
|
28
|
+
// every registry puts the entity definitions above this one.
|
|
29
|
+
//
|
|
30
|
+
// NOTE for registry authors: this still matches a bare `h2 > t`, so the
|
|
31
|
+
// prefix hazard is unchanged — if anything it claims MORE. Register
|
|
16
32
|
// `textBlock` BELOW every definition whose block opens `h2 > t` — entity,
|
|
17
33
|
// entityCollection, listItemsSection, featureCards, ctaBanner, comparison,
|
|
18
|
-
// kpi, nextSteps. Above them it claims the heading + intro
|
|
34
|
+
// kpi, nextSteps. Above them it claims the heading + intro copy off the
|
|
19
35
|
// front of the block and strands the list / links / table that follow
|
|
20
36
|
// (`EntitySectionDefinition` allows `h2 > (t | callout)* > links…`, and
|
|
21
37
|
// `listItemsSection` / `ctaBanner` are the same shape). `createSdkRegistry`
|
|
22
38
|
// and the client registries in this repo order it that way; a client
|
|
23
39
|
// registry living outside this repo has to do the same.
|
|
24
|
-
_defineProperty(this, "patterns", ['html_comment? > h2 > t
|
|
40
|
+
_defineProperty(this, "patterns", ['html_comment? > h2 > (t | link)+']);
|
|
25
41
|
_defineProperty(this, "defaults", {
|
|
26
42
|
title: 'Text Block Title',
|
|
27
|
-
description: 'Description text.'
|
|
28
|
-
callout: {
|
|
29
|
-
text: 'A key insight or quote.'
|
|
30
|
-
}
|
|
43
|
+
description: 'Description text.'
|
|
31
44
|
});
|
|
32
45
|
_defineProperty(this, "fixtures", [{
|
|
33
46
|
markdown: `## Choosing the Right Liquid Data Option
|
|
34
47
|
|
|
35
48
|
Liquid Data Go is designed for brands that want affordable, scalable access to syndicated POS, shopper, and pricing insights with minimal setup and no data expertise required.
|
|
36
49
|
|
|
37
|
-
|
|
50
|
+
It covers a single category out of the box, so a brand can stand up reporting without a data team and without negotiating a full platform contract first.
|
|
51
|
+
|
|
52
|
+
The broader Liquid Data platform becomes the better fit once you need to integrate many datasets and support multiple enterprise workflows.`,
|
|
38
53
|
expected: {
|
|
39
54
|
title: 'Choosing the Right Liquid Data Option',
|
|
40
|
-
description: 'Liquid Data Go is designed for brands that want affordable, scalable access to syndicated POS, shopper, and pricing insights with minimal setup and no data expertise required.',
|
|
41
|
-
callout: {
|
|
42
|
-
text: 'If you mainly need fast, packaged insights in one category, Liquid Data Go is usually the right fit; if you need to integrate many datasets and support multiple enterprise workflows, the broader Liquid Data platform and its solutions become more relevant.'
|
|
43
|
-
}
|
|
55
|
+
description: ['Liquid Data Go is designed for brands that want affordable, scalable access to syndicated POS, shopper, and pricing insights with minimal setup and no data expertise required.', 'It covers a single category out of the box, so a brand can stand up reporting without a data team and without negotiating a full platform contract first.', 'The broader Liquid Data platform becomes the better fit once you need to integrate many datasets and support multiple enterprise workflows.'].join('\n\n')
|
|
44
56
|
}
|
|
45
57
|
}, {
|
|
46
58
|
markdown: `<!-- section: textBlock -->
|
|
47
59
|
## Key Takeaway
|
|
48
60
|
|
|
49
|
-
The platform scales with your needs
|
|
50
|
-
|
|
51
|
-
> Start small with Go, then expand to the full suite as your data maturity grows.`,
|
|
61
|
+
The platform scales with your needs.`,
|
|
52
62
|
expected: {
|
|
53
63
|
title: 'Key Takeaway',
|
|
54
|
-
description: 'The platform scales with your needs.'
|
|
55
|
-
callout: {
|
|
56
|
-
text: 'Start small with Go, then expand to the full suite as your data maturity grows.'
|
|
57
|
-
}
|
|
64
|
+
description: 'The platform scales with your needs.'
|
|
58
65
|
}
|
|
59
66
|
}, {
|
|
60
67
|
markdown: `## How to choose between a clinical serum and a nourishing dry oil
|
|
@@ -62,23 +69,20 @@ The platform scales with your needs.
|
|
|
62
69
|
Clinically tested serums with patented actives target visible reduction of marks and uneven tone, while dry oils focus on broad nourishment and a satin finish.`,
|
|
63
70
|
expected: {
|
|
64
71
|
title: 'How to choose between a clinical serum and a nourishing dry oil',
|
|
65
|
-
description: 'Clinically tested serums with patented actives target visible reduction of marks and uneven tone, while dry oils focus on broad nourishment and a satin finish.'
|
|
66
|
-
callout: undefined
|
|
72
|
+
description: 'Clinically tested serums with patented actives target visible reduction of marks and uneven tone, while dry oils focus on broad nourishment and a satin finish.'
|
|
67
73
|
}
|
|
68
74
|
}]);
|
|
69
75
|
}
|
|
70
76
|
parse(parts, context) {
|
|
71
77
|
const title = headingMarkdown(parts, 2) || '';
|
|
72
|
-
const description =
|
|
73
|
-
const callout = calloutFromParts(parts);
|
|
78
|
+
const description = bodyMarkdown(parts) || '';
|
|
74
79
|
if (!title && !description) {
|
|
75
80
|
context == null || context.reportDiagnostic == null || context.reportDiagnostic(DIAGNOSTIC_TYPES.SECTION_REJECTED, 'textBlock: both title and description are empty');
|
|
76
81
|
return null;
|
|
77
82
|
}
|
|
78
83
|
return {
|
|
79
84
|
title,
|
|
80
|
-
description
|
|
81
|
-
callout
|
|
85
|
+
description
|
|
82
86
|
};
|
|
83
87
|
}
|
|
84
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["headingMarkdown","
|
|
1
|
+
{"version":3,"names":["headingMarkdown","bodyMarkdown","DIAGNOSTIC_TYPES","TextBlockSectionDefinition","constructor","_defineProperty","title","description","markdown","expected","join","parse","parts","context","reportDiagnostic","SECTION_REJECTED"],"sources":["../../../../src/component/componentDefinitions/TextBlockSectionDefinition.ts"],"sourcesContent":["import type { Part } from '../../parts/parts';\nimport { headingMarkdown, bodyMarkdown } from './parse-utils';\nimport type {\n TextBlockCompProps,\n TextBlockSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition, ParseContext } from '../section-definition';\nimport { DIAGNOSTIC_TYPES } from '../diagnosticTypes';\n\nexport class TextBlockSectionDefinition\n implements SectionDefinition<TextBlockSectionComponent>\n{\n sectionType = 'textBlock' as const;\n // A repeated body, not a single `t` — a heading owns ALL the running copy\n // beneath it. With one `t` the pattern took the heading and the FIRST\n // paragraph only;\n // because a pattern matches a PREFIX of the block, every later paragraph fell\n // through to `FallbackSectionDefinition` and rendered as undifferentiated\n // prose with no section treatment. Multi-paragraph explainer copy is the\n // shape stores emit constantly, so most of the block was landing unstyled.\n //\n // No callout in the grammar. A trailing blockquote is\n // `CalloutSectionDefinition`'s (`html_comment? > callout+`) — it sits above\n // `textBlock` in `createSdkRegistry` and in every client registry, so once\n // this pattern stops before the quote the parser's next iteration claims it\n // as its own section. Keeping it here only duplicated that ownership.\n //\n // `link` is in the alternation because `convertToBlockElements` unwraps a\n // paragraph into its inline nodes: `Try narrowing to [men](web5://ask/…)\n // instead` emits `t link t`. With `t+` alone the run ended at that `link`,\n // truncating the section at the first paragraph carrying an ask link — the\n // exact shape DL #134 made the prompts emit. `parse()` uses `bodyMarkdown`,\n // which keeps the link inline rather than deleting it the way `bodyText`\n // does. Entity blocks are unaffected: their links are `link[entityType]` and\n // every registry puts the entity definitions above this one.\n //\n // NOTE for registry authors: this still matches a bare `h2 > t`, so the\n // prefix hazard is unchanged — if anything it claims MORE. Register\n // `textBlock` BELOW every definition whose block opens `h2 > t` — entity,\n // entityCollection, listItemsSection, featureCards, ctaBanner, comparison,\n // kpi, nextSteps. Above them it claims the heading + intro copy off the\n // front of the block and strands the list / links / table that follow\n // (`EntitySectionDefinition` allows `h2 > (t | callout)* > links…`, and\n // `listItemsSection` / `ctaBanner` are the same shape). `createSdkRegistry`\n // and the client registries in this repo order it that way; a client\n // registry living outside this repo has to do the same.\n patterns = ['html_comment? > h2 > (t | link)+'];\n\n defaults: TextBlockCompProps = {\n title: 'Text Block Title',\n description: 'Description text.',\n };\n\n fixtures: SectionFixture<TextBlockCompProps>[] = [\n {\n markdown: `## Choosing the Right Liquid Data Option\n\nLiquid Data Go is designed for brands that want affordable, scalable access to syndicated POS, shopper, and pricing insights with minimal setup and no data expertise required.\n\nIt covers a single category out of the box, so a brand can stand up reporting without a data team and without negotiating a full platform contract first.\n\nThe broader Liquid Data platform becomes the better fit once you need to integrate many datasets and support multiple enterprise workflows.`,\n expected: {\n title: 'Choosing the Right Liquid Data Option',\n description: [\n 'Liquid Data Go is designed for brands that want affordable, scalable access to syndicated POS, shopper, and pricing insights with minimal setup and no data expertise required.',\n 'It covers a single category out of the box, so a brand can stand up reporting without a data team and without negotiating a full platform contract first.',\n 'The broader Liquid Data platform becomes the better fit once you need to integrate many datasets and support multiple enterprise workflows.',\n ].join('\\n\\n'),\n },\n },\n {\n markdown: `<!-- section: textBlock -->\n## Key Takeaway\n\nThe platform scales with your needs.`,\n expected: {\n title: 'Key Takeaway',\n description: 'The platform scales with your needs.',\n },\n },\n {\n markdown: `## How to choose between a clinical serum and a nourishing dry oil\n\nClinically tested serums with patented actives target visible reduction of marks and uneven tone, while dry oils focus on broad nourishment and a satin finish.`,\n expected: {\n title:\n 'How to choose between a clinical serum and a nourishing dry oil',\n description:\n 'Clinically tested serums with patented actives target visible reduction of marks and uneven tone, while dry oils focus on broad nourishment and a satin finish.',\n },\n },\n ];\n\n parse(parts: Part[], context?: ParseContext): TextBlockCompProps | null {\n const title = headingMarkdown(parts, 2) || '';\n const description = bodyMarkdown(parts) || '';\n\n if (!title && !description) {\n context?.reportDiagnostic?.(\n DIAGNOSTIC_TYPES.SECTION_REJECTED,\n 'textBlock: both title and description are empty',\n );\n return null;\n }\n\n return { title, description };\n }\n}\n"],"mappings":";AACA,SAASA,eAAe,EAAEC,YAAY,QAAQ,eAAe;AAO7D,SAASC,gBAAgB,QAAQ,oBAAoB;AAErD,OAAO,MAAMC,0BAA0B,CAEvC;EAAAC,YAAA;IAAAC,eAAA,sBACgB,WAAW;IACzB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IAAAA,eAAA,mBACW,CAAC,kCAAkC,CAAC;IAAAA,eAAA,mBAEhB;MAC7BC,KAAK,EAAE,kBAAkB;MACzBC,WAAW,EAAE;IACf,CAAC;IAAAF,eAAA,mBAEgD,CAC/C;MACEG,QAAQ,EAAE;AAChB;AACA;AACA;AACA;AACA;AACA,4IAA4I;MACtIC,QAAQ,EAAE;QACRH,KAAK,EAAE,uCAAuC;QAC9CC,WAAW,EAAE,CACX,iLAAiL,EACjL,2JAA2J,EAC3J,6IAA6I,CAC9I,CAACG,IAAI,CAAC,MAAM;MACf;IACF,CAAC,EACD;MACEF,QAAQ,EAAE;AAChB;AACA;AACA,qCAAqC;MAC/BC,QAAQ,EAAE;QACRH,KAAK,EAAE,cAAc;QACrBC,WAAW,EAAE;MACf;IACF,CAAC,EACD;MACEC,QAAQ,EAAE;AAChB;AACA,gKAAgK;MAC1JC,QAAQ,EAAE;QACRH,KAAK,EACH,iEAAiE;QACnEC,WAAW,EACT;MACJ;IACF,CAAC,CACF;EAAA;EAEDI,KAAKA,CAACC,KAAa,EAAEC,OAAsB,EAA6B;IACtE,MAAMP,KAAK,GAAGN,eAAe,CAACY,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE;IAC7C,MAAML,WAAW,GAAGN,YAAY,CAACW,KAAK,CAAC,IAAI,EAAE;IAE7C,IAAI,CAACN,KAAK,IAAI,CAACC,WAAW,EAAE;MAC1BM,OAAO,YAAPA,OAAO,CAAEC,gBAAgB,YAAzBD,OAAO,CAAEC,gBAAgB,CACvBZ,gBAAgB,CAACa,gBAAgB,EACjC,iDACF,CAAC;MACD,OAAO,IAAI;IACb;IAEA,OAAO;MAAET,KAAK;MAAEC;IAAY,CAAC;EAC/B;AACF","ignoreList":[]}
|