@cimplify/sdk 0.79.0 → 0.80.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.
Files changed (41) hide show
  1. package/dist/advanced.d.mts +1 -1
  2. package/dist/advanced.d.ts +1 -1
  3. package/dist/{chunk-FURKCWVG.mjs → chunk-FIC5S3KD.mjs} +1 -1
  4. package/dist/{chunk-T7ALPOK7.js → chunk-KMP4HVA2.js} +2 -2
  5. package/dist/{chunk-LBFTEXGD.mjs → chunk-NJGJCQWF.mjs} +9 -0
  6. package/dist/{chunk-5BPBDWAA.js → chunk-NV445GMS.js} +9 -0
  7. package/dist/{client-Byysg2IX.d.mts → client-6BNd11sq.d.mts} +1 -1
  8. package/dist/{client-BAzvXSLJ.d.ts → client-CuoWlAfH.d.ts} +1 -1
  9. package/dist/{client-D16qfHAB.d.mts → client-Czbt-mej.d.mts} +3 -0
  10. package/dist/{client-Cn5trGYV.d.ts → client-D22iSqke.d.ts} +3 -0
  11. package/dist/index.d.mts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +10 -10
  14. package/dist/index.mjs +1 -1
  15. package/dist/react.d.mts +33 -4
  16. package/dist/react.d.ts +33 -4
  17. package/dist/react.js +332 -51
  18. package/dist/react.mjs +312 -52
  19. package/dist/server.d.mts +2 -2
  20. package/dist/server.d.ts +2 -2
  21. package/dist/server.js +2 -2
  22. package/dist/server.mjs +1 -1
  23. package/dist/testing/suite.d.mts +2 -2
  24. package/dist/testing/suite.d.ts +2 -2
  25. package/dist/testing/suite.js +21 -21
  26. package/dist/testing/suite.mjs +2 -2
  27. package/dist/testing.d.mts +2 -2
  28. package/dist/testing.d.ts +2 -2
  29. package/dist/testing.js +77 -77
  30. package/dist/testing.mjs +3 -3
  31. package/package.json +7 -2
  32. package/registry/catalogue-page.json +1 -1
  33. package/registry/product-card.json +1 -1
  34. package/registry/product-grid.json +1 -1
  35. package/registry/product-image-gallery.json +1 -1
  36. package/registry/product-page.json +1 -1
  37. package/registry/recently-viewed.json +1 -1
  38. package/registry/recommendation-carousel.json +1 -1
  39. package/registry/search-input.json +1 -1
  40. package/dist/server-BXT62nBC.d.mts +0 -1186
  41. package/dist/server-BXT62nBC.d.ts +0 -1186
package/dist/react.mjs CHANGED
@@ -1,13 +1,14 @@
1
1
  "use client";
2
- import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, resolveGallery, RENDER_HINT } from './chunk-E6T5VJMG.mjs';
2
+ import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, resolveGallery, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-E6T5VJMG.mjs';
3
3
  export { getVariantDisplayName } from './chunk-E6T5VJMG.mjs';
4
- import { createCimplifyClient } from './chunk-LBFTEXGD.mjs';
4
+ import { createCimplifyClient } from './chunk-NJGJCQWF.mjs';
5
5
  import { ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-3BKMNVTR.mjs';
6
6
  import { parsePrice, formatMoney, formatPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-TD3AY34U.mjs';
7
7
  import { isSupportedCurrency } from './chunk-AMZXALF6.mjs';
8
8
  import './chunk-3G6RQLXK.mjs';
9
9
  import React14, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useId, useSyncExternalStore } from 'react';
10
10
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
11
+ import * as ReactDOM from 'react-dom';
11
12
  import { clsx } from 'clsx';
12
13
  import { twMerge } from 'tailwind-merge';
13
14
  import { format, parseISO, isValid } from 'date-fns';
@@ -1076,6 +1077,28 @@ function useCart(options = {}) {
1076
1077
  ]
1077
1078
  );
1078
1079
  }
1080
+ var reactPreconnect = typeof ReactDOM.preconnect === "function" ? ReactDOM.preconnect : void 0;
1081
+ var connected = /* @__PURE__ */ new Set();
1082
+ function preconnectOrigin(url, crossOrigin) {
1083
+ let origin;
1084
+ try {
1085
+ origin = new URL(url).origin;
1086
+ } catch {
1087
+ return;
1088
+ }
1089
+ if (reactPreconnect) {
1090
+ reactPreconnect(origin, crossOrigin ? { crossOrigin } : void 0);
1091
+ return;
1092
+ }
1093
+ if (typeof document === "undefined") return;
1094
+ if (origin === window.location.origin || connected.has(origin)) return;
1095
+ connected.add(origin);
1096
+ const link = document.createElement("link");
1097
+ link.rel = "preconnect";
1098
+ link.href = origin;
1099
+ if (crossOrigin) link.crossOrigin = crossOrigin;
1100
+ document.head.appendChild(link);
1101
+ }
1079
1102
  var LOCATION_STORAGE_KEY = "cimplify_location_id";
