@doswiftly/storefront-sdk 11.2.0 → 11.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +214 -2
  3. package/dist/core/auth/handlers.d.ts.map +1 -1
  4. package/dist/core/auth/handlers.js +29 -1
  5. package/dist/core/bot-protection/turnstile-manager.d.ts +0 -1
  6. package/dist/core/bot-protection/turnstile-manager.d.ts.map +1 -1
  7. package/dist/core/bot-protection/turnstile-manager.js +0 -1
  8. package/dist/react/components/AddToCartButton.d.ts +49 -0
  9. package/dist/react/components/AddToCartButton.d.ts.map +1 -0
  10. package/dist/react/components/AddToCartButton.js +47 -0
  11. package/dist/react/components/CartCount.d.ts +35 -0
  12. package/dist/react/components/CartCount.d.ts.map +1 -0
  13. package/dist/react/components/CartCount.js +23 -0
  14. package/dist/react/components/CartTotals.d.ts +54 -0
  15. package/dist/react/components/CartTotals.d.ts.map +1 -0
  16. package/dist/react/components/CartTotals.js +38 -0
  17. package/dist/react/components/Image.d.ts +42 -0
  18. package/dist/react/components/Image.d.ts.map +1 -0
  19. package/dist/react/components/Image.js +33 -0
  20. package/dist/react/components/Money.d.ts +32 -0
  21. package/dist/react/components/Money.d.ts.map +1 -0
  22. package/dist/react/components/Money.js +27 -0
  23. package/dist/react/components/PriceDisplay.d.ts +34 -0
  24. package/dist/react/components/PriceDisplay.d.ts.map +1 -0
  25. package/dist/react/components/PriceDisplay.js +21 -0
  26. package/dist/react/components/index.d.ts +15 -0
  27. package/dist/react/components/index.d.ts.map +1 -0
  28. package/dist/react/components/index.js +14 -0
  29. package/dist/react/hooks/use-auth.d.ts +19 -46
  30. package/dist/react/hooks/use-auth.d.ts.map +1 -1
  31. package/dist/react/hooks/use-auth.js +24 -141
  32. package/dist/react/hooks/use-cart-manager.d.ts +34 -0
  33. package/dist/react/hooks/use-cart-manager.d.ts.map +1 -1
  34. package/dist/react/hooks/use-cart-manager.js +22 -19
  35. package/dist/react/hooks/use-login.d.ts +40 -0
  36. package/dist/react/hooks/use-login.d.ts.map +1 -0
  37. package/dist/react/hooks/use-login.js +75 -0
  38. package/dist/react/hooks/use-logout.d.ts +40 -0
  39. package/dist/react/hooks/use-logout.d.ts.map +1 -0
  40. package/dist/react/hooks/use-logout.js +50 -0
  41. package/dist/react/hooks/use-refresh-token.d.ts +40 -0
  42. package/dist/react/hooks/use-refresh-token.d.ts.map +1 -0
  43. package/dist/react/hooks/use-refresh-token.js +66 -0
  44. package/dist/react/index.d.ts +5 -1
  45. package/dist/react/index.d.ts.map +1 -1
  46. package/dist/react/index.js +5 -0
  47. package/dist/react/server/get-storefront-client.d.ts +15 -5
  48. package/dist/react/server/get-storefront-client.d.ts.map +1 -1
  49. package/dist/react/stores/cart.store.d.ts.map +1 -1
  50. package/dist/react/stores/cart.store.js +0 -7
  51. package/dist/react/stores/store-context.d.ts.map +1 -1
  52. package/dist/react/stores/store-context.js +0 -2
  53. package/package.json +5 -1
  54. package/dist/__tests__/unit/test-helpers.d.ts +0 -46
  55. package/dist/__tests__/unit/test-helpers.d.ts.map +0 -1
  56. package/dist/__tests__/unit/test-helpers.js +0 -72
  57. package/dist/__tests__/unit/use-cart-manager.test.d.ts +0 -2
  58. package/dist/__tests__/unit/use-cart-manager.test.d.ts.map +0 -1
  59. package/dist/__tests__/unit/use-cart-manager.test.js +0 -400
@@ -42,8 +42,7 @@ import { createCartRecoveryRunner, recreateWithInput, } from '../../core/cart/ca
42
42
  import { createBrowserCartCookieStore } from '../cookies';
