@burdenoff/website-sdk 2026.1014.1 → 2026.1021.1

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/index.d.mts CHANGED
@@ -7,7 +7,7 @@ export { B as BlogListPage, a as BlogListPageProps, b as BlogPostPage, c as Blog
7
7
  import * as react from 'react';
8
8
  import { ComponentType, ReactNode } from 'react';
9
9
  export { NewsletterForm, NewsletterFormProps, NewsletterPage, NewsletterPageProps } from './components/newsletter.mjs';
10
- export { PlanCard, PricingPage, PricingProps, PricingSection } from './components/pricing.mjs';
10
+ export { PlanCard, PricingPage, PricingProductKeyError, PricingProps, PricingSection, availablePlanCurrencies, cleanPlanDisplayName, isUuidLike, planPriceIn, resolvePricingProductKey } from './components/pricing.mjs';
11
11
  export { WaitlistForm, WaitlistFormProps, WaitlistPage, WaitlistPageProps } from './components/waitlist.mjs';
12
12
  export { UnsubscribePage, UnsubscribePageProps } from './components/unsubscribe.mjs';
13
13
  export { SocialLinks, SocialLinksProps, SocialPlatform } from './components/social-links.mjs';
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export { B as BlogListPage, a as BlogListPageProps, b as BlogPostPage, c as Blog
7
7
  import * as react from 'react';
8
8
  import { ComponentType, ReactNode } from 'react';
9
9
  export { NewsletterForm, NewsletterFormProps, NewsletterPage, NewsletterPageProps } from './components/newsletter.js';
10
- export { PlanCard, PricingPage, PricingProps, PricingSection } from './components/pricing.js';
10
+ export { PlanCard, PricingPage, PricingProductKeyError, PricingProps, PricingSection, availablePlanCurrencies, cleanPlanDisplayName, isUuidLike, planPriceIn, resolvePricingProductKey } from './components/pricing.js';
11
11
  export { WaitlistForm, WaitlistFormProps, WaitlistPage, WaitlistPageProps } from './components/waitlist.js';
12
12
  export { UnsubscribePage, UnsubscribePageProps } from './components/unsubscribe.js';
13
13
  export { SocialLinks, SocialLinksProps, SocialPlatform } from './components/social-links.js';
package/dist/index.js CHANGED
@@ -2983,6 +2983,168 @@ function NewsletterForm({
2983
2983
  )
2984
2984
  ] });
2985
2985
  }
