From aa887a1efed8751b0a8344818dc2c03b156e60c6 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Wed, 12 Aug 2026 06:39:29 +0200 Subject: [PATCH] feat(components): runtime purchase-link override seam (registerBuyLink) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A private build can now register a per-component buy URL at boot (fetched from its backend), superseding the datasheet front-matter. loadDoc merges the override, so both Buy buttons, the UTM decoration and the click tracking pick it up unchanged — and a component with no authored datasheet gets a Buy button too (empty body, buy set). --- frontend/src/components/componentDocs.ts | 46 +++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/componentDocs.ts b/frontend/src/components/componentDocs.ts index a7620365..00f8f473 100644 --- a/frontend/src/components/componentDocs.ts +++ b/frontend/src/components/componentDocs.ts @@ -56,6 +56,32 @@ export function registerComponentDoc(id: string, raw: string): void { cache.delete(id); } +// Purchase-link overrides, keyed by component id. Set at runtime (overlay +// seam below); they supersede the datasheet's `buy:` front-matter and can +// give a Buy button to a component with no authored datasheet at all. +const buyOverrides = new Map(); + +/** + * Overlay seam: register a purchase-URL override for a component id. A + * private build fetches admin-curated links (e.g. affiliate URLs) from its + * backend at boot and pipes them through here, so links can change without a + * redeploy. The override wins over the datasheet front-matter; `loadDoc` + * merges it, so every doc consumer — and the UTM decoration + click tracking + * built on `doc.buy` — picks it up with no extra wiring. + */ +export function registerBuyLink(id: string, url: string): void { + buyOverrides.set(id, url); + cache.delete(id); +} + +/** Merge a registered buy-link override into a parsed doc (synthesising an + * empty-body doc when the component has no datasheet). */ +function withBuyOverride(id: string, doc: ComponentDoc | null): ComponentDoc | null { + const buy = buyOverrides.get(id); + if (!buy) return doc; + return { ...(doc ?? { body: '' }), buy }; +} + /** * Decorate a datasheet Buy/Product-page URL with UTM attribution so the * vendor sees the referral came from Velxio. `componentId` lands in @@ -111,20 +137,24 @@ export function hasDoc(id: string): boolean { return id in byId; } -/** Load (and cache) the parsed datasheet for a component id, or null if none. */ +/** Load (and cache) the parsed datasheet for a component id, or null if none. + * A registered buy-link override is merged into the result (and can produce a + * doc for an id with no .md file — body empty, `buy` set). */ export async function loadDoc(id: string): Promise { if (cache.has(id)) return cache.get(id) ?? null; const loader = byId[id]; if (!loader) { - cache.set(id, null); - return null; + const doc = withBuyOverride(id, null); + cache.set(id, doc); + return doc; } try { - const parsed = parseDoc(await loader()); - cache.set(id, parsed); - return parsed; + const doc = withBuyOverride(id, parseDoc(await loader())); + cache.set(id, doc); + return doc; } catch { - cache.set(id, null); - return null; + const doc = withBuyOverride(id, null); + cache.set(id, doc); + return doc; } }