43
43
  export function useCartManager() {
44
44
  const { cartClient } = useStorefrontClientContext();
45
- const [isLoading, setIsLoading] = useState(false);
46
- const [error, setError] = useState(null);
45
+ const [status, setStatus] = useState({ type: 'idle' });
47
46
  // Cookie store is stateless — keep one instance per hook mount.
48
47
  const cookieStore = useMemo(() => createBrowserCartCookieStore(), []);
49
48
  // Recovery runner is bound to cartClient identity (changes only on provider
@@ -55,64 +54,67 @@ export function useCartManager() {
55
54
  // `useEffect(() => onExpired(...), [onExpired])` so React re-subscribes when
56
55
  // the provider (and thus the runner) remounts.
57
56
  const onExpired = useCallback((listener) => runner.onExpired(listener), [runner]);
58
- // Generic mutation wrapper — sets loading + error state, delegates recovery
59
- // semantics to the runner.
60
- const wrapMutation = useCallback(async (run, failureFallbackMessage) => {
61
- setError(null);
62
- setIsLoading(true);
57
+ // Generic mutation wrapper — drives the tagged-union status. Delegates
58
+ // recovery semantics to the runner (which fires onExpired separately).
59
+ const wrapMutation = useCallback(async (operation, run, failureFallbackMessage) => {
60
+ setStatus({ type: 'loading', operation });
63
61
  try {
64
- return await run();
62
+ const result = await run();
63
+ setStatus({ type: 'success', operation });
64
+ return result;
65
65
  }
66
66
  catch (err) {
67
- setError(err instanceof Error ? err.message : failureFallbackMessage);
67
+ const error = err instanceof Error ? err : new Error(failureFallbackMessage);
68
+ setStatus({ type: 'error', operation, error });
68
69
  throw err;
69
70
  }
70
- finally {
71
- setIsLoading(false);
72
- }
73
71
  }, []);
74
72
  // --- Read ---
75
73
  const getCart = useCallback(() => runner.getCart(), [runner]);
76
74
  const getCartId = useCallback(() => cookieStore.get(), [cookieStore]);
77
75
  // --- Auto-replay mutations ---
78
- const addItem = useCallback((lines) => wrapMutation(() => runner.execute({
76
+ const addItem = useCallback((lines) => wrapMutation('addItem', () => runner.execute({
79
77
  name: 'addItem',
80
78
  run: (cartId) => cartClient.addItems(cartId, lines),
81
79
  recreateAndRun: recreateWithInput({ lines }),
82
80
  }), 'Failed to add to cart'), [runner, cartClient, wrapMutation]);
83
- const updateBuyerIdentity = useCallback((buyerIdentity) => wrapMutation(() => runner.execute({
81
+ const updateBuyerIdentity = useCallback((buyerIdentity) => wrapMutation('updateBuyerIdentity', () => runner.execute({
84
82
  name: 'updateBuyerIdentity',
85
83
  run: (cartId) => cartClient.updateBuyerIdentity(cartId, buyerIdentity),
86
84
  recreateAndRun: recreateWithInput({ buyerIdentity }),
87
85
  }), 'Failed to update buyer identity'), [runner, cartClient, wrapMutation]);
88
- const setShippingAddress = useCallback((address) => wrapMutation(() => runner.execute({
86
+ const setShippingAddress = useCallback((address) => wrapMutation('setShippingAddress', () => runner.execute({
89
87
  name: 'setShippingAddress',
90
88
  run: (cartId) => cartClient.setShippingAddress({ cartId, address }),
91
89
  recreateAndRun: recreateWithInput({ shippingAddress: address }),
92
90
  }), 'Failed to set shipping address'), [runner, cartClient, wrapMutation]);
93
- const updateDiscountCodes = useCallback((codes) => wrapMutation(() => runner.execute({
91
+ const updateDiscountCodes = useCallback((codes) => wrapMutation('updateDiscountCodes', () => runner.execute({
94
92
  name: 'updateDiscountCodes',
95
93
  run: (cartId) => cartClient.updateDiscountCodes(cartId, codes),
96
94
  recreateAndRun: recreateWithInput({ discountCodes: codes }),
97
95
  }), 'Failed to update discount codes'), [runner, cartClient, wrapMutation]);
98
- const updateNote = useCallback((note) => wrapMutation(() => runner.execute({
96
+ const updateNote = useCallback((note) => wrapMutation('updateNote', () => runner.execute({
99
97
  name: 'updateNote',
100
98
  run: (cartId) => cartClient.updateNote(cartId, note),
101
99
  recreateAndRun: recreateWithInput({ note }),
102
100
  }), 'Failed to update note'), [runner, cartClient, wrapMutation]);
103
101
  // --- Bail-on-stale mutations (no recreateAndRun — fires onExpired + throws) ---
104
- const updateItem = useCallback((lines) => wrapMutation(() => runner.execute({
102
+ const updateItem = useCallback((lines) => wrapMutation('updateItem', () => runner.execute({
105
103
  name: 'updateItem',
106
104
  run: (cartId) => cartClient.updateItems(cartId, lines),
107
105
  }), 'Failed to update cart'), [runner, cartClient, wrapMutation]);
108
- const removeItem = useCallback((lineIds) => wrapMutation(() => runner.execute({
106
+ const removeItem = useCallback((lineIds) => wrapMutation('removeItem', () => runner.execute({
109
107
  name: 'removeItem',
110
108
  run: (cartId) => cartClient.removeItems(cartId, lineIds),
111
109
  }), 'Failed to remove from cart'), [runner, cartClient, wrapMutation]);
112
110
  // --- Lifecycle ---
113
111
  const clearCart = useCallback(() => {
114
112
  cookieStore.clear();
113
+ setStatus({ type: 'idle' });
115
114
  }, [cookieStore]);
115
+ // Backward-compatible derived selectors over the tagged-union status.
116
+ const isLoading = status.type === 'loading';
117
+ const error = status.type === 'error' ? status.error.message : null;
116
118
  return {
117
119
  getCart,
118
120
  getCartId,
@@ -125,6 +127,7 @@ export function useCartManager() {
125
127
  removeItem,
126
128
  clearCart,
127
129
  onExpired,
130
+ status,
128
131
  isLoading,
129
132
  error,
130
133
  };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * useLogin — focused hook for the login flow.
3
+ *
4
+ * Composes the auth client + Zustand store + httpOnly cookie BFF callback.
5
+ * Returns `success`/`userErrors` rather than throwing on credential errors —
6
+ * standard form submission pattern.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { login, isLoggingIn, error } = useLogin({ onSetToken });
11
+ * const result = await login(email, password);
12
+ * if (!result.success) showErrors(result.userErrors);
13
+ * ```
14
+ */
15
+ export interface UseLoginOptions {
16
+ /**
17
+ * Called after successful login with the new access token. Use this to set
18
+ * the httpOnly cookie via an API route handler (`createSetTokenHandler`).
19
+ */
20
+ onSetToken?: (token: string) => Promise<void>;
21
+ }
22
+ export interface LoginResult {
23
+ success: boolean;
24
+ userErrors: Array<{
25
+ message: string;
26
+ field?: string[];
27
+ }>;
28
+ accessToken?: string;
29
+ expiresAt?: string;
30
+ }
31
+ export interface UseLoginReturn {
32
+ /** Submit the login form. Resolves with `{success, userErrors}` rather than throwing on credential failures. */
33
+ login: (email: string, password: string) => Promise<LoginResult>;
34
+ /** `true` while a login request is in flight. */
35
+ isLoggingIn: boolean;
36
+ /** Last unexpected error message (network, server) — credential failures land in `userErrors` instead. */
37
+ error: string | null;
38
+ }
39
+ export declare function useLogin(options?: UseLoginOptions): UseLoginReturn;
40
+ //# sourceMappingURL=use-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-login.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,gHAAgH;IAChH,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACjE,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,0GAA0G;IAC1G,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,cAAc,CA+DtE"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * useLogin — focused hook for the login flow.
3
+ *
4
+ * Composes the auth client + Zustand store + httpOnly cookie BFF callback.
5
+ * Returns `success`/`userErrors` rather than throwing on credential errors —
6
+ * standard form submission pattern.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { login, isLoggingIn, error } = useLogin({ onSetToken });
11
+ * const result = await login(email, password);
12
+ * if (!result.success) showErrors(result.userErrors);
13
+ * ```
14
+ */
15
+ 'use client';
16
+ import { useCallback, useState } from 'react';
17
+ import { useStorefrontClientContext } from '../providers/storefront-client-provider';
18
+ import { useAuthStore } from '../stores/store-context';
19
+ import { StorefrontError } from '../../core/errors';
20
+ export function useLogin(options = {}) {
21
+ const { authClient } = useStorefrontClientContext();
22
+ const { setAuth } = useAuthStore();
23
+ const [isLoggingIn, setIsLoggingIn] = useState(false);
24
+ const [error, setError] = useState(null);
25
+ const login = useCallback(async (email, password) => {
26
+ setError(null);
27
+ setIsLoggingIn(true);
28
+ try {
29
+ const result = await authClient.login(email, password);
30
+ if (options.onSetToken) {
31
+ await options.onSetToken(result.accessToken);
32
+ }
33
+ // Fetch customer data and set store (best-effort — fallback to minimal data)
34
+ try {
35
+ const customer = await authClient.getCustomer();
36
+ if (customer) {
37
+ setAuth({
38
+ id: customer.id,
39
+ email: customer.email,
40
+ firstName: customer.firstName ?? undefined,
41
+ lastName: customer.lastName ?? undefined,
42
+ phone: customer.phone ?? undefined,
43
+ }, result.accessToken);
44
+ }
45
+ else {
46
+ setAuth({ id: '', email }, result.accessToken);
47
+ }
48
+ }
49
+ catch {
50
+ setAuth({ id: '', email }, result.accessToken);
51
+ }
52
+ return {
53
+ success: true,
54
+ userErrors: [],
55
+ accessToken: result.accessToken,
56
+ expiresAt: result.expiresAt,
57
+ };
58
+ }
59
+ catch (err) {
60
+ if (err instanceof StorefrontError && err.hasUserErrors) {
61
+ return {
62
+ success: false,
63
+ userErrors: err.userErrors.map((e) => ({ message: e.message, field: e.field })),
64
+ };
65
+ }
66
+ const message = err instanceof Error ? err.message : 'Login failed';
67
+ setError(message);
68
+ return { success: false, userErrors: [{ message }] };
69
+ }
70
+ finally {
71
+ setIsLoggingIn(false);
72
+ }
73
+ }, [authClient, setAuth, options]);
74
+ return { login, isLoggingIn, error };
75
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * useLogout — focused hook for the logout flow.
3
+ *
4
+ * Calls the auth client logout (which clears the backend's httpOnly cookie),
5
+ * invokes the consumer's `onClearToken` callback (BFF), and resets the local
6
+ * auth store. Even when the backend request fails, the local state is
7
+ * cleared (defensive — better to log a user out than leave them in a half
8
+ * state).
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const { logout, isLoggingOut } = useLogout({ onClearToken });
13
+ * await logout();
14
+ * router.push('/');
15
+ * ```
16
+ */
17
+ export interface UseLogoutOptions {
18
+ /**
19
+ * Called after logout to clear the httpOnly cookie via an API route handler
20
+ * (`createClearTokenHandler`).
21
+ */
22
+ onClearToken?: () => Promise<void>;
23
+ }
24
+ export interface LogoutResult {
25
+ success: boolean;
26
+ userErrors: Array<{
27
+ message: string;
28
+ field?: string[];
29
+ }>;
30
+ }
31
+ export interface UseLogoutReturn {
32
+ /** Run the logout flow. Resolves with `{success}` — local state is always cleared. */
33
+ logout: () => Promise<LogoutResult>;
34
+ /** `true` while the logout request is in flight. */
35
+ isLoggingOut: boolean;
36
+ /** Last error message (network, server) — local state is cleared regardless. */
37
+ error: string | null;
38
+ }
39
+ export declare function useLogout(options?: UseLogoutOptions): UseLogoutReturn;
40
+ //# sourceMappingURL=use-logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-logout.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-logout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,sFAAsF;IACtF,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACpC,oDAAoD;IACpD,YAAY,EAAE,OAAO,CAAC;IACtB,gFAAgF;IAChF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,eAAe,CAgCzE"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * useLogout — focused hook for the logout flow.
3
+ *
4
+ * Calls the auth client logout (which clears the backend's httpOnly cookie),
5
+ * invokes the consumer's `onClearToken` callback (BFF), and resets the local
6
+ * auth store. Even when the backend request fails, the local state is
7
+ * cleared (defensive — better to log a user out than leave them in a half
8
+ * state).
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const { logout, isLoggingOut } = useLogout({ onClearToken });
13
+ * await logout();
14
+ * router.push('/');
15
+ * ```
16
+ */
17
+ 'use client';
18
+ import { useCallback, useState } from 'react';
19
+ import { useStorefrontClientContext } from '../providers/storefront-client-provider';
20
+ import { useAuthStore } from '../stores/store-context';
21
+ export function useLogout(options = {}) {
22
+ const { authClient } = useStorefrontClientContext();
23
+ const { clearAuth } = useAuthStore();
24
+ const [isLoggingOut, setIsLoggingOut] = useState(false);
25
+ const [error, setError] = useState(null);
26
+ const logout = useCallback(async () => {
27
+ setError(null);
28
+ setIsLoggingOut(true);
29
+ try {
30
+ // Auth context resolved server-side from cookie/Bearer — no token arg needed
31
+ await authClient.logout();
32
+ if (options.onClearToken) {
33
+ await options.onClearToken();
34
+ }
35
+ clearAuth();
36
+ return { success: true, userErrors: [] };
37
+ }
38
+ catch (err) {
39
+ // Even on error, clear local state — better than a stuck half-state
40
+ clearAuth();
41
+ const message = err instanceof Error ? err.message : 'Logout failed';
42
+ setError(message);
43
+ return { success: false, userErrors: [{ message }] };
44
+ }
45
+ finally {
46
+ setIsLoggingOut(false);
47
+ }
48
+ }, [authClient, clearAuth, options]);
49
+ return { logout, isLoggingOut, error };
50
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * useRefreshToken — focused hook for renewing the access token.
3
+ *
4
+ * Wraps `AuthClient.refreshToken`. On success the new token is propagated to
5
+ * the consumer's BFF (`onSetToken`) and the store keeps the existing customer
6
+ * data with the new token attached.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Periodic refresh (e.g. every 30 min)
11
+ * const { refreshToken } = useRefreshToken({ onSetToken });
12
+ * useEffect(() => {
13
+ * const timer = setInterval(() => refreshToken(), 30 * 60 * 1000);
14
+ * return () => clearInterval(timer);
15
+ * }, [refreshToken]);
16
+ * ```
17
+ */
18
+ export interface UseRefreshTokenOptions {
19
+ /** Called after a successful refresh with the new access token (BFF cookie sync). */
20
+ onSetToken?: (token: string) => Promise<void>;
21
+ }
22
+ export interface TokenRefreshResult {
23
+ success: boolean;
24
+ userErrors: Array<{
25
+ message: string;
26
+ field?: string[];
27
+ }>;
28
+ accessToken?: string;
29
+ expiresAt?: string;
30
+ }
31
+ export interface UseRefreshTokenReturn {
32
+ /** Renew the access token. Resolves with `{success}`. */
33
+ refreshToken: () => Promise<TokenRefreshResult>;
34
+ /** `true` while a refresh request is in flight. */
35
+ isRefreshingToken: boolean;
36
+ /** Last unexpected error message — credential / session expiry lands in `userErrors`. */
37
+ error: string | null;
38
+ }
39
+ export declare function useRefreshToken(options?: UseRefreshTokenOptions): UseRefreshTokenReturn;
40
+ //# sourceMappingURL=use-refresh-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-refresh-token.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-refresh-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,MAAM,WAAW,sBAAsB;IACrC,qFAAqF;IACrF,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,yDAAyD;IACzD,YAAY,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChD,mDAAmD;IACnD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,yFAAyF;IACzF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,qBAAqB,CA+C3F"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * useRefreshToken — focused hook for renewing the access token.
3
+ *
4
+ * Wraps `AuthClient.refreshToken`. On success the new token is propagated to
5
+ * the consumer's BFF (`onSetToken`) and the store keeps the existing customer
6
+ * data with the new token attached.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Periodic refresh (e.g. every 30 min)
11
+ * const { refreshToken } = useRefreshToken({ onSetToken });
12
+ * useEffect(() => {
13
+ * const timer = setInterval(() => refreshToken(), 30 * 60 * 1000);
14
+ * return () => clearInterval(timer);
15
+ * }, [refreshToken]);
16
+ * ```
17
+ */
18
+ 'use client';
19
+ import { useCallback, useState } from 'react';
20
+ import { useStorefrontClientContext } from '../providers/storefront-client-provider';
21
+ import { useAuthStore, useAuthStoreApi } from '../stores/store-context';
22
+ import { StorefrontError } from '../../core/errors';
23
+ export function useRefreshToken(options = {}) {
24
+ const { authClient } = useStorefrontClientContext();
25
+ const { setAuth } = useAuthStore();
26
+ const authStore = useAuthStoreApi();
27
+ const [isRefreshingToken, setIsRefreshingToken] = useState(false);
28
+ const [error, setError] = useState(null);
29
+ const refreshToken = useCallback(async () => {
30
+ setError(null);
31
+ setIsRefreshingToken(true);
32
+ try {
33
+ // Auth context resolved server-side from cookie/Bearer. If no active
34
+ // session, backend returns 401 and refreshToken throws.
35
+ const result = await authClient.refreshToken();
36
+ if (options.onSetToken) {
37
+ await options.onSetToken(result.accessToken);
38
+ }
39
+ const currentCustomer = authStore.getState().customer;
40
+ if (currentCustomer) {
41
+ setAuth(currentCustomer, result.accessToken);
42
+ }
43
+ return {
44
+ success: true,
45
+ userErrors: [],
46
+ accessToken: result.accessToken,
47
+ expiresAt: result.expiresAt,
48
+ };
49
+ }
50
+ catch (err) {
51
+ if (err instanceof StorefrontError && err.hasUserErrors) {
52
+ return {
53
+ success: false,
54
+ userErrors: err.userErrors.map((e) => ({ message: e.message, field: e.field })),
55
+ };
56
+ }
57
+ const message = err instanceof Error ? err.message : 'Token renewal failed';
58
+ setError(message);
59
+ return { success: false, userErrors: [{ message }] };
60
+ }
61
+ finally {
62
+ setIsRefreshingToken(false);
63
+ }
64
+ }, [authClient, setAuth, authStore, options]);
65
+ return { refreshToken, isRefreshingToken, error };
66
+ }
@@ -16,7 +16,10 @@ export { StorefrontClientProvider, type StorefrontClientProviderProps } from './
16
16
  export { CurrencyProvider, type CurrencyProviderProps } from './providers/currency-provider';
17
17
  export { LanguageProvider, type LanguageProviderProps } from './providers/language-provider';
18
18
  export { useAuth, type UseAuthOptions, type LoginResult, type LogoutResult, type TokenRefreshResult } from './hooks/use-auth';
19
- export { useCartManager } from './hooks/use-cart-manager';
19
+ export { useLogin, type UseLoginOptions, type UseLoginReturn } from './hooks/use-login';
20
+ export { useLogout, type UseLogoutOptions, type UseLogoutReturn } from './hooks/use-logout';
21
+ export { useRefreshToken, type UseRefreshTokenOptions, type UseRefreshTokenReturn } from './hooks/use-refresh-token';
22
+ export { useCartManager, type CartManagerOperation, type CartManagerStatus, type UseCartManagerResult } from './hooks/use-cart-manager';
20
23
  export { useStorefrontClient } from './hooks/use-storefront-client';
21
24
  export { useCurrency } from './hooks/use-currency';
22
25
  export { useAuthStore, useAuthStoreApi, useAuthHydrated } from './stores/store-context';
@@ -36,4 +39,5 @@ export { createCartStore, selectCartId, selectIsCartOpen, selectCartIsLoading, }
36
39
  export type { CartState, CartStoreConfig, CartActions, CartData, CartMutationAction, CartLineInput, CartLineUpdateInput, } from './stores/cart.store';
37
40
  export { CartProvider, useCartStore, useCartStoreApi } from './stores/cart.context';
38
41
  export { createStoreContext } from './helpers/create-store-context';
42
+ export { Money, type MoneyProps, Image, type ImageComponentProps, CartCount, type CartCountProps, AddToCartButton, type AddToCartButtonProps, PriceDisplay, type PriceDisplayProps, CartTotals, type CartTotalsProps, type CartTotalsLabels, } from './components';
39
43
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AACnG,OAAO,EAAE,wBAAwB,EAAE,KAAK,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACtH,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAG7F,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9H,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG/E,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACxH,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGlI,OAAO,EACL,SAAS,EACT,SAAS,EACT,YAAY,EACZ,0BAA0B,EAC1B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG9D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,EACL,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,9 @@ export { CurrencyProvider } from './providers/currency-provider';
18
18
  export { LanguageProvider } from './providers/language-provider';
19
19
  // Hooks
20
20
  export { useAuth } from './hooks/use-auth';
21
+ export { useLogin } from './hooks/use-login';
22
+ export { useLogout } from './hooks/use-logout';
23
+ export { useRefreshToken } from './hooks/use-refresh-token';
21
24
  export { useCartManager } from './hooks/use-cart-manager';
22
25
  export { useStorefrontClient } from './hooks/use-storefront-client';
23
26
  export { useCurrency } from './hooks/use-currency';
@@ -40,3 +43,5 @@ export { createCartStore, selectCartId, selectIsCartOpen, selectCartIsLoading, }
40
43
  export { CartProvider, useCartStore, useCartStoreApi } from './stores/cart.context';
41
44
  // Store context helper
42
45
  export { createStoreContext } from './helpers/create-store-context';
46
+ // Pre-built components (headless — no styling, accessibility-aware)
47
+ export { Money, Image, CartCount, AddToCartButton, PriceDisplay, CartTotals, } from './components';
@@ -21,13 +21,23 @@
21
21
  */
22
22
  import type { StorefrontClient, StorefrontClientConfig, Middleware } from '../../core/client/types';
23
23
  export interface ServerClientOptions extends Omit<StorefrontClientConfig, 'middleware'> {
24
- /**
25
- * Function that returns request-scoped headers (e.g. currency from cookie).
26
- * Called once per client creation.
27
- */
28
- getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
29
24
  /**
30
25
  * Additional middleware (prepended to default pipeline).
26
+ *
27
+ * Use this for request-scoped headers (e.g. currency from cookie, language
28
+ * from URL, custom tracking):
29
+ *
30
+ * ```ts
31
+ * getStorefrontClient({
32
+ * apiUrl, shopSlug,
33
+ * middleware: [
34
+ * async (req, next) => {
35
+ * req.headers['X-Preferred-Currency'] = await readCookieFromHeaders();
36
+ * return next(req);
37
+ * },
38
+ * ],
39
+ * });
40
+ * ```
31
41
  */
32
42
  middleware?: Middleware[];
33
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"get-storefront-client.d.ts","sourceRoot":"","sources":["../../../src/react/server/get-storefront-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEpG,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,sBAAsB,EAAE,YAAY,CAAC;IACrF;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5E;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,CAYlF"}
1
+ {"version":3,"file":"get-storefront-client.d.ts","sourceRoot":"","sources":["../../../src/react/server/get-storefront-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEpG,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,sBAAsB,EAAE,YAAY,CAAC;IACrF;;;;;;;;;;;;;;;;;OAiBG;IACH,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,CAYlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"cart.store.d.ts","sourceRoot":"","sources":["../../../src/react/stores/cart.store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cart.store.d.ts","sourceRoot":"","sources":["../../../src/react/stores/cart.store.ts"],"names":[],"mappings":"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"}
@@ -62,13 +62,6 @@ export function createCartStore(config) {
62
62
  let initPromise = null;
63
63
  // Read initial cartId from cookie (client-side only, returns null on server)
64
64
  const initialCartId = getCookie(CART_COOKIE_NAME);
65
- // Clean up old localStorage entry from pre-cookie era (v1 persist)
66
- if (typeof localStorage !== 'undefined') {
67
- try {
68
- localStorage.removeItem('cart-storage');
69
- }
70
- catch { /* ignore */ }
71
- }
72
65
  function emitExpired(event) {
73
66
  if (!config.onExpired)
74
67
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"store-context.d.ts","sourceRoot":"","sources":["../../../src/react/stores/store-context.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMtD,eAAO,MAAM,gBAAgB,qDAAkD,CAAC;AAChF,eAAO,MAAM,oBAAoB,yDAAsD,CAAC;AACxF,eAAO,MAAM,oBAAoB,yDAAsD,CAAC;AAMxF,wBAAgB,YAAY,IAAI,SAAS,CAAC;AAC1C,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,CAAC;AAQlE,wBAAgB,eAAe,IAAI,QAAQ,CAAC,SAAS,CAAC,CAIrD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAczC;AAMD,wBAAgB,gBAAgB,IAAI,aAAa,CAAC;AAClD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,CAAC,GAAG,CAAC,CAAC;AAQ1E,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,aAAa,CAAC,CAI7D;AAMD,wBAAgB,gBAAgB,IAAI,aAAa,CAAC;AAClD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,CAAC,GAAG,CAAC,CAAC;AAQ1E,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,aAAa,CAAC,CAI7D"}
1
+ {"version":3,"file":"store-context.d.ts","sourceRoot":"","sources":["../../../src/react/stores/store-context.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMtD,eAAO,MAAM,gBAAgB,qDAAkD,CAAC;AAChF,eAAO,MAAM,oBAAoB,yDAAsD,CAAC;AACxF,eAAO,MAAM,oBAAoB,yDAAsD,CAAC;AAMxF,wBAAgB,YAAY,IAAI,SAAS,CAAC;AAC1C,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,CAAC;AAQlE,wBAAgB,eAAe,IAAI,QAAQ,CAAC,SAAS,CAAC,CAIrD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAoBzC;AAMD,wBAAgB,gBAAgB,IAAI,aAAa,CAAC;AAClD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,CAAC,GAAG,CAAC,CAAC;AAQ1E,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,aAAa,CAAC,CAI7D;AAMD,wBAAgB,gBAAgB,IAAI,aAAa,CAAC;AAClD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,CAAC,GAAG,CAAC,CAAC;AAQ1E,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,aAAa,CAAC,CAI7D"}
@@ -39,9 +39,7 @@ export function useAuthHydrated() {
39
39
  const [hydrated, setHydrated] = useState(false);
40
40
  useEffect(() => {
41
41
  const persistApi = store.persist;
42
- // Subscribe to future hydration completions
43
42
  const unsubFinish = persistApi.onFinishHydration(() => setHydrated(true));
44
- // Check if hydration already completed before effect ran
45
43
  if (persistApi.hasHydrated())
46
44
  setHydrated(true);
47
45
  return unsubFinish;
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@doswiftly/storefront-sdk",
3
- "version": "11.2.0",
3
+ "version": "11.3.1",
4
4
  "description": "Storefront runtime SDK for DoSwiftly Commerce — layered transport, middleware pipeline, React providers, Zustand stores, cache strategies. 0 runtime dependencies in core.",
5
5
  "type": "module",
6
+ "sideEffects": false,
6
7
  "files": [
7
8
  "dist",
8
9
  "README.md",
@@ -44,6 +45,7 @@
44
45
  "@types/node": "^22.10.2",
45
46
  "@types/react": "^18.3.0 || ^19.0.0",
46
47
  "@types/react-dom": "^19.0.0",
48
+ "@vitest/coverage-v8": "^4.1.0",
47
49
  "fast-check": "^3.23.2",
48
50
  "jsdom": "^25.0.1",
49
51
  "next": "^16.2.3",
@@ -74,6 +76,8 @@
74
76
  "test:watch": "vitest",
75
77
  "test:unit": "vitest run src/__tests__/unit/",
76
78
  "test:contract": "vitest run src/__tests__/contract/",
79
+ "test:coverage": "vitest run --coverage",
80
+ "doctor": "node scripts/doctor.cjs",
77
81
  "validate:cart": "node scripts/validate-cart-operations.cjs --strict"
78
82
  }
79
83
  }
@@ -1,46 +0,0 @@
1
- /**
2
- * Shared test helpers — mock fetch factory, mock client builder.
3
- */
4
- /**
5
- * Create a mock fetch that returns the specified GraphQL response.
6
- */
7
- export declare function createMockFetch(responseData: unknown, options?: {
8
- errors?: Array<{
9
- message: string;
10
- }>;
11
- status?: number;
12
- delay?: number;
13
- }): typeof globalThis.fetch;
14
- /**
15
- * Create a mock fetch that tracks all calls and returns specified response.
16
- */
17
- export declare function createSpyFetch(responseData: unknown, options?: {
18
- errors?: Array<{
19
- message: string;
20
- }>;
21
- status?: number;
22
- }): {
23
- fetch: typeof globalThis.fetch;
24
- calls: {
25
- url: string;
26
- init: RequestInit;
27
- }[];
28
- };
29
- /**
30
- * Create a mock fetch that fails with a network error.
31
- */
32
- export declare function createNetworkErrorFetch(errorMessage?: string): typeof globalThis.fetch;
33
- /**
34
- * Create a mock fetch that returns different responses on subsequent calls.
35
- */
36
- export declare function createSequenceFetch(responses: Array<{
37
- data?: unknown;
38
- errors?: Array<{
39
- message: string;
40
- }>;
41
- status?: number;
42
- }>): {
43
- fetch: typeof globalThis.fetch;
44
- callCount: () => number;
45
- };
46
- //# sourceMappingURL=test-helpers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/test-helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACA,OAAO,UAAU,CAAC,KAAK,CAqBzB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;WAqB0B,OAAO,UAAU,CAAC,KAAK;;aAnBxB,MAAM;cAAQ,WAAW;;EAoBpD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,SAAiB,GAAG,OAAO,UAAU,CAAC,KAAK,CAI9F;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACzF;IAAE,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,MAAM,CAAA;CAAE,CAiB7D"}