@tapcart/mobile-components 0.12.14 → 0.12.16

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.
@@ -0,0 +1,28 @@
1
+ import type { SWRConfiguration } from "swr";
2
+ export declare const RETRY_COUNT = 3;
3
+ export declare const BASE_INTERVAL_MS = 250;
4
+ export declare const MAX_RETRY_DELAY_MS = 4000;
5
+ export declare const isAbortError: (err: unknown) => boolean;
6
+ export declare const shouldRetryOnError: (err: unknown) => boolean;
7
+ type OnErrorRetry = NonNullable<SWRConfiguration["onErrorRetry"]>;
8
+ type OnSuccess = NonNullable<SWRConfiguration["onSuccess"]>;
9
+ export type SwrRetryConfig = {
10
+ errorRetryCount: number;
11
+ shouldRetryOnError: SWRConfiguration["shouldRetryOnError"];
12
+ onErrorRetry: OnErrorRetry;
13
+ onSuccess: OnSuccess;
14
+ isRetrying: boolean;
15
+ };
16
+ /**
17
+ * Shared retry config for SWR-backed hooks consuming the search-client.
18
+ *
19
+ * Custom `onErrorRetry` replaces SWR's built-in scheduler entirely, so the cap
20
+ * guard at the top is required — without it retries are unbounded.
21
+ *
22
+ * `isRetrying` is exposed so the hook can derive a loading state that covers
23
+ * the inter-attempt sleep window (where SWR's `isValidating` and `isLoading`
24
+ * are both false).
25
+ */
26
+ export declare const useSwrRetryConfig: () => SwrRetryConfig;
27
+ export {};
28
+ //# sourceMappingURL=swr-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swr-retry.d.ts","sourceRoot":"","sources":["../../../components/hooks/swr-retry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAE3C,eAAO,MAAM,WAAW,IAAI,CAAA;AAC5B,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,kBAAkB,OAAO,CAAA;AAOtC,eAAO,MAAM,YAAY,QAAS,OAAO,KAAG,OAG3C,CAAA;AAED,eAAO,MAAM,kBAAkB,QAAS,OAAO,KAAG,OAA6B,CAAA;AAiC/E,KAAK,YAAY,GAAG,WAAW,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAA;AACjE,KAAK,SAAS,GAAG,WAAW,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAA;AAE3D,MAAM,MAAM,cAAc,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAA;IACvB,kBAAkB,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;IAC1D,YAAY,EAAE,YAAY,CAAA;IAC1B,SAAS,EAAE,SAAS,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAAO,cAkCpC,CAAA"}
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { useCallback, useState } from "react";
3
+ export const RETRY_COUNT = 3;
4
+ export const BASE_INTERVAL_MS = 250;
5
+ export const MAX_RETRY_DELAY_MS = 4000;
6
+ export const isAbortError = (err) => {
7
+ if (!err || typeof err !== "object")
8
+ return false;
9
+ return err.name === "AbortError";
10
+ };
11
+ export const shouldRetryOnError = (err) => !isAbortError(err);
12
+ const parseRetryAfter = (value) => {
13
+ if (value === null || value === undefined)
14
+ return null;
15
+ const seconds = typeof value === "number" ? value : Number(value);
16
+ if (Number.isFinite(seconds) && seconds >= 0) {
17
+ return Math.round(seconds * 1000);
18
+ }
19
+ if (typeof value === "string") {
20
+ const dateMs = Date.parse(value);
21
+ if (Number.isFinite(dateMs)) {
22
+ return Math.max(0, dateMs - Date.now());
23
+ }
24
+ }
25
+ return null;
26
+ };
27
+ const getRetryAfterMs = (err) => {
28
+ if (!err || typeof err !== "object")
29
+ return null;
30
+ const e = err;
31
+ if (e.status !== 429)
32
+ return null;
33
+ return parseRetryAfter(e.retryAfter);
34
+ };
35
+ const computeBackoffMs = (retryCount) => {
36
+ const exponent = Math.min(Math.max(retryCount - 1, 0), 3);
37
+ const base = (1 << exponent) * BASE_INTERVAL_MS;
38
+ const jitter = 0.7 + Math.random() * 0.6;
39
+ return Math.min(Math.floor(base * jitter), MAX_RETRY_DELAY_MS);
40
+ };
41
+ /**
42
+ * Shared retry config for SWR-backed hooks consuming the search-client.
43
+ *
44
+ * Custom `onErrorRetry` replaces SWR's built-in scheduler entirely, so the cap
45
+ * guard at the top is required — without it retries are unbounded.
46
+ *
47
+ * `isRetrying` is exposed so the hook can derive a loading state that covers
48
+ * the inter-attempt sleep window (where SWR's `isValidating` and `isLoading`
49
+ * are both false).
50
+ */
51
+ export const useSwrRetryConfig = () => {
52
+ const [retryCount, setRetryCount] = useState(0);
53
+ const onErrorRetry = useCallback((err, _key, _config, revalidate, opts) => {
54
+ var _a;
55
+ const currentRetryCount = (_a = opts === null || opts === void 0 ? void 0 : opts.retryCount) !== null && _a !== void 0 ? _a : 0;
56
+ if (currentRetryCount > RETRY_COUNT) {
57
+ setRetryCount(0);
58
+ return;
59
+ }
60
+ setRetryCount(currentRetryCount);
61
+ const retryAfterMs = getRetryAfterMs(err);
62
+ const waitMs = retryAfterMs !== null
63
+ ? retryAfterMs
64
+ : computeBackoffMs(currentRetryCount);
65
+ setTimeout(() => revalidate(opts), waitMs);
66
+ }, []);
67
+ const onSuccess = useCallback(() => {
68
+ setRetryCount(0);
69
+ }, []);
70
+ const isRetrying = retryCount > 0 && retryCount <= RETRY_COUNT;
71
+ return {
72
+ errorRetryCount: RETRY_COUNT,
73
+ shouldRetryOnError,
74
+ onErrorRetry,
75
+ onSuccess,
76
+ isRetrying,
77
+ };
78
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=swr-retry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swr-retry.test.d.ts","sourceRoot":"","sources":["../../../components/hooks/swr-retry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,240 @@
1
+ import { act, renderHook } from "@testing-library/react";
2
+ import { isAbortError, shouldRetryOnError, useSwrRetryConfig, RETRY_COUNT, BASE_INTERVAL_MS, MAX_RETRY_DELAY_MS, } from "./swr-retry";
3
+ describe("swr-retry helpers", () => {
4
+ describe("isAbortError", () => {
5
+ it("returns true for a DOMException named AbortError", () => {
6
+ const err = new DOMException("aborted", "AbortError");
7
+ expect(isAbortError(err)).toBe(true);
8
+ });
9
+ it("returns true for a plain Error named AbortError", () => {
10
+ const err = Object.assign(new Error("aborted"), { name: "AbortError" });
11
+ expect(isAbortError(err)).toBe(true);
12
+ });
13
+ it("returns true for a plain object with name AbortError", () => {
14
+ expect(isAbortError({ name: "AbortError" })).toBe(true);
15
+ });
16
+ it("returns false for a generic Error", () => {
17
+ expect(isAbortError(new Error("boom"))).toBe(false);
18
+ });
19
+ it("returns false for null / undefined / primitives", () => {
20
+ expect(isAbortError(null)).toBe(false);
21
+ expect(isAbortError(undefined)).toBe(false);
22
+ expect(isAbortError("AbortError")).toBe(false);
23
+ expect(isAbortError(42)).toBe(false);
24
+ });
25
+ });
26
+ describe("shouldRetryOnError", () => {
27
+ it("returns false for AbortError", () => {
28
+ expect(shouldRetryOnError(new DOMException("x", "AbortError"))).toBe(false);
29
+ });
30
+ it("returns true for any other error", () => {
31
+ expect(shouldRetryOnError(new Error("boom"))).toBe(true);
32
+ expect(shouldRetryOnError({ status: 500 })).toBe(true);
33
+ });
34
+ });
35
+ });
36
+ describe("useSwrRetryConfig", () => {
37
+ beforeEach(() => {
38
+ jest.useFakeTimers();
39
+ });
40
+ afterEach(() => {
41
+ jest.useRealTimers();
42
+ });
43
+ const invoke = (onErrorRetry, err, retryCount, revalidate = jest.fn()) => {
44
+ onErrorRetry(err, "key", {}, revalidate, {
45
+ retryCount,
46
+ dedupe: true,
47
+ });
48
+ return revalidate;
49
+ };
50
+ it("exposes the retry count constant", () => {
51
+ const { result } = renderHook(() => useSwrRetryConfig());
52
+ expect(result.current.errorRetryCount).toBe(RETRY_COUNT);
53
+ });
54
+ it("starts with isRetrying = false", () => {
55
+ const { result } = renderHook(() => useSwrRetryConfig());
56
+ expect(result.current.isRetrying).toBe(false);
57
+ });
58
+ it("sets isRetrying = true after onErrorRetry schedules a retry", () => {
59
+ const { result } = renderHook(() => useSwrRetryConfig());
60
+ const revalidate = jest.fn();
61
+ act(() => {
62
+ invoke(result.current.onErrorRetry, new Error("boom"), 1, revalidate);
63
+ });
64
+ expect(result.current.isRetrying).toBe(true);
65
+ });
66
+ it("schedules revalidate with the full opts object (preserves dedupe)", () => {
67
+ const { result } = renderHook(() => useSwrRetryConfig());
68
+ const revalidate = jest.fn();
69
+ act(() => {
70
+ invoke(result.current.onErrorRetry, new Error("boom"), 1, revalidate);
71
+ });
72
+ act(() => {
73
+ jest.advanceTimersByTime(MAX_RETRY_DELAY_MS + 100);
74
+ });
75
+ expect(revalidate).toHaveBeenCalledTimes(1);
76
+ expect(revalidate).toHaveBeenCalledWith(expect.objectContaining({ retryCount: 1, dedupe: true }));
77
+ });
78
+ it("schedules with a delay within the jittered range for attempt 1", () => {
79
+ const { result } = renderHook(() => useSwrRetryConfig());
80
+ const revalidate = jest.fn();
81
+ act(() => {
82
+ invoke(result.current.onErrorRetry, new Error("boom"), 1, revalidate);
83
+ });
84
+ // attempt 1: base = 1 * BASE_INTERVAL_MS, jitter 0.7-1.3 → 175-325ms
85
+ act(() => {
86
+ jest.advanceTimersByTime(BASE_INTERVAL_MS * 1.3 + 1);
87
+ });
88
+ expect(revalidate).toHaveBeenCalledTimes(1);
89
+ });
90
+ it("caps the delay at MAX_RETRY_DELAY_MS for high retry counts", () => {
91
+ const { result } = renderHook(() => useSwrRetryConfig());
92
+ const revalidate = jest.fn();
93
+ act(() => {
94
+ // retryCount 3 → exponent 2 → base = 4 * 250 = 1000ms (worst case 1300ms)
95
+ // Cap is 4000ms; verify revalidate fires before exceeding the cap.
96
+ invoke(result.current.onErrorRetry, new Error("boom"), 3, revalidate);
97
+ });
98
+ act(() => {
99
+ jest.advanceTimersByTime(MAX_RETRY_DELAY_MS + 1);
100
+ });
101
+ expect(revalidate).toHaveBeenCalledTimes(1);
102
+ });
103
+ it("enforces the cap guard: opts.retryCount > RETRY_COUNT skips scheduling", () => {
104
+ const { result } = renderHook(() => useSwrRetryConfig());
105
+ const revalidate = jest.fn();
106
+ act(() => {
107
+ invoke(result.current.onErrorRetry, new Error("boom"), RETRY_COUNT + 1, revalidate);
108
+ });
109
+ act(() => {
110
+ jest.advanceTimersByTime(MAX_RETRY_DELAY_MS * 2);
111
+ });
112
+ expect(revalidate).not.toHaveBeenCalled();
113
+ expect(result.current.isRetrying).toBe(false);
114
+ });
115
+ it("resets isRetrying = false when retries exhaust", () => {
116
+ const { result } = renderHook(() => useSwrRetryConfig());
117
+ const revalidate = jest.fn();
118
+ // Schedule attempt 1, 2, 3 — each before the next is scheduled
119
+ for (let i = 1; i <= RETRY_COUNT; i++) {
120
+ act(() => {
121
+ invoke(result.current.onErrorRetry, new Error("boom"), i, revalidate);
122
+ });
123
+ }
124
+ expect(result.current.isRetrying).toBe(true);
125
+ // Cap hit: opts.retryCount === RETRY_COUNT + 1 → callback resets state
126
+ act(() => {
127
+ invoke(result.current.onErrorRetry, new Error("boom"), RETRY_COUNT + 1, revalidate);
128
+ });
129
+ expect(result.current.isRetrying).toBe(false);
130
+ });
131
+ it("resets isRetrying = false when onSuccess fires after retries", () => {
132
+ const { result } = renderHook(() => useSwrRetryConfig());
133
+ const revalidate = jest.fn();
134
+ act(() => {
135
+ invoke(result.current.onErrorRetry, new Error("boom"), 1, revalidate);
136
+ });
137
+ expect(result.current.isRetrying).toBe(true);
138
+ act(() => {
139
+ result.current.onSuccess({}, "key", {});
140
+ });
141
+ expect(result.current.isRetrying).toBe(false);
142
+ });
143
+ describe("Retry-After honoring", () => {
144
+ it("uses Retry-After in seconds for 429 errors", () => {
145
+ const { result } = renderHook(() => useSwrRetryConfig());
146
+ const revalidate = jest.fn();
147
+ const err = Object.assign(new Error("rate limit"), {
148
+ status: 429,
149
+ retryAfter: "2",
150
+ });
151
+ act(() => {
152
+ invoke(result.current.onErrorRetry, err, 1, revalidate);
153
+ });
154
+ // Should NOT fire after backoff window (would have been ~175-325ms)
155
+ act(() => {
156
+ jest.advanceTimersByTime(1000);
157
+ });
158
+ expect(revalidate).not.toHaveBeenCalled();
159
+ // Should fire after the 2s Retry-After window
160
+ act(() => {
161
+ jest.advanceTimersByTime(1100);
162
+ });
163
+ expect(revalidate).toHaveBeenCalledTimes(1);
164
+ });
165
+ it("uses Retry-After as numeric seconds for 429 errors", () => {
166
+ const { result } = renderHook(() => useSwrRetryConfig());
167
+ const revalidate = jest.fn();
168
+ const err = Object.assign(new Error("rate limit"), {
169
+ status: 429,
170
+ retryAfter: 3,
171
+ });
172
+ act(() => {
173
+ invoke(result.current.onErrorRetry, err, 1, revalidate);
174
+ });
175
+ act(() => {
176
+ jest.advanceTimersByTime(2999);
177
+ });
178
+ expect(revalidate).not.toHaveBeenCalled();
179
+ act(() => {
180
+ jest.advanceTimersByTime(2);
181
+ });
182
+ expect(revalidate).toHaveBeenCalledTimes(1);
183
+ });
184
+ it("uses Retry-After as HTTP-date for 429 errors", () => {
185
+ // Anchor system time so toUTCString rounding + Date.parse precision are
186
+ // deterministic. Otherwise the HTTP-date round-trip can lose ~999ms of
187
+ // precision depending on jest fake-timer mode.
188
+ jest.setSystemTime(new Date("2026-01-01T00:00:00.000Z"));
189
+ const { result } = renderHook(() => useSwrRetryConfig());
190
+ const revalidate = jest.fn();
191
+ const futureDate = new Date(Date.now() + 5000).toUTCString();
192
+ const err = Object.assign(new Error("rate limit"), {
193
+ status: 429,
194
+ retryAfter: futureDate,
195
+ });
196
+ act(() => {
197
+ invoke(result.current.onErrorRetry, err, 1, revalidate);
198
+ });
199
+ act(() => {
200
+ jest.advanceTimersByTime(4500);
201
+ });
202
+ expect(revalidate).not.toHaveBeenCalled();
203
+ act(() => {
204
+ jest.advanceTimersByTime(1000);
205
+ });
206
+ expect(revalidate).toHaveBeenCalledTimes(1);
207
+ });
208
+ it("ignores Retry-After for non-429 errors", () => {
209
+ const { result } = renderHook(() => useSwrRetryConfig());
210
+ const revalidate = jest.fn();
211
+ const err = Object.assign(new Error("server error"), {
212
+ status: 500,
213
+ retryAfter: "10",
214
+ });
215
+ act(() => {
216
+ invoke(result.current.onErrorRetry, err, 1, revalidate);
217
+ });
218
+ // Should fire via normal backoff (~175-325ms), well before 10s.
219
+ act(() => {
220
+ jest.advanceTimersByTime(500);
221
+ });
222
+ expect(revalidate).toHaveBeenCalledTimes(1);
223
+ });
224
+ it("falls back to backoff when Retry-After is unparseable", () => {
225
+ const { result } = renderHook(() => useSwrRetryConfig());
226
+ const revalidate = jest.fn();
227
+ const err = Object.assign(new Error("rate limit"), {
228
+ status: 429,
229
+ retryAfter: "not-a-date",
230
+ });
231
+ act(() => {
232
+ invoke(result.current.onErrorRetry, err, 1, revalidate);
233
+ });
234
+ act(() => {
235
+ jest.advanceTimersByTime(MAX_RETRY_DELAY_MS + 100);
236
+ });
237
+ expect(revalidate).toHaveBeenCalledTimes(1);
238
+ });
239
+ });
240
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"use-infinite-scroll.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAmB,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,UAAU,QAAQ;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,GAAG,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAGxC,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGpC,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,GAAG,GAAG,IAAI,EAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,CAAA;IACR,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,oBAAoB,EAAE,GAAG,EAAE,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,sCAAsC,iBACnC,uBAAuB,OAsBtC,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,uBAqS3B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"use-infinite-scroll.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAmB,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAG5D,UAAU,QAAQ;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,GAAG,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAGxC,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGpC,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,GAAG,GAAG,IAAI,EAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,CAAA;IACR,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,oBAAoB,EAAE,GAAG,EAAE,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,sCAAsC,iBACnC,uBAAuB,OAsBtC,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,uBAoT3B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAA"}
@@ -13,6 +13,7 @@ import useSWRInfinite from "swr/infinite";
13
13
  import { useInView } from "react-intersection-observer";
14
14
  import { useSearchParams } from "next/navigation";
15
15
  import { throttle } from "lodash";
16
+ import { useSwrRetryConfig } from "./swr-retry";
16
17
  export const formatSearchParamsAsNextQueryVariables = (searchParams) => {
17
18
  const formattedParams = {};
18
19
  if (!searchParams)
@@ -147,11 +148,21 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
147
148
  });
148
149
  // Check if caller provided pre-fetched products to display while SWR fetches
149
150
  const hasInitialProducts = Boolean((_a = initialData === null || initialData === void 0 ? void 0 : initialData.products) === null || _a === void 0 ? void 0 : _a.length);
150
- const { data, error, size, setSize, isLoading, isValidating, mutate, } = useSWRInfinite(getKey, fetcher, {
151
+ const retryConfig = useSwrRetryConfig();
152
+ const { data, error, size, setSize, isLoading: rawIsLoading, isValidating, mutate, } = useSWRInfinite(getKey, fetcher, {
151
153
  revalidateFirstPage: false,
152
154
  initialSize: 1,
153
155
  keepPreviousData: true,
156
+ errorRetryCount: retryConfig.errorRetryCount,
157
+ shouldRetryOnError: retryConfig.shouldRetryOnError,
158
+ onErrorRetry: retryConfig.onErrorRetry,
159
+ onSuccess: retryConfig.onSuccess,
154
160
  });
161
+ // Keep the loader visible across the retry window. SWR commits
162
+ // { isValidating: false, isLoading: false } immediately on each failure, so
163
+ // a naive isValidating-based derivation would flash "not loading" during the
164
+ // inter-attempt sleep. `isRetrying` from useSwrRetryConfig closes that gap.
165
+ const isLoading = rawIsLoading || retryConfig.isRetrying;
155
166
  // Detect when params change and force cache invalidation
156
167
  useEffect(() => {
157
168
  const prevParams = prevParamsRef.current;
@@ -175,8 +186,10 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
175
186
  isFirstRender.current = false;
176
187
  }, [currentCollectionId, currentCollectionHandle, currentSearchQuery, mutate]);
177
188
  // When we have pre-fetched products, don't report as "loading initial data"
178
- // so the grid renders immediately with those products
179
- const isLoadingInitialData = !data && !error && !hasInitialProducts;
189
+ // so the grid renders immediately with those products.
190
+ // During the retry window (after a failure, before exhaustion) we still
191
+ // want to show as loading-initial-data so blocks keep the loader visible.
192
+ const isLoadingInitialData = !data && !hasInitialProducts && (retryConfig.isRetrying || !error);
180
193
  const isLoadingMore = isLoadingInitialData ||
181
194
  (size > 0 && data && typeof data[size - 1] === "undefined");
182
195
  const isEmpty = data
@@ -1 +1 @@
1
- {"version":3,"file":"use-order-details.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-order-details.ts"],"names":[],"mappings":"AAKA,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAClC,CAAA;AA0LD,eAAO,MAAM,qBAAqB,UACzB,OAAO,MAAM,EAAE,GAAG,CAAC;kBACT,OAAO,MAAM,EAAE,GAAG,CAAC;kBAAgB,OAAO,MAAM,EAAE,GAAG,CAAC;CA+NxE,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,MAAM,EACN,KAAK,EACL,QAAQ,EACR,OAAO,EACP,IAAY,GACb,EAAE,oBAAoB,GAAG,iBAAiB,CA8D1C"}
1
+ {"version":3,"file":"use-order-details.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-order-details.ts"],"names":[],"mappings":"AAKA,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAClC,CAAA;AA0LD,eAAO,MAAM,qBAAqB,UACzB,OAAO,MAAM,EAAE,GAAG,CAAC;kBACT,OAAO,MAAM,EAAE,GAAG,CAAC;kBAAgB,OAAO,MAAM,EAAE,GAAG,CAAC;CA+NxE,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,MAAM,EACN,KAAK,EACL,QAAQ,EACR,OAAO,EACP,IAAY,GACb,EAAE,oBAAoB,GAAG,iBAAiB,CAiE1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../components/ui/chip.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAsC,MAAM,OAAO,CAAA;AAI1D,QAAA,MAAM,YAAY;;;;mFAwBjB,CAAA;AAED,KAAK,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GACpD,YAAY,CAAC,OAAO,YAAY,CAAC,GAAG;IAClC,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAEH,QAAA,MAAM,IAAI;;;;;;;;;;yBALc,IAAI;yBACJ,IAAI;;wCAgF3B,CAAA;AAGD,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAA;IACzC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;CAC9C,CAAA;AAED,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAoD/C,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../components/ui/chip.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAsC,MAAM,OAAO,CAAA;AAI1D,QAAA,MAAM,YAAY;;;;mFAwBjB,CAAA;AAED,KAAK,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GACpD,YAAY,CAAC,OAAO,YAAY,CAAC,GAAG;IAClC,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAEH,QAAA,MAAM,IAAI;;;;;;;;;;yBALc,IAAI;yBACJ,IAAI;;wCAgF3B,CAAA;AAGD,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAA;IACzC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;CAC9C,CAAA;AAED,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAqD/C,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,kBAAkB,EAAE,CAAA"}
@@ -60,20 +60,22 @@ Chip.displayName = "Chip";
60
60
  const MultipleChips = ({ children, containerRef, style, }) => {
61
61
  const [showFadeLeft, setShowFadeLeft] = useState(false);
62
62
  const [showFadeRight, setShowFadeRight] = useState(false);
63
- const checkOverflow = () => {
64
- const { scrollLeft, scrollWidth, clientWidth } = containerRef.current;
63
+ const checkOverflow = React.useCallback(() => {
64
+ const el = containerRef.current;
65
+ if (!el)
66
+ return;
67
+ const { scrollLeft, scrollWidth, clientWidth } = el;
65
68
  setShowFadeLeft(scrollLeft > 0);
66
69
  setShowFadeRight(scrollLeft + clientWidth < scrollWidth);
67
- };
70
+ }, [containerRef]);
68
71
  useEffect(() => {
69
- const handleResize = () => checkOverflow();
70
- window.addEventListener("resize", handleResize);
71
- checkOverflow();
72
- return () => window.removeEventListener("resize", handleResize);
72
+ const el = containerRef.current;
73
+ if (!el)
74
+ return;
75
+ const ro = new ResizeObserver(() => checkOverflow());
76
+ ro.observe(el);
77
+ return () => ro.disconnect();
73
78
  }, [checkOverflow]);
74
- useEffect(() => {
75
- checkOverflow();
76
- }, [children, checkOverflow]);
77
79
  return (_jsxs("div", Object.assign({ className: "relative no-scrollbar" }, { children: [_jsx("div", Object.assign({ ref: containerRef, className: "flex overflow-x-auto overflow-y-hidden", onScroll: checkOverflow, style: style }, { children: children.map((chip, index) => (_jsx("div", Object.assign({ className: cn("shrink-0", {
78
80
  "mr-2": index < children.length - 1,
79
81
  }) }, { children: chip }), index))) })), showFadeLeft && (_jsx("div", { className: "absolute top-0 left-0 w-8 h-full pointer-events-none bg-fade-left" })), showFadeRight && (_jsx("div", { className: "absolute top-0 right-0 w-8 h-full pointer-events-none bg-fade-right" }))] })));
@@ -1 +1 @@
1
- {"version":3,"file":"selectors.d.ts","sourceRoot":"","sources":["../../../components/ui/selectors.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAMjE,QAAA,MAAM,iBAAiB;;;mFAiBtB,CAAA;AACD,KAAK,cAAc,GAAG,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC,GAC5D,YAAY,CAAC,OAAO,iBAAiB,CAAC,GAAG;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAA;IACjE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAChC,CAAA;AAEH,QAAA,MAAM,SAAS;gIAYZ,cAAc,GAAG,aAAa,wBAAwB,CAAC;;CAyCzD,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EACJ,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,GACpC,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;IACtC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AACD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CA+CvD,CAAA;AAID,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAA"}
1
+ {"version":3,"file":"selectors.d.ts","sourceRoot":"","sources":["../../../components/ui/selectors.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAMjE,QAAA,MAAM,iBAAiB;;;mFAiBtB,CAAA;AACD,KAAK,cAAc,GAAG,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC,GAC5D,YAAY,CAAC,OAAO,iBAAiB,CAAC,GAAG;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAA;IACjE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAChC,CAAA;AAEH,QAAA,MAAM,SAAS;gIAYZ,cAAc,GAAG,aAAa,wBAAwB,CAAC;;CAyCzD,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EACJ,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,GACpC,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;IACtC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AACD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAgDvD,CAAA;AAID,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAA"}
@@ -55,20 +55,22 @@ const Selectors = (_a) => {
55
55
  const SelectorContainer = ({ children, containerRef, className, height, }) => {
56
56
  const [showFadeLeft, setShowFadeLeft] = React.useState(false);
57
57
  const [showFadeRight, setShowFadeRight] = React.useState(false);
58
- const checkOverflow = () => {
59
- const { scrollLeft, scrollWidth, clientWidth } = containerRef.current;
58
+ const checkOverflow = React.useCallback(() => {
59
+ const el = containerRef.current;
60
+ if (!el)
61
+ return;
62
+ const { scrollLeft, scrollWidth, clientWidth } = el;
60
63
  setShowFadeLeft(scrollLeft > 0);
61
64
  setShowFadeRight(scrollLeft + clientWidth < scrollWidth);
62
- };
63
- React.useEffect(() => {
64
- const handleResize = () => checkOverflow();
65
- window.addEventListener("resize", handleResize);
66
- checkOverflow();
67
- return () => window.removeEventListener("resize", handleResize);
68
- }, []);
65
+ }, [containerRef]);
69
66
  React.useEffect(() => {
70
- checkOverflow();
71
- }, [children]);
67
+ const el = containerRef.current;
68
+ if (!el)
69
+ return;
70
+ const ro = new ResizeObserver(() => checkOverflow());
71
+ ro.observe(el);
72
+ return () => ro.disconnect();
73
+ }, [checkOverflow]);
72
74
  return (_jsx("div", Object.assign({ className: "relative no-scrollbar" }, { children: _jsxs("div", Object.assign({ className: cn(`flex gap-2 p-1 items-center overflow-x-auto overflow-y-hidden no-scrollbar`, className), onScroll: checkOverflow, style: { maxHeight: height }, ref: containerRef }, { children: [children, showFadeLeft && (_jsx("div", { className: "absolute top-0 left-0 w-8 h-full pointer-events-none bg-fade-left" })), showFadeRight && (_jsx("div", { className: "absolute top-0 right-0 w-8 h-full pointer-events-none bg-fade-right" }))] })) })));
73
75
  };
74
76
  Selectors.displayName = "Selectors";
@@ -1 +1 @@
1
- {"version":3,"file":"video-enhanced.d.ts","sourceRoot":"","sources":["../../../components/ui/video-enhanced.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,UAAU,UAAW,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IACtE,GAAG,EAAE,MAAM,CAAA;IACX,eAAe,EAAE,eAAe,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,QAAA,MAAM,KAAK,qFAqOV,CAAA;AAID,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"video-enhanced.d.ts","sourceRoot":"","sources":["../../../components/ui/video-enhanced.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,UAAU,UAAW,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IACtE,GAAG,EAAE,MAAM,CAAA;IACX,eAAe,EAAE,eAAe,CAAA;IAChC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,QAAA,MAAM,KAAK,qFA4OV,CAAA;AAID,OAAO,EAAE,KAAK,IAAI,aAAa,EAAE,CAAA"}
@@ -132,18 +132,26 @@ const Video = React.forwardRef((_a, ref) => {
132
132
  video.removeEventListener("timeupdate", handleTimeUpdate);
133
133
  };
134
134
  }, [videoRef]);
135
- // kick off resource selection on first mount for environments that
135
+ // Defer src assignment to useEffect so only committed elements trigger
136
+ // resource loads. React 19's concurrent renderer creates DOM elements
137
+ // during render passes that may be abandoned — setting src in JSX would
138
+ // cause every abandoned <video> to initiate a network request.
139
+ //
140
+ // After assigning src, kick off resource selection for environments that
136
141
  // ignore `preload="metadata"` (e.g., iOS Safari / WebView, Low Power Mode).
137
- // gated on readyState so re-runs of this effect (e.g., from an unstable
142
+ // Gated on readyState so re-runs of this effect (e.g., from an unstable
138
143
  // parent ref) don't abort an in-flight fetch and restart playback.
139
144
  React.useEffect(() => {
140
145
  const video = videoRef.current;
141
146
  if (!video)
142
147
  return;
148
+ if (video.getAttribute("src") !== src) {
149
+ video.src = src;
150
+ }
143
151
  if (video.readyState === 0) {
144
152
  video.load();
145
153
  }
146
- }, [videoRef]);
154
+ }, [src, videoRef]);
147
155
  // metadata setup — uses native addEventListener instead of React's JSX
148
156
  // onLoadedData because non-bubbling media events can fire in the gap
149
157
  // between DOM element creation and React's event listener attachment,
@@ -187,7 +195,7 @@ const Video = React.forwardRef((_a, ref) => {
187
195
  };
188
196
  return (_jsxs("div", Object.assign({ className: "relative w-full" }, { children: [_jsx("video", Object.assign({ ref: videoRef, className: cn(videoVariants.base, videoAttributes.videoFit === "fill"
189
197
  ? videoVariants.fit.cover
190
- : videoVariants.fit.contain, className), style: videoStyle, preload: "metadata", playsInline: true, loop: videoAttributes.enabled, controls: false, src: src }, props)), isLoaded && !isPlaying && (_jsx("button", Object.assign({ onClick: handlePlayPause, className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50\n w-16 h-16 rounded-full bg-black/75 \n flex items-center justify-center transition-all duration-300\n focus:outline-none focus:ring-2 focus:ring-white", "aria-label": "Play video" }, { children: _jsx("div", { className: "w-0 h-0 border-y-8 border-y-transparent \n border-l-[16px] border-l-white \n translate-x-[2px]" }) })))] })));
198
+ : videoVariants.fit.contain, className), style: videoStyle, preload: "metadata", playsInline: true, loop: videoAttributes.enabled, controls: false }, props)), isLoaded && !isPlaying && (_jsx("button", Object.assign({ onClick: handlePlayPause, className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50\n w-16 h-16 rounded-full bg-black/75 \n flex items-center justify-center transition-all duration-300\n focus:outline-none focus:ring-2 focus:ring-white", "aria-label": "Play video" }, { children: _jsx("div", { className: "w-0 h-0 border-y-8 border-y-transparent \n border-l-[16px] border-l-white \n translate-x-[2px]" }) })))] })));
191
199
  });
192
200
  Video.displayName = "Video";
193
201
  export { Video as VideoEnhanced };
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./lib/isTapcartVersion20.util";
6
6
  export * from "./components/contexts/translation-context";
7
7
  export * from "./components/hooks/use-collection";
8
8
  export * from "./components/hooks/use-infinite-scroll";
9
+ export * from "./components/hooks/swr-retry";
9
10
  export * from "./components/hooks/use-infinite-wishlist";
10
11
  export * from "./components/hooks/use-recommendations";
11
12
  export * from "./components/hooks/use-products";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export * from "./lib/isTapcartVersion20.util";
6
6
  export * from "./components/contexts/translation-context";
7
7
  export * from "./components/hooks/use-collection";
8
8
  export * from "./components/hooks/use-infinite-scroll";
9
+ export * from "./components/hooks/swr-retry";
9
10
  export * from "./components/hooks/use-infinite-wishlist";
10
11
  export * from "./components/hooks/use-recommendations";
11
12
  export * from "./components/hooks/use-products";
@@ -1,6 +1,11 @@
1
1
  /* eslint-env jest */
2
- import { getVariablesCalculatedCartData } from "./variablesCart.util";
2
+ import { getVariablesCalculatedCartData as _getVariablesCalculatedCartData } from "./variablesCart.util";
3
3
  import { DiscountApplicationTargetType, } from "app-studio-types";
4
+ // Test wrapper: TestCart uses simplified shapes that aren't structurally assignable
5
+ // to CartState (e.g. appliedGiftCards omits Shopify's required `balance` field).
6
+ // The function under test uses `@ts-nocheck` and reads via `(cart as any)`, so the
7
+ // runtime shape is fine.
8
+ const getVariablesCalculatedCartData = (cart) => _getVariablesCalculatedCartData(cart);
4
9
  const baseCartData = {
5
10
  id: "cart123",
6
11
  subtotal: 100.0,
@@ -9,16 +14,6 @@ const baseCartData = {
9
14
  attributes: [],
10
15
  note: "",
11
16
  items: [],
12
- cost: {
13
- subtotalAmount: {
14
- amount: 0,
15
- currencyCode: "",
16
- },
17
- totalAmount: {
18
- amount: 0,
19
- currencyCode: "",
20
- },
21
- },
22
17
  };
23
18
  describe("cart-provider.util", () => {
24
19
  describe("getData", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapcart/mobile-components",
3
- "version": "0.12.14",
3
+ "version": "0.12.16",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "style": "dist/styles.css",