/** * Finds the Swish save buttons already on a page and says which product each * one is about (SWI-3033). * * Lives here rather than in the customiser because the resolvers it must match * are SDK internals and this package publishes a single entry, so the customiser * cannot import them. It deliberately returns plain data and ELEMENTS, never * strings shaped for a reader: describing an element for a model is the * customiser's job, and its summariser is where those strings get bounded. */ import { type ProductRef } from "../state/item-context-source"; /** How a save button reached the storefront. Each resolves its product with its * own code, which is why the scan cannot use one sweep for all of them. * * The theme extension's `wishlist-*` app blocks are deliberately absent. They * are the previous product generation: they load `@appmate/wishlist` through * `WishlistKingAppLoaderURL`, not this SDK. Leaving them out rests on a PRODUCT * assumption that a shop runs one generation or the other; nothing in code * enforces it. Both embeds live in the same theme extension, so a merchant * migrating from Wishlist King who leaves the old embed and its block enabled * would get a duplicate this scan does not report, not even as unverified. */ export type ExistingSaveButtonSource = "placement" | "theme"; export interface ExistingSaveButton { el: HTMLElement; source: ExistingSaveButtonSource; /** `handle:` or `id:`, or null when it could not be determined without * a network call. Never dropped: a button we cannot key is still a button, * and a real placement has been observed mounting with an empty product-id. */ productKey: string | null; } /** * Normalise a ref to a comparable product key. Synchronous throughout: * `parseProductUrl` is a regex, not a query. * * Two keys decide anything only in the SAME namespace. A handle against an id * needs a product lookup, which a pre-mount check must not wait on, so the * caller treats that pair as undecided rather than guessing either way. */ export declare function productKey(ref: ProductRef): string | null; /** Which product a save button placed on a target would save, and where on * the page that answer came from. */ export interface SaveButtonTargetProduct { /** As `saveButtonProductKey`: null when nothing names a product. */ productKey: string | null; /** The part of the page the product was read from: the target itself when * it names its product, holds the link, or nothing names a product at all; * otherwise the closest ancestor * holding both the target and the link the resolver climbed to. Null when * the page URL names the product. * * A caller placing one button per product compares this with its OTHER * targets. A footer menu or a promo tile in a grid has no product of its * own, so the resolver climbs until it reaches any product link, and the * area it climbed to holds the real cards (SWI-3140). Counting the products * in the area cannot tell those apart: a real card that links its sibling * colours holds several products, and on a page with one product a footer's * area holds only one. */ area: HTMLElement | null; } /** * The product for a target a placement is ABOUT to mount on, and the area it * was read from. Wraps the placement-side resolver, which already encodes the * pdp-versus-card fallback ordering, then normalises its answer the same way an * existing button's is. */ export declare function saveButtonTargetProduct(target: HTMLElement, context?: string): SaveButtonTargetProduct; /** The key for a target a placement is ABOUT to mount on. */ export declare function saveButtonProductKey(target: HTMLElement, context?: string): string | null; /** * Every Swish save button on the page, by the two routes one can reach a * storefront. Never throws, and one element failing to resolve does not fail its * siblings: a reader that throws takes the whole tool call with it. */ export declare function listExistingSaveButtons(root?: ParentNode | null): ExistingSaveButton[];