1080
1103
  var DISPLAY_CURRENCY_STORAGE_KEY = "cimplify_display_currency";
1081
1104
  var FX_REFRESH_INTERVAL = 12e4;
@@ -1141,6 +1164,11 @@ function resolveInitialLocation(locations) {
1141
1164
  }
1142
1165
  return locations[0];
1143
1166
  }
1167
+ var DEFAULT_PREFETCH_OPTIONS = {
1168
+ mode: "aggressive",
1169
+ rootMargin: "300px",
1170
+ images: true
1171
+ };
1144
1172
  var CimplifyClientContext = createContext(null);
1145
1173
  var CimplifyStateContext = createContext(null);
1146
1174
  function CimplifyProvider({
@@ -1149,9 +1177,19 @@ function CimplifyProvider({
1149
1177
  onLocationChange,
1150
1178
  initialBusiness,
1151
1179
  initialLocations,
1152
- optimistic = true
1180
+ optimistic = true,
1181
+ prefetch,
1182
+ linkComponent
1153
1183
  }) {
1154
1184
  const resolvedClient = useMemo(() => client ?? createDefaultClient(), [client]);
1185
+ const resolvedPrefetch = useMemo(
1186
+ () => ({
1187
+ mode: prefetch?.mode ?? DEFAULT_PREFETCH_OPTIONS.mode,
1188
+ rootMargin: prefetch?.rootMargin ?? DEFAULT_PREFETCH_OPTIONS.rootMargin,
1189
+ images: prefetch?.images ?? DEFAULT_PREFETCH_OPTIONS.images
1190
+ }),
1191
+ [prefetch?.mode, prefetch?.rootMargin, prefetch?.images]
1192
+ );
1155
1193
  const onLocationChangeRef = useRef(onLocationChange);
1156
1194
  const seeded = initialBusiness !== void 0;
1157
1195
  const initialBootstrapDone = useRef(seeded);
@@ -1218,6 +1256,12 @@ function CimplifyProvider({
1218
1256
  },
1219
1257
  [fxRate, displayCurrency, baseCurrency]
1220
1258
  );
1259
+ if (resolvedPrefetch.mode !== "off") {
1260
+ const apiCredentials = resolvedClient.getCredentialsMode() === "include" ? "use-credentials" : "anonymous";
1261
+ preconnectOrigin(resolvedClient.getBaseUrl(), apiCredentials);
1262
+ preconnectOrigin(resolvedClient.getLinkApiUrl(), apiCredentials);
1263
+ preconnectOrigin(DEFAULT_CDN_BASE_URL);
1264
+ }
1221
1265
  const setCurrentLocation = useCallback(
1222
1266
  (location) => {
1223
1267
  const previousLocationId = resolvedClient.getLocationId();
@@ -1291,7 +1335,9 @@ function CimplifyProvider({
1291
1335
  locations,
1292
1336
  isReady,
1293
1337
  baseCurrency,
1294
- optimistic
1338
+ optimistic,
1339
+ prefetch: resolvedPrefetch,
1340
+ linkComponent: linkComponent ?? null
1295
1341
  }),
1296
1342
  [
1297
1343
  resolvedClient,
@@ -1299,7 +1345,9 @@ function CimplifyProvider({
1299
1345
  baseCurrency,
1300
1346
  locations,
1301
1347
  isReady,
1302
- optimistic
1348
+ optimistic,
1349
+ resolvedPrefetch,
1350
+ linkComponent
1303
1351
  ]
1304
1352
  );
1305
1353
  const stateContextValue = useMemo(
@@ -4321,6 +4369,16 @@ function createCachedQuery(config) {
4321
4369
  }, [cacheKey2, load]);
4322
4370
  return { data, isLoading, error, refetch };
4323
4371
  }
4372
+ useCachedQuery.prefetch = (client, options) => {
4373
+ const key = config.key(client, options);
4374
+ if (cache.has(key) || inflight.has(key)) return void 0;
4375
+ const promise = config.fetch(client, options);
4376
+ inflight.set(key, promise);
4377
+ return promise.then((value) => {
4378
+ cache.set(key, { data: value, ts: Date.now() });
4379
+ }).catch(() => {
4380
+ }).finally(() => inflight.delete(key));
4381
+ };
4324
4382
  useCachedQuery.clear = () => {
4325
4383
  cache.clear();
4326
4384
  inflight.clear();
@@ -4377,6 +4435,9 @@ var productsQuery = createCachedQuery({
4377
4435
  };
4378
4436
  }
4379
4437
  });
4438
+ function prefetchProducts(client, options) {
4439
+ return productsQuery.prefetch(client, options);
4440
+ }
4380
4441
  function useProducts(options = {}) {
4381
4442
  const { data, isLoading, error, refetch } = productsQuery(options);
4382
4443
  return {
@@ -4401,14 +4462,27 @@ var productQuery = createCachedQuery({
4401
4462
  fetch: async (client, options) => {
4402
4463
  const result = isLikelySlug(options.slugOrId) ? await client.catalogue.getProductBySlug(options.slugOrId) : await client.catalogue.getProduct(options.slugOrId);
4403
4464
  if (!result.ok) throw result.error;
4404
- client.activity.trackProductView(result.value.id, {
4405
- productName: result.value.name,
4406
- categoryId: result.value.category_id,
4407
- price: String(result.value.default_price)
4408
- });
4409
4465
  return result.value;
4410
4466
  }
4411
4467
  });
4468
+ function prefetchProduct(client, slugOrId) {
4469
+ const normalized = (slugOrId || "").trim();
4470
+ if (!normalized) return void 0;
4471
+ return productQuery.prefetch(client, { slugOrId: normalized });
4472
+ }
4473
+ var VIEW_DEDUPE_WINDOW_MS = 3e5;
4474
+ var recentlyTracked = /* @__PURE__ */ new Map();
4475
+ function trackProductView(client, product) {
4476
+ const now = Date.now();
4477
+ const last = recentlyTracked.get(product.id);
4478
+ if (last !== void 0 && now - last < VIEW_DEDUPE_WINDOW_MS) return;
4479
+ recentlyTracked.set(product.id, now);
4480
+ client.activity.trackProductView(product.id, {
4481
+ productName: product.name,
4482
+ categoryId: product.category_id,
4483
+ price: String(product.default_price)
4484
+ });
4485
+ }
4412
4486
  function useProduct(slugOrId, options = {}) {
4413
4487
  const normalized = (slugOrId || "").trim();
4414
4488
  const { data, isLoading, error, refetch } = productQuery({
@@ -9621,6 +9695,39 @@ function ProductCustomizer({
9621
9695
  )
9622
9696
  ] });
9623
9697
  }
9698
+
9699
+ // src/react/utils/prefetch-policy.ts
9700
+ var SLOW_EFFECTIVE_TYPES = /* @__PURE__ */ new Set(["slow-2g", "2g"]);
9701
+ var MAX_CONCURRENT_SPECULATIVE = 4;
9702
+ var inFlight = 0;
9703
+ function connectionAllowsPrefetch() {
9704
+ if (typeof navigator === "undefined") return false;
9705
+ const connection = navigator.connection;
9706
+ if (!connection) return true;
9707
+ if (connection.saveData) return false;
9708
+ return !SLOW_EFFECTIVE_TYPES.has(connection.effectiveType ?? "");
9709
+ }
9710
+ function speculate(task) {
9711
+ if (!connectionAllowsPrefetch() || inFlight >= MAX_CONCURRENT_SPECULATIVE) return;
9712
+ const pending = task();
9713
+ if (!pending) return;
9714
+ inFlight++;
9715
+ pending.catch(() => {
9716
+ }).finally(() => {
9717
+ inFlight--;
9718
+ });
9719
+ }
9720
+
9721
+ // src/react/utils/preload-image.ts
9722
+ var preloaded = /* @__PURE__ */ new Set();
9723
+ function preloadImage(src) {
9724
+ if (!src || typeof window === "undefined") return;
9725
+ if (!connectionAllowsPrefetch() || preloaded.has(src)) return;
9726
+ preloaded.add(src);
9727
+ const image = new Image();
9728
+ image.decoding = "async";
9729
+ image.src = src;
9730
+ }
9624
9731
  var ASPECT_STYLES = {
9625
9732
  square: { aspectRatio: "1/1" },
9626
9733
  "4/3": { aspectRatio: "4/3" },
@@ -9653,6 +9760,8 @@ function ProductImageGallery({
9653
9760
  return null;
9654
9761
  }
9655
9762
  const activeImage = normalizedImages[selectedImage] || normalizedImages[0];
9763
+ preloadImage(normalizedImages[selectedImage + 1]);
9764
+ preloadImage(normalizedImages[selectedImage - 1]);
9656
9765
  return /* @__PURE__ */ jsxs("div", { "data-cimplify-image-gallery": true, className, children: [
9657
9766
  /* @__PURE__ */ jsx(
9658
9767
  "div",
@@ -9664,6 +9773,7 @@ function ProductImageGallery({
9664
9773
  {
9665
9774
  src: activeImage,
9666
9775
  alt: productName,
9776
+ fetchPriority: "high",
9667
9777
  style: {
9668
9778
  width: "100%",
9669
9779
  height: "100%",
@@ -9705,6 +9815,8 @@ function ProductImageGallery({
9705
9815
  {
9706
9816
  src: image,
9707
9817
  alt: "",
9818
+ loading: "lazy",
9819
+ decoding: "async",
9708
9820
  style: { width: "100%", height: "100%", objectFit: "cover" }
9709
9821
  }
9710
9822
  )
@@ -10693,6 +10805,63 @@ function ProductSheet({
10693
10805
  }
10694
10806
  );
10695
10807
  }
10808
+ var observers = /* @__PURE__ */ new Map();
10809
+ function observe(node, rootMargin, onEnter) {
10810
+ let shared = observers.get(rootMargin);
10811
+ if (!shared) {
10812
+ const targets = /* @__PURE__ */ new Map();
10813
+ const observer = new IntersectionObserver(
10814
+ (entries) => {
10815
+ for (const entry of entries) {
10816
+ if (!entry.isIntersecting) continue;
10817
+ const callback = targets.get(entry.target);
10818
+ if (callback) {
10819
+ unobserve(entry.target, rootMargin);
10820
+ callback();
10821
+ }
10822
+ }
10823
+ },
10824
+ { rootMargin }
10825
+ );
10826
+ shared = { observer, targets };
10827
+ observers.set(rootMargin, shared);
10828
+ }
10829
+ shared.targets.set(node, onEnter);
10830
+ shared.observer.observe(node);
10831
+ }
10832
+ function unobserve(node, rootMargin) {
10833
+ const shared = observers.get(rootMargin);
10834
+ if (!shared || !shared.targets.delete(node)) return;
10835
+ shared.observer.unobserve(node);
10836
+ if (shared.targets.size === 0) {
10837
+ shared.observer.disconnect();
10838
+ observers.delete(rootMargin);
10839
+ }
10840
+ }
10841
+ function useInViewport(onEnter, { rootMargin = "300px", disabled = false } = {}) {
10842
+ const onEnterRef = useRef(onEnter);
10843
+ onEnterRef.current = onEnter;
10844
+ const attachedRef = useRef(null);
10845
+ const firedRef = useRef(false);
10846
+ return useCallback(
10847
+ (node) => {
10848
+ if (attachedRef.current) {
10849
+ unobserve(attachedRef.current.node, attachedRef.current.rootMargin);
10850
+ attachedRef.current = null;
10851
+ }
10852
+ if (!node || disabled || firedRef.current) return;
10853
+ if (typeof IntersectionObserver === "undefined") return;
10854
+ attachedRef.current = { node, rootMargin };
10855
+ observe(node, rootMargin, () => {
10856
+ firedRef.current = true;
10857
+ attachedRef.current = null;
10858
+ onEnterRef.current();
10859
+ });
10860
+ },
10861
+ [rootMargin, disabled]
10862
+ );
10863
+ }
10864
+ var ImagePriorityContext = React14.createContext(false);
10696
10865
  function CardImage({
10697
10866
  src,
10698
10867
  alt,
@@ -10701,6 +10870,7 @@ function CardImage({
10701
10870
  className,
10702
10871
  children
10703
10872
  }) {
10873
+ const priority = React14.useContext(ImagePriorityContext);
10704
10874
  const aspectClass = {
10705
10875
  square: "aspect-square",
10706
10876
  "3/4": "aspect-[3/4]",
@@ -10708,11 +10878,14 @@ function CardImage({
10708
10878
  "16/9": "aspect-[16/9]"
10709
10879
  }[aspectRatio];
10710
10880
  return /* @__PURE__ */ jsxs("div", { "data-cimplify-card-image": true, className: cn("relative overflow-hidden bg-muted", aspectClass, className), children: [
10711
- renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : src ? /* @__PURE__ */ jsx(
10881
+ renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]", priority }) : src ? /* @__PURE__ */ jsx(
10712
10882
  "img",
10713
10883
  {
10714
10884
  src,
10715
10885
  alt,
10886
+ loading: priority ? "eager" : "lazy",
10887
+ decoding: priority ? "auto" : "async",
10888
+ fetchPriority: priority ? "high" : void 0,
10716
10889
  className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]"
10717
10890
  }
10718
10891
  ) : null,
@@ -11652,25 +11825,41 @@ function ProductCard({
11652
11825
  onQuickAdd,
11653
11826
  children,
11654
11827
  aspectRatio = "4/3",
11828
+ prefetch: prefetchProp,
11655
11829
  className,
11656
11830
  classNames
11657
11831
  }) {
11658
11832
  const mode = displayMode ?? product.display_mode ?? "card";
11659
11833
  const [isOpen, setIsOpen] = useState(false);
11660
- const [shouldFetch, setShouldFetch] = useState(false);
11661
- const { product: productDetails } = useProduct(
11662
- product.slug ?? product.id,
11663
- { enabled: shouldFetch || isOpen }
11664
- );
11665
- const handlePrefetch = useCallback(() => {
11666
- setShouldFetch(true);
11667
- }, []);
11668
- const handleOpenChange = useCallback((open) => {
11669
- setIsOpen(open);
11670
- if (open) {
11671
- setShouldFetch(true);
11834
+ const context = useOptionalCimplifyClient();
11835
+ const client = context?.client ?? null;
11836
+ const prefetchConfig = context?.prefetch ?? DEFAULT_PREFETCH_OPTIONS;
11837
+ const LinkComponent = context?.linkComponent ?? null;
11838
+ const prefetchMode = prefetchProp ?? prefetchConfig.mode;
11839
+ const imagePriority = useContext(ImagePriorityContext);
11840
+ const { product: productDetails } = useProduct(product.slug ?? product.id, {
11841
+ enabled: isOpen
11842
+ });
11843
+ const warm = useCallback(() => {
11844
+ if (!client || prefetchMode === "off") return;
11845
+ speculate(() => prefetchProduct(client, product.slug ?? product.id));
11846
+ if (prefetchConfig.images) {
11847
+ preloadImage(product.images?.[0] || product.image_url);
11672
11848
  }
11673
- }, []);
11849
+ }, [client, prefetchMode, prefetchConfig.images, product]);
11850
+ const viewportRef = useInViewport(warm, {
11851
+ rootMargin: prefetchConfig.rootMargin,
11852
+ disabled: prefetchMode !== "aggressive"
11853
+ });
11854
+ const handleOpenChange = useCallback(
11855
+ (open) => {
11856
+ setIsOpen(open);
11857
+ if (open && client) {
11858
+ trackProductView(client, product);
11859
+ }
11860
+ },
11861
+ [client, product]
11862
+ );
11674
11863
  const handleClose = useCallback(() => {
11675
11864
  setIsOpen(false);
11676
11865
  }, []);
@@ -11699,6 +11888,9 @@ function ProductCard({
11699
11888
  {
11700
11889
  src: imageUrl,
11701
11890
  alt: product.name,
11891
+ loading: imagePriority ? "eager" : "lazy",
11892
+ decoding: imagePriority ? "auto" : "async",
11893
+ fetchPriority: imagePriority ? "high" : void 0,
11702
11894
  className: cn("w-full h-full object-cover transition-transform duration-300 group-hover:scale-105", classNames?.image),
11703
11895
  "data-cimplify-product-card-image": true
11704
11896
  }
@@ -11716,12 +11908,33 @@ function ProductCard({
11716
11908
  const linkHref = href ?? `/menu/${product.slug}`;
11717
11909
  const linkClassName = cn("group block no-underline text-[inherit] rounded-xl border border-border bg-background transition-shadow duration-200 hover:shadow-md", className, classNames?.root);
11718
11910
  if (renderLink) {
11719
- return renderLink({ href: linkHref, className: linkClassName, children: cardBody });
11911
+ return renderLink({
11912
+ href: linkHref,
11913
+ className: linkClassName,
11914
+ children: cardBody,
11915
+ onPointerEnter: warm,
11916
+ onFocus: warm
11917
+ });
11918
+ }
11919
+ if (LinkComponent) {
11920
+ return /* @__PURE__ */ jsx(
11921
+ LinkComponent,
11922
+ {
11923
+ href: linkHref,
11924
+ className: linkClassName,
11925
+ onPointerEnter: warm,
11926
+ onFocus: warm,
11927
+ children: cardBody
11928
+ }
11929
+ );
11720
11930
  }
11721
11931
  return /* @__PURE__ */ jsx(
11722
11932
  "a",
11723
11933
  {
11724
11934
  href: linkHref,
11935
+ ref: viewportRef,
11936
+ onPointerEnter: warm,
11937
+ onFocus: warm,
11725
11938
  "data-cimplify-product-card": true,
11726
11939
  "data-display-mode": "page",
11727
11940
  className: linkClassName,
@@ -11733,7 +11946,9 @@ function ProductCard({
11733
11946
  /* @__PURE__ */ jsx(
11734
11947
  Dialog.Trigger,
11735
11948
  {
11736
- onPointerEnter: handlePrefetch,
11949
+ ref: viewportRef,
11950
+ onPointerEnter: warm,
11951
+ onFocus: warm,
11737
11952
  "data-cimplify-product-card": true,
11738
11953
  "data-display-mode": "card",
11739
11954
  className: cn(
@@ -11835,21 +12050,18 @@ function ProductGrid({
11835
12050
  id: gridId,
11836
12051
  "data-cimplify-product-grid": true,
11837
12052
  className: cn(className, classNames?.root),
11838
- children: products.map((product) => /* @__PURE__ */ jsx(
11839
- "div",
11840
- {
11841
- "data-cimplify-product-grid-item": true,
11842
- className: classNames?.item,
11843
- children: renderCard ? renderCard(product) : /* @__PURE__ */ jsx(
11844
- ProductCard,
11845
- {
11846
- product,
11847
- renderImage
11848
- }
11849
- )
11850
- },
11851
- product.id
11852
- ))
12053
+ children: products.map((product, index) => {
12054
+ const card = renderCard ? renderCard(product) : /* @__PURE__ */ jsx(ProductCard, { product, renderImage });
12055
+ return /* @__PURE__ */ jsx(
12056
+ "div",
12057
+ {
12058
+ "data-cimplify-product-grid-item": true,
12059
+ className: classNames?.item,
12060
+ children: index < xl ? /* @__PURE__ */ jsx(ImagePriorityContext.Provider, { value: true, children: card }) : card
12061
+ },
12062
+ product.id
12063
+ );
12064
+ })
11853
12065
  }
11854
12066
  )
11855
12067
  ] });
@@ -11890,6 +12102,15 @@ function SearchInput({
11890
12102
  [searchOptions, categoryId]
11891
12103
  );
11892
12104
  const { results, isLoading, query, setQuery, clear } = useSearch(mergedOptions);
12105
+ const context = useOptionalCimplifyClient();
12106
+ const warmResult = useCallback(
12107
+ (product) => {
12108
+ const client = mergedOptions.client ?? context?.client;
12109
+ if (!client || context?.prefetch.mode === "off") return;
12110
+ speculate(() => prefetchProduct(client, product.slug ?? product.id));
12111
+ },
12112
+ [mergedOptions.client, context]
12113
+ );
11893
12114
  const handleValueChange = useCallback(
11894
12115
  (value) => {
11895
12116
  setQuery(value);
@@ -11954,6 +12175,8 @@ function SearchInput({
11954
12175
  {
11955
12176
  type: "button",
11956
12177
  onClick: () => onResultClick?.(product),
12178
+ onPointerEnter: () => warmResult(product),
12179
+ onFocus: () => warmResult(product),
11957
12180
  "data-cimplify-search-result": true,
11958
12181
  className: classNames?.resultItem,
11959
12182
  style: !renderResult ? { display: "flex", alignItems: "center", gap: "0.5rem", width: "100%" } : void 0,
@@ -13663,18 +13886,24 @@ function CataloguePage({
13663
13886
  const [maxPrice, setMaxPrice] = useState("");
13664
13887
  const [page, setPage] = useState(1);
13665
13888
  const debouncedSearch = useDebounce(searchQuery, 300);
13889
+ const filterOptions = useMemo(
13890
+ () => ({
13891
+ category: selectedCategory ?? void 0,
13892
+ search: debouncedSearch.length >= 2 ? debouncedSearch : void 0,
13893
+ sort_by: sortBy,
13894
+ sort_order: sortOrder,
13895
+ in_stock: inStockOnly || void 0,
13896
+ tags: selectedTags.length > 0 ? selectedTags : void 0,
13897
+ min_price: minPrice ? parseFloat(minPrice) : void 0,
13898
+ max_price: maxPrice ? parseFloat(maxPrice) : void 0,
13899
+ limit: pageSize
13900
+ }),
13901
+ [selectedCategory, debouncedSearch, sortBy, sortOrder, inStockOnly, selectedTags, minPrice, maxPrice, pageSize]
13902
+ );
13666
13903
  const { products: fetchedProducts, isLoading, pagination } = useProducts({
13904
+ ...filterOptions,
13667
13905
  enabled: productsProp === void 0,
13668
- category: selectedCategory ?? void 0,
13669
- search: debouncedSearch.length >= 2 ? debouncedSearch : void 0,
13670
- sort_by: sortBy,
13671
- sort_order: sortOrder,
13672
- in_stock: inStockOnly || void 0,
13673
- tags: selectedTags.length > 0 ? selectedTags : void 0,
13674
- min_price: minPrice ? parseFloat(minPrice) : void 0,
13675
- max_price: maxPrice ? parseFloat(maxPrice) : void 0,
13676
- page,
13677
- limit: pageSize
13906
+ page
13678
13907
  });
13679
13908
  const { categories: fetchedCategories } = useCategories({
13680
13909
  enabled: categoriesProp === void 0
@@ -13682,6 +13911,10 @@ function CataloguePage({
13682
13911
  const products = productsProp ?? fetchedProducts;
13683
13912
  const categories = categoriesProp ?? fetchedCategories;
13684
13913
  const totalPages = pagination?.total_pages ?? 1;
13914
+ const context = useOptionalCimplifyClient();
13915
+ if (context && context.prefetch.mode !== "off" && productsProp === void 0 && !isLoading && page < totalPages) {
13916
+ speculate(() => prefetchProducts(context.client, { ...filterOptions, page: page + 1 }));
13917
+ }
13685
13918
  const handleCategoryChange = useCallback((id) => {
13686
13919
  setSelectedCategory(id);
13687
13920
  setPage(1);
@@ -14583,6 +14816,12 @@ function ProductPage({
14583
14816
  enabled: !productProp && resolvedId.length > 0
14584
14817
  });
14585
14818
  const product = productProp ?? fetched;
14819
+ const context = useOptionalCimplifyClient();
14820
+ useEffect(() => {
14821
+ if (product && context) {
14822
+ trackProductView(context.client, product);
14823
+ }
14824
+ }, [context, product]);
14586
14825
  if (isLoading && !product) {
14587
14826
  return /* @__PURE__ */ jsx(
14588
14827
  "div",
@@ -16007,6 +16246,15 @@ function RecommendationCarousel({
16007
16246
  enabled: recommendationsProp === void 0
16008
16247
  });
16009
16248
  const recommendations = recommendationsProp ?? fetched;
16249
+ const context = useOptionalCimplifyClient();
16250
+ const warmRecommendation = useCallback(
16251
+ (rec) => {
16252
+ const productId = rec.product?.id;
16253
+ if (!productId || !context || context.prefetch.mode === "off") return;
16254
+ speculate(() => prefetchProduct(context.client, productId));
16255
+ },
16256
+ [context]
16257
+ );
16010
16258
  if (isLoading && recommendations.length === 0) {
16011
16259
  return /* @__PURE__ */ jsx(
16012
16260
  "div",
@@ -16033,6 +16281,8 @@ function RecommendationCarousel({
16033
16281
  {
16034
16282
  type: "button",
16035
16283
  onClick: () => onProductClick?.(rec),
16284
+ onPointerEnter: () => warmRecommendation(rec),
16285
+ onFocus: () => warmRecommendation(rec),
16036
16286
  "data-cimplify-recommendation-item": true,
16037
16287
  className: classNames?.item,
16038
16288
  style: { flexShrink: 0 },
@@ -16111,6 +16361,14 @@ function RecentlyViewed({
16111
16361
  classNames
16112
16362
  }) {
16113
16363
  const { state, isLoading } = useActivityState();
16364
+ const context = useOptionalCimplifyClient();
16365
+ const warmViewed = useCallback(
16366
+ (productId) => {
16367
+ if (!context || context.prefetch.mode === "off") return;
16368
+ speculate(() => prefetchProduct(context.client, productId));
16369
+ },
16370
+ [context]
16371
+ );
16114
16372
  const rawViewed = state?.activity?.viewed_products ?? [];
16115
16373
  const viewed = limit ? rawViewed.slice(0, limit) : rawViewed;
16116
16374
  if (isLoading && viewed.length === 0) {
@@ -16131,6 +16389,8 @@ function RecentlyViewed({
16131
16389
  {
16132
16390
  type: "button",
16133
16391
  onClick: () => onProductClick?.(product),
16392
+ onPointerEnter: () => warmViewed(product.product_id),
16393
+ onFocus: () => warmViewed(product.product_id),
16134
16394
  "data-cimplify-recently-viewed-item": true,
16135
16395
  className: classNames?.item,
16136
16396
  children: renderProduct ? renderProduct(product) : /* @__PURE__ */ jsx("span", { children: product.product_name ?? product.product_id })
@@ -16771,4 +17031,4 @@ function SparkleIcon({ className }) {
16771
17031
  );
16772
17032
  }
16773
17033
 
16774
- export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, roomToResource, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
17034
+ export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, prefetchProduct, prefetchProducts, roomToResource, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
package/dist/server.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { ad as CimplifyClient } from './client-D16qfHAB.mjs';
2
- export { K as CacheOptions, cq as ReadRequestOptions, cM as Result } from './client-D16qfHAB.mjs';
1
+ import { ad as CimplifyClient } from './client-Czbt-mej.mjs';
2
+ export { K as CacheOptions, cq as ReadRequestOptions, cM as Result } from './client-Czbt-mej.mjs';
3
3
  export { Q as Category, T as CimplifyError, U as Collection, aX as Product, b9 as ProductWithDetails } from './product-BgFOFmQw.mjs';
4
4
  import { JWTVerifyResult } from 'jose';
5
5
  import './payment-B7qzWdaJ.mjs';
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ad as CimplifyClient } from './client-Cn5trGYV.js';
2
- export { K as CacheOptions, cq as ReadRequestOptions, cM as Result } from './client-Cn5trGYV.js';
1
+ import { ad as CimplifyClient } from './client-D22iSqke.js';
2
+ export { K as CacheOptions, cq as ReadRequestOptions, cM as Result } from './client-D22iSqke.js';
3
3
  export { Q as Category, T as CimplifyError, U as Collection, aX as Product, b9 as ProductWithDetails } from './product-BgFOFmQw.js';
4
4
  import { JWTVerifyResult } from 'jose';
5
5
  import './payment-mVIJIJcq.js';
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk5BPBDWAA_js = require('./chunk-5BPBDWAA.js');
3
+ var chunkNV445GMS_js = require('./chunk-NV445GMS.js');
4
4
  var chunkCV5QMDQS_js = require('./chunk-CV5QMDQS.js');
5
5
  require('./chunk-7Y2O3E4D.js');
6
6
  var chunkXA3ZNR75_js = require('./chunk-XA3ZNR75.js');
@@ -26,7 +26,7 @@ function readEnv(...keys) {
26
26
  var getServerClient = react.cache((opts = {}) => {
27
27
  const baseUrl = opts.apiUrl ?? readEnv("CIMPLIFY_API_URL", "NEXT_PUBLIC_CIMPLIFY_API_URL") ?? defaultBaseUrl();
28
28
  const publicKey = opts.secretKey ?? readEnv("CIMPLIFY_SECRET_KEY", "NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY") ?? "mock-dev";
29
- const client = chunk5BPBDWAA_js.createCimplifyClient({
29
+ const client = chunkNV445GMS_js.createCimplifyClient({
30
30
  baseUrl,
31
31
  publicKey,
32
32
  suppressPublicKeyWarning: true
package/dist/server.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createCimplifyClient } from './chunk-LBFTEXGD.mjs';
1
+ import { createCimplifyClient } from './chunk-NJGJCQWF.mjs';
2
2
  import { PKCE_COOKIE, decodePkceState, PKCE_COOKIE_PATH, DEFAULT_ISSUER } from './chunk-3BKMNVTR.mjs';
3
3
  import './chunk-TD3AY34U.mjs';
4
4
  export { CimplifyError } from './chunk-AMZXALF6.mjs';
@@ -1,8 +1,8 @@
1
1
  import { TestAPI } from 'vitest';
2
- import { T as TestClientHandle } from '../client-Byysg2IX.mjs';
2
+ import { T as TestClientHandle } from '../client-6BNd11sq.mjs';
3
3
  import { C as CreateAppOptions } from '../server-CGO4zneR.mjs';
4
4
  export { S as SeedName } from '../server-CGO4zneR.mjs';
5
- import '../client-D16qfHAB.mjs';
5
+ import '../client-Czbt-mej.mjs';
6
6
  import '../product-BgFOFmQw.mjs';
7
7
  import '../payment-B7qzWdaJ.mjs';
8
8
  import 'hono';
@@ -1,8 +1,8 @@
1
1
  import { TestAPI } from 'vitest';
2
- import { T as TestClientHandle } from '../client-BAzvXSLJ.js';
2
+ import { T as TestClientHandle } from '../client-CuoWlAfH.js';
3
3
  import { C as CreateAppOptions } from '../server-Daq42vK7.js';
4
4
  export { S as SeedName } from '../server-Daq42vK7.js';
5
- import '../client-Cn5trGYV.js';
5
+ import '../client-D22iSqke.js';
6
6
  import '../product-BgFOFmQw.js';
7
7
  import '../payment-mVIJIJcq.js';
8
8
  import 'hono';