@lime-bundles/react 4.0.0 → 5.0.0
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/README.md +43 -0
- package/dist/index.cjs +55 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -4
- package/dist/index.d.ts +56 -4
- package/dist/index.js +60 -21
- package/dist/index.js.map +1 -1
- package/docs/css-variables.md +3 -0
- package/docs/react-nextjs.md +14 -5
- package/docs/web-component.md +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -113,6 +113,49 @@ useEffect(() => {
|
|
|
113
113
|
|
|
114
114
|
Full CSS variable reference: [css-variables.md](./docs/css-variables.md).
|
|
115
115
|
|
|
116
|
+
## Markets & B2B
|
|
117
|
+
|
|
118
|
+
`<FixedBundle>`, `<VolumeBundle>`, `<MixMatchBundle>`, and `useBundleData` accept four optional props for Shopify Markets and B2B catalogs:
|
|
119
|
+
|
|
120
|
+
- **`country`** — ISO-3166 alpha-2 (e.g. `"US"`). Passed as Storefront `@inContext(country:)`. Drives Markets pricing and currency.
|
|
121
|
+
- **`language`** — ISO-639-1 (e.g. `"EN"`). Passed as `@inContext(language:)`.
|
|
122
|
+
- **`buyer`** — B2B buyer identity. `BuyerInput` object or `() => Promise<BuyerInput>` callback.
|
|
123
|
+
- **`marketId`** — Current visitor's Market GID. When the bundle is `marketVisibility: "specific"`, it's hidden unless this market is in its allow-list.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<FixedBundle
|
|
127
|
+
shopDomain="acme.myshopify.com"
|
|
128
|
+
storefrontAccessToken={token}
|
|
129
|
+
bundleGid={gid}
|
|
130
|
+
onAddToCart={addToCart}
|
|
131
|
+
country="US"
|
|
132
|
+
language="EN"
|
|
133
|
+
buyer={async () => fetchBuyerToken()} // resolved per request
|
|
134
|
+
marketId="gid://shopify/Market/1"
|
|
135
|
+
/>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Security — B2B `customerAccessToken`:**
|
|
139
|
+
|
|
140
|
+
- Prefer the callback form (`() => Promise<BuyerInput>`) so the token doesn't sit in static prop trees, React DevTools snapshots, or Sentry/LogRocket traces.
|
|
141
|
+
- Never pass via URL query parameters (leaks to Referer headers and server logs).
|
|
142
|
+
- Never store in localStorage shared with third-party scripts.
|
|
143
|
+
- The Customer Account API PKCE flow is the canonical source. See https://shopify.dev/docs/api/customer/latest.
|
|
144
|
+
|
|
145
|
+
**Caching:**
|
|
146
|
+
|
|
147
|
+
Buyer-contextual responses MUST be `Cache-Control: private` — never share across users. `@lime-bundles/core` exports `getCacheKey()` for SWR / TanStack Query consumers; it hashes the buyer token (never includes the raw value) so per-buyer caches don't leak tokens.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { getCacheKey } from "@lime-bundles/core";
|
|
151
|
+
useQuery({
|
|
152
|
+
queryKey: getCacheKey("Bundle", { id }, { shopDomain, country, buyer }),
|
|
153
|
+
queryFn: () => fetchBundleData({ ... }),
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Filtering vs throwing:** `fetchBundleData` throws `BundleParseError` when the bundle is hidden by the market gate. Use `fetchBundleDataWithWarnings` instead to receive `{ bundle, warnings }` and decide whether to render alternative content.
|
|
158
|
+
|
|
116
159
|
## Version policy
|
|
117
160
|
|
|
118
161
|
`BundleComponentProps`, `UseBundleDataResult`, and `UseBundlesForProductResult` are locked at v2.0.0. All three packages (`core`, `react`, `widget`) bump majors together.
|
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,6 @@ __export(index_exports, {
|
|
|
30
30
|
VariantDropdown: () => VariantDropdown,
|
|
31
31
|
VolumeBundle: () => VolumeBundle,
|
|
32
32
|
WIDGET_CONFIG_DEFAULTS: () => import_core13.WIDGET_CONFIG_DEFAULTS,
|
|
33
|
-
applyABVariantB: () => import_core13.applyABVariantB,
|
|
34
33
|
applyWidgetConfigVars: () => import_core13.applyWidgetConfigVars,
|
|
35
34
|
calculateDiscount: () => import_core13.calculateDiscount,
|
|
36
35
|
calculateTierSavings: () => import_core13.calculateTierSavings,
|
|
@@ -40,12 +39,13 @@ __export(index_exports, {
|
|
|
40
39
|
fetchBundleData: () => fetchBundleData,
|
|
41
40
|
fetchBundlesForProduct: () => import_core13.fetchBundlesForProduct,
|
|
42
41
|
fetchShopCustomCss: () => fetchShopCustomCss,
|
|
42
|
+
fnv1a: () => import_core13.fnv1a,
|
|
43
43
|
formatCents: () => import_core13.formatCents,
|
|
44
44
|
formatCountdown: () => import_core13.formatCountdown,
|
|
45
45
|
formatMoney: () => import_core13.formatMoney,
|
|
46
46
|
formatUnitPrice: () => import_core13.formatUnitPrice,
|
|
47
|
-
getABTestAssignment: () => import_core13.getABTestAssignment,
|
|
48
47
|
getActiveTier: () => import_core13.getActiveTier,
|
|
48
|
+
getLinkGroupAssignment: () => import_core13.getLinkGroupAssignment,
|
|
49
49
|
hasConsent: () => import_core13.hasConsent,
|
|
50
50
|
injectCustomCss: () => import_core13.injectCustomCss,
|
|
51
51
|
mergeWidgetConfig: () => import_core13.mergeWidgetConfig,
|
|
@@ -54,6 +54,7 @@ __export(index_exports, {
|
|
|
54
54
|
parseMetaobjectBundle: () => import_core13.parseMetaobjectBundle,
|
|
55
55
|
parseMetaobjectBundleStrict: () => import_core13.parseMetaobjectBundleStrict,
|
|
56
56
|
percentageDiscountUnit: () => import_core13.percentageDiscountUnit,
|
|
57
|
+
pickVariantFromWeights: () => import_core13.pickVariantFromWeights,
|
|
57
58
|
reportAddToCart: () => import_core13.reportAddToCart,
|
|
58
59
|
reportImpression: () => import_core13.reportImpression,
|
|
59
60
|
sanitizeCustomCss: () => import_core13.sanitizeCustomCss,
|
|
@@ -80,14 +81,29 @@ var import_core3 = require("@lime-bundles/core");
|
|
|
80
81
|
// src/fetchBundleData.ts
|
|
81
82
|
var import_core = require("@lime-bundles/core");
|
|
82
83
|
async function fetchBundleData(options) {
|
|
83
|
-
const
|
|
84
|
+
const result = await fetchBundleDataWithWarnings(options);
|
|
85
|
+
if (!result.bundle) {
|
|
86
|
+
throw new import_core.BundleParseError(
|
|
87
|
+
`Bundle hidden by market gate: ${options.bundleGid}`,
|
|
88
|
+
"not_found"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return result.bundle;
|
|
92
|
+
}
|
|
93
|
+
async function fetchBundleDataWithWarnings(options) {
|
|
94
|
+
const config = {
|
|
84
95
|
shopDomain: options.shopDomain,
|
|
85
96
|
accessToken: options.storefrontAccessToken,
|
|
86
97
|
buyerIp: options.buyerIp,
|
|
87
|
-
apiVersion: options.apiVersion
|
|
88
|
-
|
|
98
|
+
apiVersion: options.apiVersion,
|
|
99
|
+
country: options.country,
|
|
100
|
+
language: options.language,
|
|
101
|
+
buyer: options.buyer
|
|
102
|
+
};
|
|
103
|
+
const client = (0, import_core.createStorefrontClient)(config);
|
|
104
|
+
const query = (0, import_core.hasInContext)(config) ? (0, import_core.withInContext)(import_core.BUNDLE_METAOBJECT_QUERY) : import_core.BUNDLE_METAOBJECT_QUERY;
|
|
89
105
|
const data = await client.query(
|
|
90
|
-
|
|
106
|
+
query,
|
|
91
107
|
{ id: options.bundleGid },
|
|
92
108
|
{ signal: options.signal }
|
|
93
109
|
);
|
|
@@ -97,10 +113,24 @@ async function fetchBundleData(options) {
|
|
|
97
113
|
"not_found"
|
|
98
114
|
);
|
|
99
115
|
}
|
|
100
|
-
|
|
116
|
+
const bundle = (0, import_core.parseMetaobjectBundleStrict)(
|
|
101
117
|
data.metaobject.id,
|
|
102
118
|
data.metaobject.fields
|
|
103
119
|
);
|
|
120
|
+
if (bundle.marketVisibility === "specific" && (!options.marketId || !bundle.marketIds.includes(options.marketId))) {
|
|
121
|
+
return {
|
|
122
|
+
bundle: null,
|
|
123
|
+
warnings: [
|
|
124
|
+
{
|
|
125
|
+
bundleGid: options.bundleGid,
|
|
126
|
+
reason: "market_mismatch",
|
|
127
|
+
currentMarketId: options.marketId ?? null,
|
|
128
|
+
allowedMarketIds: bundle.marketIds
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return { bundle, warnings: [] };
|
|
104
134
|
}
|
|
105
135
|
|
|
106
136
|
// src/fetchShopCustomCss.ts
|
|
@@ -146,7 +176,11 @@ function useBundleData(options) {
|
|
|
146
176
|
shopDomain: options.shopDomain,
|
|
147
177
|
storefrontAccessToken: options.storefrontAccessToken,
|
|
148
178
|
bundleGid: options.bundleGid,
|
|
149
|
-
signal: controller.signal
|
|
179
|
+
signal: controller.signal,
|
|
180
|
+
country: options.country,
|
|
181
|
+
language: options.language,
|
|
182
|
+
buyer: options.buyer,
|
|
183
|
+
marketId: options.marketId
|
|
150
184
|
}).then((bundle) => {
|
|
151
185
|
if (controller.signal.aborted) return;
|
|
152
186
|
setState({ status: "success", bundle, error: null });
|
|
@@ -173,7 +207,11 @@ function useBundleData(options) {
|
|
|
173
207
|
}, [
|
|
174
208
|
options.shopDomain,
|
|
175
209
|
options.storefrontAccessToken,
|
|
176
|
-
options.bundleGid
|
|
210
|
+
options.bundleGid,
|
|
211
|
+
options.country,
|
|
212
|
+
options.language,
|
|
213
|
+
options.buyer,
|
|
214
|
+
options.marketId
|
|
177
215
|
]);
|
|
178
216
|
return state;
|
|
179
217
|
}
|
|
@@ -193,7 +231,7 @@ function useAnalytics(options) {
|
|
|
193
231
|
);
|
|
194
232
|
(0, import_react2.useEffect)(() => {
|
|
195
233
|
impressionFiredRef.current = false;
|
|
196
|
-
}, [options.bundleGid
|
|
234
|
+
}, [options.bundleGid]);
|
|
197
235
|
(0, import_react2.useEffect)(() => {
|
|
198
236
|
if (options.enabled === false) return;
|
|
199
237
|
if (impressionFiredRef.current) return;
|
|
@@ -204,8 +242,7 @@ function useAnalytics(options) {
|
|
|
204
242
|
(0, import_core4.reportImpression)(config, {
|
|
205
243
|
bundleGid: options.bundleGid,
|
|
206
244
|
bundleType: options.bundleType,
|
|
207
|
-
|
|
208
|
-
abVariant: options.abVariant
|
|
245
|
+
linkGroupId: options.linkGroupId
|
|
209
246
|
});
|
|
210
247
|
});
|
|
211
248
|
return cleanup;
|
|
@@ -215,8 +252,7 @@ function useAnalytics(options) {
|
|
|
215
252
|
options.enabled,
|
|
216
253
|
options.bundleGid,
|
|
217
254
|
options.bundleType,
|
|
218
|
-
options.
|
|
219
|
-
options.abVariant
|
|
255
|
+
options.linkGroupId
|
|
220
256
|
]);
|
|
221
257
|
const trackAddToCart = (0, import_react2.useCallback)(
|
|
222
258
|
(event) => {
|
|
@@ -227,8 +263,7 @@ function useAnalytics(options) {
|
|
|
227
263
|
productId: event.productId,
|
|
228
264
|
quantity: event.quantity,
|
|
229
265
|
totalPrice: event.totalPrice,
|
|
230
|
-
|
|
231
|
-
abVariant: options.abVariant
|
|
266
|
+
linkGroupId: options.linkGroupId
|
|
232
267
|
});
|
|
233
268
|
},
|
|
234
269
|
[
|
|
@@ -236,8 +271,7 @@ function useAnalytics(options) {
|
|
|
236
271
|
options.enabled,
|
|
237
272
|
options.bundleGid,
|
|
238
273
|
options.bundleType,
|
|
239
|
-
options.
|
|
240
|
-
options.abVariant
|
|
274
|
+
options.linkGroupId
|
|
241
275
|
]
|
|
242
276
|
);
|
|
243
277
|
return { elementRef, trackAddToCart };
|
|
@@ -1515,7 +1549,6 @@ var import_core13 = require("@lime-bundles/core");
|
|
|
1515
1549
|
VariantDropdown,
|
|
1516
1550
|
VolumeBundle,
|
|
1517
1551
|
WIDGET_CONFIG_DEFAULTS,
|
|
1518
|
-
applyABVariantB,
|
|
1519
1552
|
applyWidgetConfigVars,
|
|
1520
1553
|
calculateDiscount,
|
|
1521
1554
|
calculateTierSavings,
|
|
@@ -1525,12 +1558,13 @@ var import_core13 = require("@lime-bundles/core");
|
|
|
1525
1558
|
fetchBundleData,
|
|
1526
1559
|
fetchBundlesForProduct,
|
|
1527
1560
|
fetchShopCustomCss,
|
|
1561
|
+
fnv1a,
|
|
1528
1562
|
formatCents,
|
|
1529
1563
|
formatCountdown,
|
|
1530
1564
|
formatMoney,
|
|
1531
1565
|
formatUnitPrice,
|
|
1532
|
-
getABTestAssignment,
|
|
1533
1566
|
getActiveTier,
|
|
1567
|
+
getLinkGroupAssignment,
|
|
1534
1568
|
hasConsent,
|
|
1535
1569
|
injectCustomCss,
|
|
1536
1570
|
mergeWidgetConfig,
|
|
@@ -1539,6 +1573,7 @@ var import_core13 = require("@lime-bundles/core");
|
|
|
1539
1573
|
parseMetaobjectBundle,
|
|
1540
1574
|
parseMetaobjectBundleStrict,
|
|
1541
1575
|
percentageDiscountUnit,
|
|
1576
|
+
pickVariantFromWeights,
|
|
1542
1577
|
reportAddToCart,
|
|
1543
1578
|
reportImpression,
|
|
1544
1579
|
sanitizeCustomCss,
|