{"version":3,"file":"rewards.cjs","names":["getSupportedCurrency","getCurrencyAmountKey","formatAmount","getCurrencyAmountKey","getSupportedCurrency","formatAmount"],"sources":["../src/rewards/conditions.ts","../src/rewards/example.ts","../src/rewards/value.ts","../src/rewards/format.ts","../src/rewards/select.ts"],"sourcesContent":["import type { ConditionGroup, RuleCondition, RuleConditions } from \"../types\";\n\ntype ConditionNode = RuleCondition | ConditionGroup;\n\nfunction conditionNodes(conditions: RuleConditions): ConditionNode[] {\n    return Array.isArray(conditions) ? conditions : conditions.conditions;\n}\n\nfunction conditionValueToNumber(\n    value: RuleCondition[\"value\"]\n): number | undefined {\n    if (typeof value === \"number\") return value;\n    if (typeof value === \"string\" && value.trim() !== \"\") {\n        const parsed = Number(value);\n        return Number.isFinite(parsed) ? parsed : undefined;\n    }\n    return undefined;\n}\n\n// Conditions arrive as a flat list or recursive groups; collect every numeric\n// value whose field + operator match, descending into nested groups.\nfunction collectConditionValues(\n    conditions: RuleConditions,\n    field: string,\n    operators: ReadonlySet<RuleCondition[\"operator\"]>\n): number[] {\n    const values: number[] = [];\n    for (const node of conditionNodes(conditions)) {\n        if (\"logic\" in node) {\n            values.push(...collectConditionValues(node, field, operators));\n            continue;\n        }\n        if (node.field === field && operators.has(node.operator)) {\n            const value = conditionValueToNumber(node.value);\n            if (value != null) values.push(value);\n        }\n    }\n    return values;\n}\n\nconst MIN_PURCHASE_OPERATORS: ReadonlySet<RuleCondition[\"operator\"]> = new Set([\n    \"gt\",\n    \"gte\",\n    \"between\",\n]);\nconst START_DATE_OPERATORS: ReadonlySet<RuleCondition[\"operator\"]> = new Set([\n    \"gt\",\n    \"gte\",\n]);\n\nexport function extractMinPurchaseAmount(\n    conditions: RuleConditions\n): number | undefined {\n    const values = collectConditionValues(\n        conditions,\n        \"purchase.amount\",\n        MIN_PURCHASE_OPERATORS\n    );\n    return values.length > 0 ? Math.min(...values) : undefined;\n}\n\n// The business app encodes a campaign start as a `time.timestamp >= <unix s>`\n// rule condition; the endpoint returns it verbatim inside `conditions`.\nexport function extractStartDate(conditions: RuleConditions): Date | undefined {\n    const values = collectConditionValues(\n        conditions,\n        \"time.timestamp\",\n        START_DATE_OPERATORS\n    );\n    if (values.length === 0) return undefined;\n    return new Date(Math.min(...values) * 1000);\n}\n","import type { EstimatedReward } from \"../types\";\n\ntype PercentageReward = Extract<EstimatedReward, { payoutType: \"percentage\" }>;\n\n// Reference order total used to turn a \"% of basket\" reward into a concrete\n// example amount. The campaign's minimum purchase overrides it when higher.\nconst REFERENCE_BASKET = 100;\n\nexport type RewardExample = {\n    basket: number;\n    reward: number;\n};\n\nfunction roundWithinRange(min: number, max: number): number {\n    const rounded = Math.round((min + max) / 2 / 10) * 10;\n    return Math.min(max, Math.max(min, rounded));\n}\n\nexport function pickFlatBasket(minPurchase: number | undefined): number {\n    return Math.max(REFERENCE_BASKET, minPurchase ?? 0);\n}\n\n// Basket for a percentage tier: the reference when it sits inside the tier\n// range, a round number within the range otherwise, or the lower bound for an\n// open-ended (uncapped) top tier.\nexport function pickTierBasket(\n    minValue: number,\n    maxValue: number | undefined\n): number {\n    if (maxValue == null) return minValue;\n    if (minValue <= REFERENCE_BASKET && REFERENCE_BASKET <= maxValue) {\n        return REFERENCE_BASKET;\n    }\n    return roundWithinRange(minValue, maxValue);\n}\n\nexport function buildPercentageExample(\n    reward: PercentageReward,\n    minPurchase: number | undefined\n): RewardExample | undefined {\n    const basket = pickFlatBasket(minPurchase);\n    if (basket <= 0) return undefined;\n    let amount = (reward.percent / 100) * basket;\n    if (reward.minAmount) amount = Math.max(amount, reward.minAmount.eurAmount);\n    if (reward.maxAmount) amount = Math.min(amount, reward.maxAmount.eurAmount);\n    return { basket, reward: amount };\n}\n\nexport function buildTierExample(\n    percent: number,\n    minValue: number,\n    maxValue: number | undefined\n): RewardExample | undefined {\n    const basket = pickTierBasket(minValue, maxValue);\n    if (basket <= 0) return undefined;\n    return { basket, reward: (percent / 100) * basket };\n}\n","import type { EstimatedReward, TokenAmountType } from \"../types\";\n\n/**\n * Comparable fiat value of a single reward, used to rank rewards against each\n * other.\n *\n * - `fixed`      → the token amount in the requested currency\n * - `tiered`     → the highest token amount across tiers\n * - `percentage` → the capped (`maxAmount`) value when present, otherwise `0`\n *                  (an uncapped percentage has no comparable fiat value)\n */\nexport function getRewardValue(\n    reward: EstimatedReward,\n    key: keyof TokenAmountType\n): number {\n    switch (reward.payoutType) {\n        case \"fixed\":\n            return reward.amount[key];\n        case \"percentage\":\n            return reward.maxAmount?.[key] ?? 0;\n        case \"tiered\":\n            return reward.tiers.reduce(\n                (max, tier) =>\n                    \"amount\" in tier ? Math.max(max, tier.amount[key]) : max,\n                0\n            );\n    }\n}\n\n/**\n * Highest percent a reward exposes: the flat `percentage` percent, or the\n * richest percent across `tiered` tiers, or `0` when it carries none.\n *\n * Shared by {@link getRewardRank} (to weight a percentage-only reward) and\n * `formatEstimatedReward` (to render it as `\"X %\"`), so the value the UI ranks\n * by and the value it prints stay derived from one tier traversal.\n */\nexport function maxRewardPercent(reward: EstimatedReward): number {\n    if (reward.payoutType === \"percentage\") return reward.percent;\n    if (reward.payoutType === \"tiered\") {\n        return reward.tiers.reduce(\n            (max, tier) =>\n                \"percent\" in tier ? Math.max(max, tier.percent) : max,\n            0\n        );\n    }\n    return 0;\n}\n\n// A reward with no money value (an uncapped percentage, or a percent-only\n// tier set) still renders as \"X %\", so it is worth surfacing. We give it a\n// positive ranking weight derived from its percent but scaled far below any\n// real-money reward, so the two invariants the UI relies on hold: (1) a reward\n// with real money always outranks a percentage-only reward, and (2) a\n// percentage-only reward still outranks a zero-value reward. Together they\n// guarantee the reward the ranking picks is always one we can display.\nconst PERCENT_ONLY_RANK_WEIGHT = 1e-6;\n\n/**\n * Ranking weight used to pick the single most attractive reward to surface.\n *\n * Mirrors {@link getRewardValue} (money value) but lifts a percentage-only\n * reward to a tiny positive weight instead of `0`, so it is never buried\n * behind a zero-value reward when choosing what to display.\n */\nexport function getRewardRank(\n    reward: EstimatedReward,\n    key: keyof TokenAmountType\n): number {\n    const value = getRewardValue(reward, key);\n    if (value > 0) return value;\n    return maxRewardPercent(reward) * PERCENT_ONLY_RANK_WEIGHT;\n}\n","import type { Currency, EstimatedReward } from \"../types\";\nimport { formatAmount } from \"../utils/format/formatAmount\";\nimport { getCurrencyAmountKey } from \"../utils/format/getCurrencyAmountKey\";\nimport { getSupportedCurrency } from \"../utils/format/getSupportedCurrency\";\nimport { getRewardRank, getRewardValue, maxRewardPercent } from \"./value\";\n\n/**\n * Format an {@link EstimatedReward} into a human-readable string.\n *\n * - `fixed`      → e.g. `\"5 €\"`\n * - `percentage` → e.g. `\"10 %\"`\n * - `tiered`     → the richest tier: max token amount (e.g. `\"50 €\"`) or, when\n *                  tiers only carry percentages, the max percent (e.g. `\"10 %\"`)\n *\n * Percentages are always rendered as a `\"X %\"` string: the backend never sends\n * a reference basket, so the reward cannot be resolved to a concrete amount\n * here. Callers that need a worked example use the `example` helpers instead.\n */\nexport function formatEstimatedReward(\n    reward: EstimatedReward,\n    currency?: Currency\n): string {\n    const supportedCurrency = getSupportedCurrency(currency);\n    const key = getCurrencyAmountKey(supportedCurrency);\n\n    switch (reward.payoutType) {\n        case \"fixed\":\n            return formatAmount(\n                Math.round(reward.amount[key]),\n                supportedCurrency\n            );\n\n        case \"percentage\":\n            return `${reward.percent} %`;\n\n        case \"tiered\": {\n            const maxAmount = getRewardValue(reward, key);\n            if (maxAmount > 0) {\n                return formatAmount(Math.round(maxAmount), supportedCurrency);\n            }\n            const maxPercent = maxRewardPercent(reward);\n            if (maxPercent > 0) {\n                return `${maxPercent} %`;\n            }\n            return formatAmount(0, supportedCurrency);\n        }\n    }\n}\n\n/**\n * Format a reward for display, or return `undefined` when it is not worth\n * advertising. Callers rely on `undefined` to hide a badge / fall back to\n * other copy.\n *\n * A reward is hidden only when it carries no displayable value — a `fixed` or\n * `tiered` reward whose money value is `0` (e.g. an unknown token price). An\n * uncapped percentage always renders as `\"X %\"`, since the percent itself is\n * meaningful even without a money value.\n */\nexport function formatRewardOrHide(\n    reward: EstimatedReward | undefined,\n    currency?: Currency\n): string | undefined {\n    if (!reward) return undefined;\n    const key = getCurrencyAmountKey(getSupportedCurrency(currency));\n    if (getRewardRank(reward, key) <= 0) return undefined;\n    return formatEstimatedReward(reward, currency);\n}\n\n/**\n * Replace the `{REWARD}` placeholder in a text string with a reward value.\n * If no reward is provided, returns the text with `{REWARD}` stripped.\n */\nexport function applyRewardPlaceholder(\n    text: string,\n    reward: string | undefined\n): string {\n    return reward\n        ? text.replaceAll(\"{REWARD}\", reward)\n        : text.replaceAll(\"{REWARD}\", \"\");\n}\n","import type { InteractionTypeKey } from \"../constants/interactionTypes\";\nimport type {\n    Currency,\n    EstimatedReward,\n    MerchantReward,\n    TokenAmountType,\n} from \"../types\";\nimport { formatAmount } from \"../utils/format/formatAmount\";\nimport { getCurrencyAmountKey } from \"../utils/format/getCurrencyAmountKey\";\nimport { getSupportedCurrency } from \"../utils/format/getSupportedCurrency\";\nimport { extractMinPurchaseAmount, extractStartDate } from \"./conditions\";\nimport { formatRewardOrHide } from \"./format\";\nimport { getRewardRank } from \"./value\";\n\n/** Reward side a surface cares about: the sharer (`referrer`) or the referred\n * user (`referee`). Drives both campaign ranking and which side is displayed. */\nexport type RewardAudience = \"referrer\" | \"referee\";\n\nexport type DisplayCampaign = {\n    campaign: MerchantReward;\n    status: \"live\" | \"upcoming\";\n    startsAt?: Date;\n};\n\nexport type SelectDisplayCampaignOptions = {\n    /** Reference \"now\" for expiry / start gating; defaults to the current time. */\n    now?: Date;\n    /** Currency the ranking is expressed in; defaults to EUR. */\n    currency?: Currency;\n    /** When set, only campaigns triggered by this interaction are considered. */\n    targetInteraction?: InteractionTypeKey;\n    /** Reward side to rank campaigns by; defaults to `\"referrer\"`. */\n    audience?: RewardAudience;\n};\n\nfunction isExpired(campaign: MerchantReward, nowMs: number): boolean {\n    return (\n        campaign.expiresAt != null &&\n        new Date(campaign.expiresAt).getTime() <= nowMs\n    );\n}\n\nfunction hasStarted(campaign: MerchantReward, nowMs: number): boolean {\n    const startsAt = extractStartDate(campaign.conditions);\n    return startsAt == null || startsAt.getTime() <= nowMs;\n}\n\nfunction audienceReward(campaign: MerchantReward, audience: RewardAudience) {\n    return audience === \"referee\" ? campaign.referee : campaign.referrer;\n}\n\nfunction campaignRank(\n    campaign: MerchantReward,\n    key: keyof TokenAmountType,\n    audience: RewardAudience\n): number {\n    const reward = audienceReward(campaign, audience);\n    return reward ? getRewardRank(reward, key) : 0;\n}\n\n/**\n * Pick the single campaign a merchant surface should display.\n *\n * Filters out expired (and, when `targetInteraction` is set, non-matching)\n * campaigns, then prefers the highest-ranked *live* campaign — ranked by the\n * `audience` reward side in the requested currency. When none has started yet\n * it falls back to the soonest-starting upcoming campaign (the endpoint does\n * not gate on the start-date condition, so future-start campaigns come\n * through).\n */\nexport function selectDisplayCampaign(\n    rewards: readonly MerchantReward[],\n    options: SelectDisplayCampaignOptions = {}\n): DisplayCampaign | undefined {\n    const nowMs = (options.now ?? new Date()).getTime();\n    const audience = options.audience ?? \"referrer\";\n    const key = getCurrencyAmountKey(getSupportedCurrency(options.currency));\n\n    const matching = options.targetInteraction\n        ? rewards.filter(\n              (campaign) =>\n                  campaign.interactionTypeKey === options.targetInteraction\n          )\n        : rewards;\n    const active = matching.filter((campaign) => !isExpired(campaign, nowMs));\n\n    const live = active.filter((campaign) => hasStarted(campaign, nowMs));\n    if (live.length > 0) {\n        const best = live.reduce((a, b) =>\n            campaignRank(b, key, audience) > campaignRank(a, key, audience)\n                ? b\n                : a\n        );\n        return { campaign: best, status: \"live\" };\n    }\n\n    const upcoming = active\n        .map((campaign) => ({\n            campaign,\n            startsAt: extractStartDate(campaign.conditions),\n        }))\n        .filter(\n            (entry): entry is { campaign: MerchantReward; startsAt: Date } =>\n                entry.startsAt != null\n        );\n    if (upcoming.length === 0) return undefined;\n\n    const soonest = upcoming.reduce((a, b) =>\n        b.startsAt.getTime() < a.startsAt.getTime() ? b : a\n    );\n    return {\n        campaign: soonest.campaign,\n        status: \"upcoming\",\n        startsAt: soonest.startsAt,\n    };\n}\n\n/**\n * The single reward a merchant surface should display: its formatted string\n * plus the `payoutType` of the underlying reward, so surfaces can adapt their\n * presentation (e.g. hide percentage rewards, prefix tiered ones with \"Up to\").\n */\nexport type BestReward = {\n    /** Display-ready reward string (e.g. `\"5 €\"`, `\"10 %\"`). */\n    formatted: string;\n    /** Payout type of the selected reward. */\n    payoutType: EstimatedReward[\"payoutType\"];\n    /**\n     * Minimum purchase amount gating the reward, formatted with the requested\n     * currency (e.g. `\"10 €\"`), or `undefined` when the campaign sets no\n     * minimum.\n     */\n    minPurchaseAmount?: string;\n    /**\n     * Whole-day lockup applied before the reward settles, or `undefined` when\n     * the campaign has no lockup.\n     */\n    lockupDurationDays?: number;\n    /**\n     * Raw referrer/referee rewards of the selected campaign, surfaced so\n     * consumers can render the full per-audience breakdown (tier rows,\n     * percentage examples) rather than only the headline number.\n     */\n    referrerReward?: EstimatedReward;\n    refereeReward?: EstimatedReward;\n    /**\n     * Raw minimum purchase value (unformatted), used to build percentage\n     * worked-examples consistent with the campaign's gating.\n     */\n    minPurchaseValue?: number;\n};\n\n/**\n * Pick the best campaign for `options` and resolve its `audience`-side reward\n * to a formatted string plus its `payoutType`, or `undefined` when there is\n * nothing worth showing.\n *\n * Single entry point shared by every \"headline reward\" surface (share button,\n * wallet modal, sharing/install screens) so they all show the same number for\n * a given merchant and can branch on the payout type.\n */\nexport function selectBestReward(\n    rewards: readonly MerchantReward[],\n    options: SelectDisplayCampaignOptions = {}\n): BestReward | undefined {\n    const selected = selectDisplayCampaign(rewards, options);\n    if (!selected) return undefined;\n    const reward = audienceReward(\n        selected.campaign,\n        options.audience ?? \"referrer\"\n    );\n    if (!reward) return undefined;\n    const formatted = formatRewardOrHide(reward, options.currency);\n    if (!formatted) return undefined;\n\n    const minPurchase = extractMinPurchaseAmount(selected.campaign.conditions);\n    const minPurchaseAmount =\n        minPurchase != null\n            ? formatAmount(minPurchase, options.currency)\n            : undefined;\n\n    const lockupSeconds = selected.campaign.defaultLockupSeconds;\n    const lockupDurationDays =\n        lockupSeconds && lockupSeconds > 0\n            ? Math.round(lockupSeconds / 86_400)\n            : undefined;\n\n    return {\n        formatted,\n        payoutType: reward.payoutType,\n        minPurchaseAmount,\n        lockupDurationDays,\n        referrerReward: selected.campaign.referrer,\n        refereeReward: selected.campaign.referee,\n        minPurchaseValue: minPurchase ?? undefined,\n    };\n}\n\n/**\n * Headline reward string for a merchant: picks the best campaign for `options`\n * and formats its `audience`-side reward, or returns `undefined` when there is\n * nothing worth showing.\n *\n * Thin wrapper over {@link selectBestReward} for callers that only need the\n * formatted string.\n */\nexport function formatBestReward(\n    rewards: readonly MerchantReward[],\n    options: SelectDisplayCampaignOptions = {}\n): string | undefined {\n    return selectBestReward(rewards, options)?.formatted;\n}\n"],"mappings":"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"}