2986
+
2987
+ // src/components/pricing-catalog.ts
2988
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2989
+ function isUuidLike(value) {
2990
+ return UUID_PATTERN.test(value.trim());
2991
+ }
2992
+ var PricingProductKeyError = class extends Error {
2993
+ constructor(productId) {
2994
+ super(
2995
+ `PricingPage was given the product id "${productId}", which matches no public product. publicPlans(productId) expects the product slug (for example "vibecontrols"); pass product.slug.`
2996
+ );
2997
+ this.name = "PricingProductKeyError";
2998
+ }
2999
+ };
3000
+ function resolvePricingProductKey(productId, candidates) {
3001
+ const trimmed = productId.trim();
3002
+ if (!isUuidLike(trimmed)) return trimmed;
3003
+ const wanted = trimmed.toLowerCase();
3004
+ const match = candidates.find(
3005
+ (c) => !!c && c.id.toLowerCase() === wanted && c.slug.trim().length > 0
3006
+ );
3007
+ return match ? match.slug.trim() : null;
3008
+ }
3009
+ var PERIOD_SUFFIX = /\s*[([]\s*(monthly|month|annual|annually|yearly|year)\s*[)\]]\s*$/i;
3010
+ var PRODUCT_PREFIX = /^\S.*?\s[-–—]\s+(?=\S)/;
3011
+ function cleanPlanDisplayName(name) {
3012
+ const withoutPeriod = name.replace(PERIOD_SUFFIX, "").trim();
3013
+ const withoutProduct = withoutPeriod.replace(PRODUCT_PREFIX, "").trim();
3014
+ if (withoutProduct.length > 0) return withoutProduct;
3015
+ if (withoutPeriod.length > 0) return withoutPeriod;
3016
+ return name.trim();
3017
+ }
3018
+ function isFiniteNumber(value) {
3019
+ return typeof value === "number" && Number.isFinite(value);
3020
+ }
3021
+ function planPriceIn(plan, currency) {
3022
+ const code = currency.toUpperCase();
3023
+ if (plan.price === 0) return 0;
3024
+ if (plan.currency.toUpperCase() === code) return plan.price;
3025
+ const prices = plan.currencyPrices;
3026
+ if (!prices) return null;
3027
+ const direct = prices[code];
3028
+ if (isFiniteNumber(direct)) return direct;
3029
+ const key = Object.keys(prices).find((k) => k.toUpperCase() === code);
3030
+ const value = key === void 0 ? void 0 : prices[key];
3031
+ return isFiniteNumber(value) ? value : null;
3032
+ }
3033
+ function availablePlanCurrencies(plans) {
3034
+ if (plans.length === 0) return [];
3035
+ const all = /* @__PURE__ */ new Set();
3036
+ for (const plan of plans) {
3037
+ all.add(plan.currency.toUpperCase());
3038
+ for (const key of Object.keys(plan.currencyPrices ?? {})) {
3039
+ all.add(key.toUpperCase());
3040
+ }
3041
+ }
3042
+ const usable = [...all].filter(
3043
+ (code) => plans.every((plan) => planPriceIn(plan, code) !== null)
3044
+ );
3045
+ const base = plans.find((p) => p.price > 0)?.currency.toUpperCase();
3046
+ return usable.sort((a, b) => {
3047
+ if (a === base) return -1;
3048
+ if (b === base) return 1;
3049
+ return a.localeCompare(b);
3050
+ });
3051
+ }
3052
+ var EURO_REGIONS = [
3053
+ "AT",
3054
+ "BE",
3055
+ "CY",
3056
+ "DE",
3057
+ "EE",
3058
+ "ES",
3059
+ "FI",
3060
+ "FR",
3061
+ "GR",
3062
+ "HR",
3063
+ "IE",
3064
+ "IT",
3065
+ "LT",
3066
+ "LU",
3067
+ "LV",
3068
+ "MT",
3069
+ "NL",
3070
+ "PT",
3071
+ "SI",
3072
+ "SK"
3073
+ ];
3074
+ var REGION_CURRENCY = Object.freeze({
3075
+ AU: "AUD",
3076
+ BR: "BRL",
3077
+ CA: "CAD",
3078
+ CH: "CHF",
3079
+ CN: "CNY",
3080
+ DK: "DKK",
3081
+ GB: "GBP",
3082
+ HK: "HKD",
3083
+ IN: "INR",
3084
+ JP: "JPY",
3085
+ KR: "KRW",
3086
+ MX: "MXN",
3087
+ MY: "MYR",
3088
+ NO: "NOK",
3089
+ NZ: "NZD",
3090
+ PL: "PLN",
3091
+ SE: "SEK",
3092
+ SG: "SGD",
3093
+ TH: "THB",
3094
+ US: "USD",
3095
+ ...Object.fromEntries(EURO_REGIONS.map((r) => [r, "EUR"]))
3096
+ });
3097
+ var TIME_ZONE_REGION = Object.freeze({
3098
+ "Asia/Kolkata": "IN",
3099
+ "Asia/Calcutta": "IN"
3100
+ });
3101
+ function regionOfLanguage(tag) {
3102
+ const parts = tag.replace(/_/g, "-").split("-");
3103
+ for (const part of parts.slice(1)) {
3104
+ if (/^[A-Za-z]{2}$/.test(part)) return part.toUpperCase();
3105
+ }
3106
+ return null;
3107
+ }
3108
+ function detectRegionalCurrency(available, locale) {
3109
+ const sellable = new Set(available.map((c) => c.toUpperCase()));
3110
+ const regions = [];
3111
+ const tzRegion = locale.timeZone ? TIME_ZONE_REGION[locale.timeZone] : null;
3112
+ if (tzRegion) regions.push(tzRegion);
3113
+ for (const tag of locale.languages ?? []) {
3114
+ const region = regionOfLanguage(tag);
3115
+ if (region) regions.push(region);
3116
+ }
3117
+ for (const region of regions) {
3118
+ const currency = REGION_CURRENCY[region];
3119
+ if (currency && sellable.has(currency)) return currency;
3120
+ }
3121
+ return null;
3122
+ }
3123
+ function formatCatalogPrice(amount, currency) {
3124
+ const code = currency.toUpperCase();
3125
+ const whole = amount % 1 === 0;
3126
+ try {
3127
+ return new Intl.NumberFormat("en-US", {
3128
+ style: "currency",
3129
+ currency: code,
3130
+ minimumFractionDigits: whole ? 0 : 2,
3131
+ maximumFractionDigits: whole ? 0 : 2
3132
+ }).format(amount);
3133
+ } catch {
3134
+ return `${code} ${whole ? amount.toString() : amount.toFixed(2)}`;
3135
+ }
3136
+ }
3137
+ function plansForBillingCycle(plans, cycle) {
3138
+ if (cycle === "monthly") {
3139
+ return plans.filter((p) => p.duration === "monthly");
3140
+ }
3141
+ const yearly = plans.filter((p) => p.duration === "yearly");
3142
+ if (yearly.some((p) => p.priceRaw === 0)) return yearly;
3143
+ const free = plans.filter(
3144
+ (p) => p.duration === "monthly" && p.priceRaw === 0
3145
+ );
3146
+ return [...free, ...yearly];
3147
+ }
2986
3148
  var GET_PLANS_QUERY = `
2987
3149
  query GetPublicPlans($productId: String!) {
2988
3150
  publicPlans(productId: $productId) {
@@ -3008,18 +3170,49 @@ var GET_PLANS_QUERY = `
3008
3170
  }
