@lime-bundles/core
v4.1.0
Published
Headless storefront primitives for the Lime Bundles Shopify app. Storefront API client, bundle parser, pricing math, analytics, and A/B helpers.
Maintainers
Readme
@lime-bundles/core
Headless storefront primitives for the Lime Bundles Shopify app. If you're not a merchant using Lime Bundles, this package probably isn't what you're looking for.
Used internally by @lime-bundles/react and @lime-bundles/widget. Import it directly when you're building on Vue, Svelte, a native app, or any non-React / non-custom-element surface with fetch.
Install
npm install @lime-bundles/coreNo peer dependencies. Works in any modern browser or Node runtime with fetch and AbortController.
Quick start
Fetch a bundle
import {
createStorefrontClient,
BUNDLE_METAOBJECT_QUERY,
parseMetaobjectBundleStrict,
type BundleMetaobjectResponse,
} from "@lime-bundles/core";
// Inside your server-side handler (Hydrogen loader, Next.js route
// handler, Express middleware, etc.) where `request` is the incoming
// Request object:
const client = createStorefrontClient({
shopDomain: "my-shop.myshopify.com",
accessToken: process.env.LIME_BUNDLES_TOKEN!,
// Required on SSR. Forwarded as Shopify-Storefront-Buyer-IP; Shopify
// may return 430 for server traffic without it.
buyerIp: request.headers.get("x-forwarded-for")?.split(",")[0].trim(),
});
const data = await client.query<BundleMetaobjectResponse>(
BUNDLE_METAOBJECT_QUERY,
{ id: "gid://shopify/Metaobject/42" },
);
const bundle = parseMetaobjectBundleStrict(
data.metaobject!.id,
data.metaobject!.fields,
);parseMetaobjectBundleStrict throws BundleParseError with a typed reason (not_found, invalid_type, inactive, not_started, expired). Use parseMetaobjectBundle for the non-throwing variant that returns null.
All bundles for a product
import { fetchBundlesForProduct } from "@lime-bundles/core";
const bundles = await fetchBundlesForProduct({
shopDomain,
storefrontAccessToken,
productHandle: "snowboard",
});Returns only active, in-schedule bundles. No further filtering needed.
Pricing that matches Shopify checkout
import { computeFixedPricing, formatCents } from "@lime-bundles/core";
const pricing = computeFixedPricing(bundle);
// { rows, totalCents, saleCents, savingsCents, headerBadge, currency }
console.log(formatCents(pricing.saleCents, pricing.currency));Uses per-unit floor rounding so widget totals match what the customer pays. Naive total * (1 - discount) arithmetic can drift a cent or two. Use computeBundleSaleCents(totalCents, discount) for mix-match or custom-quantity scenarios.
Apply merchant widget styling
import { applyWidgetConfigVars } from "@lime-bundles/core";
applyWidgetConfigVars(wrapperEl, bundle.widgetConfig);
// Sets every --lb-* CSS custom property the merchant configured in the
// admin. Your own stylesheet consumes them via var(--lb-*).A/B test resolution
import { getABTestAssignment, applyABVariantB } from "@lime-bundles/core";
const assignment = await getABTestAssignment(bundle.abTestId!, bundle.id);
const bundleForRender =
assignment?.variant === "B" ? applyABVariantB(bundle) : bundle;Consent-gated via Shopify's customerPrivacy framework or the setConsent(true) helper; without consent, everyone sees Variant A with no cookies written. Variant attribution flows through the analytics events the widget already emits — no separate persistence call is needed.
Analytics
import { reportImpression, reportAddToCart, observeImpression } from "@lime-bundles/core";
const cleanup = observeImpression(widgetEl, () => {
reportImpression(
{ shopDomain, appUrl },
{ bundleGid: bundle.id, bundleType: bundle.bundleType },
);
});Full export list
Data: createStorefrontClient, fetchBundlesForProduct, parseMetaobjectBundle, parseMetaobjectBundleStrict, BUNDLE_METAOBJECT_QUERY, BUNDLES_FOR_PRODUCT_QUERY, CART_CREATE_MUTATION, CART_LINES_ADD_MUTATION, SHOP_CUSTOM_CSS_QUERY.
Pricing: parseCents, formatCents, percentageDiscountUnit, computeFixedPricing, computeBundleSaleCents, calculateTierSavings, getActiveTier, validateQuantity, formatMoney, calculateDiscount.
Widget config: WIDGET_CONFIG_DEFAULTS, mergeWidgetConfig, flattenWidgetConfig, applyWidgetConfigVars, CSS_VAR_MAP, PX_KEYS, applyABVariantB.
A/B + consent: getABTestAssignment, setConsent, hasConsent.
Analytics: reportImpression, reportAddToCart, observeImpression.
CSS: injectCustomCss, sanitizeCustomCss.
Image + countdown: transformImageUrl, formatCountdown.
Types (all exported): ParsedBundle, FixedBundleData, VolumeBundleData, MixMatchBundleData, WidgetConfig + 8 sub-configs, VolumeTier, DiscountConfig, ABVariantOverrides, CartLineInput, Product, ProductVariant, BundleParseError, StorefrontApiError, PricingRow, FixedBundlePricing, ImageTransform.
Version policy
ParsedBundle, WidgetConfig, and CartLineInput are locked at v2.0.0. Narrowing via bundleType is safe; widening or changing field shapes is a breaking change. All three packages bump majors together.
License
MIT.
Support
Merchant support and bug reports: email via the Lime Bundles listing on the Shopify App Store.
