import { ExperienceResolver } from '@fanfare-io/fanfare-sdk-core/storefront'; import { ShopifyProductRef } from './config'; export type ExperienceResolutionErrorReason = "network" | "http" | "malformed" | "unavailable" | "override"; export interface ExperienceResolutionError { /** The normalized product lookup key (a handle or product id — never a consumer identifier). */ lookupKey: string; reason: ExperienceResolutionErrorReason; /** HTTP status code when `reason === "http"`. */ status?: number; /** The thrown value when `reason` is `"network"` or `"override"`. */ cause?: unknown; } export type ShopifyExperienceResolution = { outcome: "gated"; experienceId: string; } | { outcome: "ungated"; } | { outcome: "error"; error: ExperienceResolutionError; }; export interface ShopifyExperienceResolverConfig { /** * App-proxy base on the Online Store's primary domain, e.g. * "https://shop.example.com/apps/fanfare/api". The live * resolution tier fetches `${proxyBaseUrl}/experiences/ids-by-handle/${key}`; * the proxy resolves the `$app:` namespace server-side, so no Storefront creds * or numeric app id are needed here. */ proxyBaseUrl: string; /** * SSR fast path: a preloaded map of product lookup key → raw `experienceIds` * metafield value (the JSON-array string like `'["exp_1"]'`, not a bare id). * When the normalized key is present, `resolve(product)` parses it via * `parseFirstExperienceId` and performs NO network call. When it is absent, * `resolve(product)` falls through to the live app-proxy read below. * Provide it to resolve from values you have already SSR-loaded; omit it to * always go to the network. */ metafieldValues?: Record; /** * Full-control override: your own product → experienceId function. When supplied * it takes precedence over both the preloaded map and the live read. May be sync * or async; return a bare Fanfare id (`"exp_1"`) or `null` for an uncovered * product. */ resolveExperience?: (product: ShopifyProductRef) => string | null | Promise; /** * Injectable fetch (SSR/test seam) for the live app-proxy read of the product's * experience ids by lookup key. Defaults to `globalThis.fetch`. Only exercised * when neither `resolveExperience` nor a preloaded `metafieldValues[key]` covers * the product. */ fetchImpl?: typeof fetch; /** * Shared in-flight live reads keyed by request URL; an entry is removed when * its request settles. Supply a shared map to coalesce concurrent live reads * across callers; absent means no coalescing. A request that never settles keeps * its entry, so every later read of the same URL joins it; no deadline is applied. */ inFlight?: Map>; } /** * Resolve a Shopify product to a gated, ungated, or typed error outcome. * Resolution uses the override first, then a preloaded metafield value, then * a live app-proxy read. The normalized lookup key accepts handles, storefront * product URLs, and product ids. An unresolvable ref is ungated; failures are * returned as error outcomes rather than thrown. */ export declare function resolveShopifyExperience(product: ShopifyProductRef, config: ShopifyExperienceResolverConfig): Promise; /** * Core's `ExperienceResolver` seam has no error slot, so error outcomes collapse * to `null` there. */ export declare function resolveShopifyExperienceId(product: ShopifyProductRef, config: ShopifyExperienceResolverConfig): Promise; /** * Build a Shopify ExperienceResolver. `resolve(product)` returns the product's * Fanfare experience id (first id wins), or null when the product is not covered or resolution fails. * * Resolution follows the three-tier order in `resolveShopifyExperience`: a * `resolveExperience` override, else a preloaded `metafieldValues[key]` (SSR * fast path, no network), else a live app-proxy read of * `GET {proxyBaseUrl}/experiences/ids-by-handle/{key}` via `fetchImpl`. */ export declare function createShopifyExperienceResolver(config: ShopifyExperienceResolverConfig): ExperienceResolver;