3009
3171
  }
3010
3172
  `;
3173
+ var PUBLIC_PRODUCT_KEYS_QUERY = `
3174
+ query PricingProductKeys {
3175
+ publicProducts(limit: 200) {
3176
+ items {
3177
+ id
3178
+ slug
3179
+ }
3180
+ }
3181
+ }
3182
+ `;
3183
+ var CURRENCY_STORAGE_KEY = "burdenoff.pricing.currency";
3184
+ function readStoredCurrency() {
3185
+ try {
3186
+ if (typeof window === "undefined") return null;
3187
+ return window.localStorage.getItem(CURRENCY_STORAGE_KEY);
3188
+ } catch {
3189
+ return null;
3190
+ }
3191
+ }
3192
+ function storeCurrency(code) {
3193
+ try {
3194
+ if (typeof window === "undefined") return;
3195
+ window.localStorage.setItem(CURRENCY_STORAGE_KEY, code);
3196
+ } catch {
3197
+ }
3198
+ }
3199
+ function readVisitorLocale() {
3200
+ let timeZone = null;
3201
+ try {
3202
+ timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
3203
+ } catch {
3204
+ timeZone = null;
3205
+ }
3206
+ const languages = typeof navigator === "undefined" ? [] : [...navigator.languages ?? [], navigator.language].filter(
3207
+ (l) => typeof l === "string" && l.length > 0
3208
+ );
3209
+ return { timeZone, languages };
3210
+ }
3011
3211
  function formatPrice(price, currencyCode = "USD", currencies) {
3012
- const defaultCurrencies = {
3013
- USD: { code: "USD", symbol: "$", position: "prefix" },
3014
- EUR: { code: "EUR", symbol: "\u20AC", position: "prefix" },
3015
- GBP: { code: "GBP", symbol: "\xA3", position: "prefix" },
3016
- INR: { code: "INR", symbol: "\u20B9", position: "prefix" }
3017
- };
3018
- const currencyMap = currencies || defaultCurrencies;
3019
- const currency = currencyMap[currencyCode] || {
3020
- symbol: currencyCode,
3021
- position: "prefix"
3022
- };
3212
+ const currency = currencies?.[currencyCode];
3213
+ if (!currency) {
3214
+ return formatCatalogPrice(price, currencyCode);
3215
+ }
3023
3216
  const formattedNumber = price % 1 === 0 ? price.toString() : price.toFixed(2);
3024
3217
  return currency.position === "prefix" ? `${currency.symbol}${formattedNumber}` : `${formattedNumber}${currency.symbol}`;
3025
3218
  }
@@ -3080,11 +3273,12 @@ function getFallbackFeaturesForTier(tier, fallbackFeatures) {
3080
3273
  }
3081
3274
  function getMetadataForPlan(planName, priceRaw, planMetadata) {
3082
3275
  const normalizedName = planName.toLowerCase();
3083
- if (planMetadata?.[normalizedName]) {
3084
- const meta = planMetadata[normalizedName];
3276
+ const cleanName = cleanPlanDisplayName(planName);
3277
+ const meta = planMetadata?.[normalizedName] ?? planMetadata?.[cleanName.toLowerCase()];
3278
+ if (meta) {
3085
3279
  return {
3086
3280
  tier: meta.tier || "basic",
3087
- displayName: meta.displayName || planName,
3281
+ displayName: meta.displayName || cleanName,
3088
3282
  defaultDescription: meta.defaultDescription || "Choose this plan",
3089
3283
  popular: meta.popular ?? false,
3090
3284
  ctaText: meta.ctaText || "Get Started",
@@ -3098,7 +3292,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
3098
3292
  if (isPaidEnterpriseName) {
3099
3293
  return {
3100
3294
  tier: "professional",
3101
- displayName: planName,
3295
+ displayName: cleanName,
3102
3296
  defaultDescription: "Choose this plan",
3103
3297
  popular: false,
3104
3298
  ctaText: "Get Started",
@@ -3107,7 +3301,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
3107
3301
  }
3108
3302
  return {
3109
3303
  tier,
3110
- displayName: planName,
3304
+ displayName: cleanName,
3111
3305
  defaultDescription: "Choose this plan",
3112
3306
  popular: tier === "professional",
3113
3307
  ctaText: tier === "enterprise" ? "Contact Sales" : "Get Started",
@@ -3116,7 +3310,7 @@ function getMetadataForPlan(planName, priceRaw, planMetadata) {
3116
3310
  }
3117
3311
  return {
3118
3312
  tier: "basic",
3119
- displayName: planName,
3313
+ displayName: cleanName,
3120
3314
  defaultDescription: "Choose this plan",
3121
3315
  popular: false,
3122
3316
  ctaText: "Get Started",
@@ -3173,17 +3367,14 @@ function extractDisplayFeatures(features, planName, fallbackFeatures) {
3173
3367
  }
3174
3368
  return [];
3175
3369
  }
3176
- function transformToDisplayCard(plan, props) {
3370
+ function transformToDisplayCard(plan, props, currency) {
3177
3371
  const metadata = getMetadataForPlan(
3178
3372
  plan.name,
3179
3373
  plan.price,
3180
3374
  props.planMetadata
3181
3375
  );
3182
- const formattedPrice = formatPrice(
3183
- plan.price,
3184
- plan.currency,
3185
- props.currencies
3186
- );
3376
+ const selectedPrice = currency ? planPriceIn(plan, currency) : null;
3377
+ const formattedPrice = currency && selectedPrice !== null ? formatPrice(selectedPrice, currency.toUpperCase(), props.currencies) : formatPrice(plan.price, plan.currency, props.currencies);
3187
3378
  const interval = formatInterval(plan.duration, props.billingIntervals);
3188
3379
  const features = metadata.features && metadata.features.length > 0 ? metadata.features : extractDisplayFeatures(
3189
3380
  plan.features,
@@ -3192,13 +3383,14 @@ function transformToDisplayCard(plan, props) {
3192
3383
  );
3193
3384
  const contactUrl = props.contactUrl || "/contact";
3194
3385
  const ctaLink = metadata.ctaType === "contact" ? contactUrl : props.appLoginUrl;
3386
+ const isQuoteBased = metadata.ctaType === "contact" && plan.price === 0;
3195
3387
  return {
3196
3388
  id: plan.id,
3197
- name: plan.name,
3389
+ name: metadata.displayName,
3198
3390
  description: metadata.defaultDescription,
3199
- price: formattedPrice,
3391
+ price: isQuoteBased ? "Custom" : formattedPrice,
3200
3392
  priceRaw: plan.price,
3201
- interval,
3393
+ interval: isQuoteBased ? "" : interval,
3202
3394
  duration: plan.duration,
3203
3395
  features,
3204
3396
  popular: metadata.popular,
@@ -3216,8 +3408,13 @@ function PricingSection(props) {
3216
3408
  } = props;
3217
3409
  const client = useWebSDK();
3218
3410
  const config = useWebSDKConfig();
3219
- const resolvedProductId = props.productId || config.productId;
3220
- const [allPlans, setAllPlans] = React.useState([]);
3411
+ const { product: contextProduct } = useProduct();
3412
+ const requestedProductId = (props.productId || config.productId || "").trim();
3413
+ const contextKey = resolvePricingProductKey(requestedProductId, [
3414
+ contextProduct
3415
+ ]);
3416
+ const productKey = contextKey ?? requestedProductId;
3417
+ const [catalogPlans, setCatalogPlans] = React.useState([]);
3221
3418
  const [loading, setLoading] = React.useState(true);
3222
3419
  const [error, setError] = React.useState(null);
3223
3420
  const [billingCycle, setBillingCycle] = React.useState(
@@ -3226,13 +3423,38 @@ function PricingSection(props) {
3226
3423
  const [audience, setAudience] = React.useState(
3227
3424
  "individual"
3228
3425
  );
3426
+ const [selectedCurrency, setSelectedCurrency] = React.useState(
3427
+ props.defaultCurrency ? props.defaultCurrency.toUpperCase() : null
3428
+ );
3229
3429
  React.useEffect(() => {
3230
3430
  let cancelled = false;
3431
+ async function resolveKey() {
3432
+ if (!productKey) {
3433
+ throw new Error(
3434
+ "PricingPage needs a product slug: pass productId or set WebSDKConfig.productId."
3435
+ );
3436
+ }
3437
+ if (!isUuidLike(productKey)) return productKey;
3438
+ const response = await client.query(
3439
+ PUBLIC_PRODUCT_KEYS_QUERY
3440
+ );
3441
+ if (response.errors && response.errors.length > 0) {
3442
+ throw new Error(response.errors[0].message);
3443
+ }
3444
+ const resolved = resolvePricingProductKey(
3445
+ productKey,
3446
+ response.data?.publicProducts?.items ?? []
3447
+ );
3448
+ if (!resolved) throw new PricingProductKeyError(productKey);
3449
+ return resolved;
3450
+ }
3231
3451
  async function loadPlans() {
3232
3452
  try {
3233
3453
  setLoading(true);
3454
+ const key = await resolveKey();
3455
+ if (cancelled) return;
3234
3456
  const response = await client.query(GET_PLANS_QUERY, {
3235
- productId: resolvedProductId
3457
+ productId: key
3236
3458
  });
3237
3459
  if (cancelled) return;
3238
3460
  if (response.errors && response.errors.length > 0) {
@@ -3241,12 +3463,20 @@ function PricingSection(props) {
3241
3463
  if (!response.data?.publicPlans) {
3242
3464
  throw new Error("No plans data received");
3243
3465
  }
3244
- const cards = response.data.publicPlans.filter((plan) => plan.isActive).map((plan) => transformToDisplayCard(plan, props)).sort((a, b) => a.priceRaw - b.priceRaw);
3245
- setAllPlans(cards);
3466
+ const active = response.data.publicPlans.filter(
3467
+ (plan) => plan.isActive
3468
+ );
3469
+ if (active.length === 0) {
3470
+ throw new Error(
3471
+ `publicPlans returned no active plans for product "${key}".`
3472
+ );
3473
+ }
3474
+ setCatalogPlans(active);
3246
3475
  setError(null);
3247
3476
  } catch (err) {
3248
3477
  if (cancelled) return;
3249
3478
  console.error("Failed to load plans:", err);
3479
+ setCatalogPlans([]);
3250
3480
  setError("Unable to load pricing information. Please try again later.");
3251
3481
  } finally {
3252
3482
  if (!cancelled) {
@@ -3258,13 +3488,38 @@ function PricingSection(props) {
3258
3488
  return () => {
3259
3489
  cancelled = true;
3260
3490
  };
3261
- }, [client, resolvedProductId]);
3491
+ }, [client, productKey]);
3492
+ const currencyOptions = React.useMemo(
3493
+ () => availablePlanCurrencies(catalogPlans),
3494
+ [catalogPlans]
3495
+ );
3496
+ React.useEffect(() => {
3497
+ if (selectedCurrency || currencyOptions.length < 2) return;
3498
+ const stored = readStoredCurrency()?.toUpperCase();
3499
+ if (stored && currencyOptions.includes(stored)) {
3500
+ setSelectedCurrency(stored);
3501
+ return;
3502
+ }
3503
+ const regional = detectRegionalCurrency(
3504
+ currencyOptions,
3505
+ readVisitorLocale()
3506
+ );
3507
+ if (regional) setSelectedCurrency(regional);
3508
+ }, [currencyOptions, selectedCurrency]);
3509
+ const activeCurrency = selectedCurrency && currencyOptions.includes(selectedCurrency) ? selectedCurrency : currencyOptions[0] ?? null;
3510
+ const allPlans = catalogPlans.map((plan) => transformToDisplayCard(plan, props, activeCurrency)).sort((a, b) => {
3511
+ const aCustom = a.price === "Custom" ? 1 : 0;
3512
+ const bCustom = b.price === "Custom" ? 1 : 0;
3513
+ return aCustom - bCustom || a.priceRaw - b.priceRaw;
3514
+ });
3515
+ const showCurrencySelector = props.showCurrencySelector !== false && currencyOptions.length > 1;
3262
3516
  const {
3263
3517
  individualPlans: individualAllPlans,
3264
3518
  enterprisePlans: enterpriseApiPlans
3265
3519
  } = splitPlansByAudience(allPlans, props.showAudienceTabs);
3266
- const individualPlans = individualAllPlans.filter(
3267
- (plan) => billingCycle === "monthly" ? plan.duration === "monthly" : plan.duration === "yearly"
3520
+ const individualPlans = plansForBillingCycle(
3521
+ individualAllPlans,
3522
+ billingCycle
3268
3523
  );
3269
3524
  const enterpriseCards = enterpriseApiPlans.map((p) => {
3270
3525
  if (p.priceRaw === 0 && p.name.toLowerCase().includes("enterprise")) {
@@ -3382,6 +3637,23 @@ function PricingSection(props) {
3382
3637
  children: "Yearly"
3383
3638
  }
3384
3639
  )
3640
+ ] }) }),
3641
+ showCurrencySelector && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mt-4", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [
3642
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Currency" }),
3643
+ /* @__PURE__ */ jsxRuntime.jsx(
3644
+ "select",
3645
+ {
3646
+ value: activeCurrency ?? "",
3647
+ onChange: (event) => {
3648
+ const code = event.target.value;
3649
+ setSelectedCurrency(code);
3650
+ storeCurrency(code);
3651
+ },
3652
+ className: "h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring",
3653
+ "data-testid": "pricing-currency-select",
3654
+ children: currencyOptions.map((code) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: code, children: code }, code))
3655
+ }
3656
+ )
3385
3657
  ] }) })
3386
3658
  ] }),
3387
3659
  props.showAudienceTabs && audience === "enterprise" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl text-center mb-8", children: [
@@ -19123,6 +19395,7 @@ exports.PageHead = PageHead;
19123
19395
  exports.PartnersPage = PartnersPage;
19124
19396
  exports.PressKitPage = PressKitPage;
19125
19397
  exports.PricingPage = PricingPage;
19398
+ exports.PricingProductKeyError = PricingProductKeyError;
19126
19399
  exports.PricingSection = PricingSection;
19127
19400
  exports.ProblemsSolvedSection = ProblemsSolvedSection;
19128
19401
  exports.ProductCard = ProductCard;
@@ -19149,8 +19422,10 @@ exports.answerPlainText = answerPlainText;
19149
19422
  exports.answerSourceHost = answerSourceHost;
19150
19423
  exports.answerSpeechText = answerSpeechText;
19151
19424
  exports.answerStrings = answerStrings;
19425
+ exports.availablePlanCurrencies = availablePlanCurrencies;
19152
19426
  exports.buttonVariants = buttonVariants;
19153
19427
  exports.classifyExploreError = classifyExploreError;
19428
+ exports.cleanPlanDisplayName = cleanPlanDisplayName;
19154
19429
  exports.cn = cn;
19155
19430
  exports.conceptPath = conceptPath;
19156
19431
  exports.detectElectronPlatform = detectElectronPlatform;
@@ -19172,14 +19447,17 @@ exports.isInlineImageAllowed = isInlineImageAllowed;
19172
19447
  exports.isInlineVideoAllowed = isInlineVideoAllowed;
19173
19448
  exports.isPublishedSourceUrl = isPublishedSourceUrl;
19174
19449
  exports.isSameProductSlug = isSameProductSlug;
19450
+ exports.isUuidLike = isUuidLike;
19175
19451
  exports.isValidUrl = isValidUrl;
19176
19452
  exports.legacyProductSlug = legacyProductSlug;
19177
19453
  exports.loadRecaptchaV3 = loadRecaptchaV3;
19178
19454
  exports.looksLikeDeliverableEmail = looksLikeDeliverableEmail;
19179
19455
  exports.normalizeProductSlug = normalizeProductSlug;
19180
19456
  exports.normalizeRoutePath = normalizeRoutePath;
19457
+ exports.planPriceIn = planPriceIn;
19181
19458
  exports.productSlugVariants = productSlugVariants;
19182
19459
  exports.productSlugsByOrigin = productSlugsByOrigin;
19460
+ exports.resolvePricingProductKey = resolvePricingProductKey;
19183
19461
  exports.shouldRenderSiteFooter = shouldRenderSiteFooter;
19184
19462
  exports.speechLocaleOf = speechLocaleOf;
19185
19463
  exports.stopSpeaking = stopSpeaking;