medusa-cart-logic 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/cart-logic/src/hooks/useCart.d.ts +32 -0
- package/dist/cart-logic/src/index.d.ts +2 -0
- package/dist/medusa-services/cart.d.ts +106 -0
- package/dist/ui-library.js +275 -0
- package/dist/ui-library.umd.cjs +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# medusa-cart-logic
|
|
2
|
+
|
|
3
|
+
Reusable cart hook logic for Medusa storefronts.
|
|
4
|
+
|
|
5
|
+
This package exposes `useCart` and Medusa cart helpers used by cart flows:
|
|
6
|
+
- cart create/retrieve/update
|
|
7
|
+
- line items (add/update/remove)
|
|
8
|
+
- promotions/coupons (apply/remove)
|
|
9
|
+
- totals + items state for cart UI
|
|
10
|
+
- guest/customer cookie session support via `credentials: "include"`
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
For local development in this monorepo:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm run build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Exports
|
|
25
|
+
|
|
26
|
+
From `src/index.ts`:
|
|
27
|
+
- `useCart` hook
|
|
28
|
+
- cart service helpers from `medusa-services/cart`
|
|
29
|
+
|
|
30
|
+
## Quick Usage
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { useCart } from "medusa-cart-logic"
|
|
34
|
+
|
|
35
|
+
const cart = useCart({
|
|
36
|
+
backendUrl: "https://your-medusa-server.com",
|
|
37
|
+
publishableApiKey: "pk_...",
|
|
38
|
+
cartId, // from localStorage/session/app state
|
|
39
|
+
onCartIdChange: (nextId) => {
|
|
40
|
+
setCartId(nextId)
|
|
41
|
+
if (nextId) localStorage.setItem("cart_id", nextId)
|
|
42
|
+
},
|
|
43
|
+
credentials: "include",
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await cart.ensureCart()
|
|
47
|
+
await cart.addLineItem({ variant_id: "variant_123", quantity: 1 })
|
|
48
|
+
await cart.applyPromotions(["SUMMER10"])
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## `useCart` Input
|
|
52
|
+
|
|
53
|
+
- `backendUrl`: Medusa backend URL
|
|
54
|
+
- `publishableApiKey`: Medusa publishable key
|
|
55
|
+
- `cartId`: current cart id (nullable)
|
|
56
|
+
- `onCartIdChange`: callback when `createCart` returns a new id
|
|
57
|
+
- `authorization`: optional customer JWT (`Bearer` prefix optional)
|
|
58
|
+
- `credentials`: request credentials mode (defaults to `"include"`)
|
|
59
|
+
- `fields`: optional Medusa Store `fields` query override
|
|
60
|
+
- `enabled`: controls auto refresh behavior (default `true`)
|
|
61
|
+
|
|
62
|
+
## Returned API
|
|
63
|
+
|
|
64
|
+
- State:
|
|
65
|
+
- `cart`
|
|
66
|
+
- `loading`
|
|
67
|
+
- `mutating`
|
|
68
|
+
- `error`
|
|
69
|
+
- Actions:
|
|
70
|
+
- `refresh`
|
|
71
|
+
- `createCart`
|
|
72
|
+
- `ensureCart`
|
|
73
|
+
- `updateCart`
|
|
74
|
+
- `addLineItem`
|
|
75
|
+
- `updateLineItem`
|
|
76
|
+
- `removeLineItem`
|
|
77
|
+
- `applyPromotions`
|
|
78
|
+
- `removePromotions`
|
|
79
|
+
- `clearError`
|
|
80
|
+
|
|
81
|
+
## Medusa Guest Cart Notes
|
|
82
|
+
|
|
83
|
+
- Browser JavaScript cannot read HttpOnly cookies like `_medusa_cart_id`.
|
|
84
|
+
- Keep your own `cartId` reference (for example in localStorage, session state, or server session).
|
|
85
|
+
- Always send cart requests with `credentials: "include"` so cookie-backed session/cart behavior works across requests.
|
|
86
|
+
|
|
87
|
+
## Notes
|
|
88
|
+
|
|
89
|
+
- This package is UI-agnostic and focuses only on cart logic/state transitions.
|
|
90
|
+
- Use your own UI components for list rendering, quantity controls, coupon input, and checkout CTA.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MedusaAddLineItemBody, MedusaStoreCartClientOptions, MedusaUpdateLineItemBody, StoreCart } from '../../../medusa-services/cart';
|
|
2
|
+
|
|
3
|
+
export interface UseCartOptions extends MedusaStoreCartClientOptions {
|
|
4
|
+
/** Medusa cart id for `GET /store/carts/:id` (store in localStorage, SSR props, or your own layer). */
|
|
5
|
+
cartId: string | null | undefined;
|
|
6
|
+
/** Called when a new cart is created so you can persist `cart.id`. */
|
|
7
|
+
onCartIdChange?: (cartId: string | null) => void;
|
|
8
|
+
/** Optional `fields` query override (see `MEDUSA_STORE_CART_DEFAULT_FIELDS`). */
|
|
9
|
+
fields?: string;
|
|
10
|
+
/** When false, skips automatic refresh on mount / cartId change. @default true */
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface UseCartResult {
|
|
14
|
+
cart: StoreCart | null;
|
|
15
|
+
loading: boolean;
|
|
16
|
+
mutating: boolean;
|
|
17
|
+
error: string | null;
|
|
18
|
+
/** Load cart from `cartId` (no-op if id missing). */
|
|
19
|
+
refresh: () => Promise<StoreCart | null>;
|
|
20
|
+
/** POST /store/carts — sets cart and notifies `onCartIdChange`. */
|
|
21
|
+
createCart: (body?: Record<string, unknown>) => Promise<StoreCart>;
|
|
22
|
+
/** If `cartId` exists, refresh; otherwise create. */
|
|
23
|
+
ensureCart: (createBody?: Record<string, unknown>) => Promise<StoreCart>;
|
|
24
|
+
updateCart: (body: Record<string, unknown>) => Promise<StoreCart>;
|
|
25
|
+
addLineItem: (item: MedusaAddLineItemBody) => Promise<StoreCart>;
|
|
26
|
+
updateLineItem: (lineId: string, body: MedusaUpdateLineItemBody) => Promise<StoreCart>;
|
|
27
|
+
removeLineItem: (lineId: string) => Promise<StoreCart>;
|
|
28
|
+
applyPromotions: (codes: string[]) => Promise<StoreCart>;
|
|
29
|
+
removePromotions: (codes: string[]) => Promise<StoreCart>;
|
|
30
|
+
clearError: () => void;
|
|
31
|
+
}
|
|
32
|
+
export declare function useCart(options: UseCartOptions): UseCartResult;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { useCart, type UseCartOptions, type UseCartResult } from './hooks/useCart';
|
|
2
|
+
export { MEDUSA_STORE_CART_DEFAULT_FIELDS, medusaCartAddLineItem, medusaCartApplyPromotions, medusaCartCreate, medusaCartRemoveLineItem, medusaCartRemovePromotions, medusaCartRetrieve, medusaCartUpdate, medusaCartUpdateLineItem, type MedusaAddLineItemBody, type MedusaStoreCartClientOptions, type MedusaUpdateLineItemBody, type StoreCart, type StoreCartLineItem, type StoreCartPromotion, type StoreCartResponse, } from '../../medusa-services/cart';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export interface MedusaStoreCartClientOptions {
|
|
2
|
+
backendUrl: string;
|
|
3
|
+
publishableApiKey: string;
|
|
4
|
+
/**
|
|
5
|
+
* Send cookies (e.g. `_medusa_cart_id`, customer session) on cross-origin requests.
|
|
6
|
+
* @default "include"
|
|
7
|
+
*/
|
|
8
|
+
credentials?: RequestCredentials;
|
|
9
|
+
/** Optional Bearer JWT (e.g. logged-in customer). */
|
|
10
|
+
authorization?: string;
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
/** Default `fields` for cart detail pages (tweak per backend / performance). */
|
|
14
|
+
export declare const MEDUSA_STORE_CART_DEFAULT_FIELDS = "*items,*items.variant,*items.variant.product,*items.product,*promotions,*region";
|
|
15
|
+
export interface StoreCartPromotion {
|
|
16
|
+
id: string;
|
|
17
|
+
code?: string | null;
|
|
18
|
+
is_automatic?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface StoreCartLineItem {
|
|
21
|
+
id: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
quantity: number;
|
|
24
|
+
variant_id?: string | null;
|
|
25
|
+
product_id?: string | null;
|
|
26
|
+
unit_price?: number;
|
|
27
|
+
subtotal?: number;
|
|
28
|
+
total?: number;
|
|
29
|
+
thumbnail?: string | null;
|
|
30
|
+
variant?: Record<string, unknown> | null;
|
|
31
|
+
product?: Record<string, unknown> | null;
|
|
32
|
+
}
|
|
33
|
+
export interface StoreCart {
|
|
34
|
+
id: string;
|
|
35
|
+
currency_code: string;
|
|
36
|
+
region_id?: string | null;
|
|
37
|
+
email?: string | null;
|
|
38
|
+
items?: StoreCartLineItem[];
|
|
39
|
+
subtotal?: number;
|
|
40
|
+
item_subtotal?: number;
|
|
41
|
+
discount_total?: number;
|
|
42
|
+
shipping_total?: number;
|
|
43
|
+
tax_total?: number;
|
|
44
|
+
total?: number;
|
|
45
|
+
promotions?: StoreCartPromotion[];
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}
|
|
48
|
+
export interface StoreCartResponse {
|
|
49
|
+
cart: StoreCart;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* POST /store/carts — create a new cart (response includes `cart.id`; cookie may be set by backend).
|
|
53
|
+
*/
|
|
54
|
+
export declare function medusaCartCreate(options: MedusaStoreCartClientOptions, body?: Record<string, unknown>): Promise<StoreCartResponse>;
|
|
55
|
+
/**
|
|
56
|
+
* GET /store/carts/:id
|
|
57
|
+
*/
|
|
58
|
+
export declare function medusaCartRetrieve(cartId: string, options: MedusaStoreCartClientOptions, query?: {
|
|
59
|
+
fields?: string;
|
|
60
|
+
}): Promise<StoreCartResponse>;
|
|
61
|
+
/**
|
|
62
|
+
* POST /store/carts/:id — update cart (region, email, metadata, etc.).
|
|
63
|
+
*/
|
|
64
|
+
export declare function medusaCartUpdate(cartId: string, body: Record<string, unknown>, options: MedusaStoreCartClientOptions, query?: {
|
|
65
|
+
fields?: string;
|
|
66
|
+
}): Promise<StoreCartResponse>;
|
|
67
|
+
export interface MedusaAddLineItemBody {
|
|
68
|
+
variant_id: string;
|
|
69
|
+
quantity: number;
|
|
70
|
+
metadata?: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* POST /store/carts/:id/line-items
|
|
74
|
+
*/
|
|
75
|
+
export declare function medusaCartAddLineItem(cartId: string, item: MedusaAddLineItemBody, options: MedusaStoreCartClientOptions, query?: {
|
|
76
|
+
fields?: string;
|
|
77
|
+
}): Promise<StoreCartResponse>;
|
|
78
|
+
export interface MedusaUpdateLineItemBody {
|
|
79
|
+
quantity?: number;
|
|
80
|
+
variant_id?: string;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* POST /store/carts/:id/line-items/:line_id
|
|
85
|
+
*/
|
|
86
|
+
export declare function medusaCartUpdateLineItem(cartId: string, lineId: string, body: MedusaUpdateLineItemBody, options: MedusaStoreCartClientOptions, query?: {
|
|
87
|
+
fields?: string;
|
|
88
|
+
}): Promise<StoreCartResponse>;
|
|
89
|
+
/**
|
|
90
|
+
* DELETE /store/carts/:id/line-items/:line_id
|
|
91
|
+
*/
|
|
92
|
+
export declare function medusaCartRemoveLineItem(cartId: string, lineId: string, options: MedusaStoreCartClientOptions, query?: {
|
|
93
|
+
fields?: string;
|
|
94
|
+
}): Promise<StoreCartResponse>;
|
|
95
|
+
/**
|
|
96
|
+
* POST /store/carts/:id/promotions — apply promotion codes.
|
|
97
|
+
*/
|
|
98
|
+
export declare function medusaCartApplyPromotions(cartId: string, promoCodes: string[], options: MedusaStoreCartClientOptions, query?: {
|
|
99
|
+
fields?: string;
|
|
100
|
+
}): Promise<StoreCartResponse>;
|
|
101
|
+
/**
|
|
102
|
+
* DELETE /store/carts/:id/promotions — remove promotion codes (body: promo_codes).
|
|
103
|
+
*/
|
|
104
|
+
export declare function medusaCartRemovePromotions(cartId: string, promoCodes: string[], options: MedusaStoreCartClientOptions, query?: {
|
|
105
|
+
fields?: string;
|
|
106
|
+
}): Promise<StoreCartResponse>;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { useRef as y, useState as b, useCallback as u, useEffect as j, useMemo as F } from "react";
|
|
2
|
+
const B = "*items,*items.variant,*items.variant.product,*items.product,*promotions,*region";
|
|
3
|
+
function K(e) {
|
|
4
|
+
return e.replace(/\/$/, "");
|
|
5
|
+
}
|
|
6
|
+
function G(e) {
|
|
7
|
+
const t = e.trim();
|
|
8
|
+
return t.toLowerCase().startsWith("bearer ") ? t : `Bearer ${t}`;
|
|
9
|
+
}
|
|
10
|
+
function H(e) {
|
|
11
|
+
const t = {
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
"x-publishable-api-key": e.publishableApiKey,
|
|
14
|
+
...e.headers
|
|
15
|
+
};
|
|
16
|
+
return e.authorization && (t.Authorization = G(e.authorization)), t;
|
|
17
|
+
}
|
|
18
|
+
async function Q(e) {
|
|
19
|
+
const t = await e.json().catch(() => ({}));
|
|
20
|
+
return typeof t.message == "string" ? t.message : typeof t.error == "string" ? t.error : `Cart request failed (${e.status})`;
|
|
21
|
+
}
|
|
22
|
+
async function h(e, t, s) {
|
|
23
|
+
const o = `${K(t.backendUrl)}/store/carts${e}`, i = t.credentials ?? "include";
|
|
24
|
+
return fetch(o, {
|
|
25
|
+
...s,
|
|
26
|
+
credentials: i,
|
|
27
|
+
headers: {
|
|
28
|
+
...H(t),
|
|
29
|
+
...s.headers
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async function C(e) {
|
|
34
|
+
if (!e.ok)
|
|
35
|
+
throw new Error(await Q(e));
|
|
36
|
+
return e.json();
|
|
37
|
+
}
|
|
38
|
+
function g(e) {
|
|
39
|
+
const t = (e == null ? void 0 : e.trim()) || B, s = new URLSearchParams();
|
|
40
|
+
return s.set("fields", t), `?${s.toString()}`;
|
|
41
|
+
}
|
|
42
|
+
async function W(e, t) {
|
|
43
|
+
const s = await h("", e, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
body: JSON.stringify(t ?? {})
|
|
46
|
+
});
|
|
47
|
+
return C(s);
|
|
48
|
+
}
|
|
49
|
+
async function R(e, t, s) {
|
|
50
|
+
const o = await h(
|
|
51
|
+
`/${encodeURIComponent(e)}${g(s == null ? void 0 : s.fields)}`,
|
|
52
|
+
t,
|
|
53
|
+
{ method: "GET" }
|
|
54
|
+
);
|
|
55
|
+
return C(o);
|
|
56
|
+
}
|
|
57
|
+
async function V(e, t, s, o) {
|
|
58
|
+
const i = await h(
|
|
59
|
+
`/${encodeURIComponent(e)}${g(o == null ? void 0 : o.fields)}`,
|
|
60
|
+
s,
|
|
61
|
+
{ method: "POST", body: JSON.stringify(t) }
|
|
62
|
+
);
|
|
63
|
+
return C(i);
|
|
64
|
+
}
|
|
65
|
+
async function X(e, t, s, o) {
|
|
66
|
+
const i = await h(
|
|
67
|
+
`/${encodeURIComponent(e)}/line-items${g(o == null ? void 0 : o.fields)}`,
|
|
68
|
+
s,
|
|
69
|
+
{ method: "POST", body: JSON.stringify(t) }
|
|
70
|
+
);
|
|
71
|
+
return C(i);
|
|
72
|
+
}
|
|
73
|
+
async function Y(e, t, s, o, i) {
|
|
74
|
+
const a = await h(
|
|
75
|
+
`/${encodeURIComponent(e)}/line-items/${encodeURIComponent(t)}${g(i == null ? void 0 : i.fields)}`,
|
|
76
|
+
o,
|
|
77
|
+
{ method: "POST", body: JSON.stringify(s) }
|
|
78
|
+
);
|
|
79
|
+
return C(a);
|
|
80
|
+
}
|
|
81
|
+
async function Z(e, t, s, o) {
|
|
82
|
+
const i = await h(
|
|
83
|
+
`/${encodeURIComponent(e)}/line-items/${encodeURIComponent(t)}${g(o == null ? void 0 : o.fields)}`,
|
|
84
|
+
s,
|
|
85
|
+
{ method: "DELETE" }
|
|
86
|
+
);
|
|
87
|
+
return C(i);
|
|
88
|
+
}
|
|
89
|
+
async function q(e, t, s, o) {
|
|
90
|
+
const i = await h(
|
|
91
|
+
`/${encodeURIComponent(e)}/promotions${g(o == null ? void 0 : o.fields)}`,
|
|
92
|
+
s,
|
|
93
|
+
{ method: "POST", body: JSON.stringify({ promo_codes: t }) }
|
|
94
|
+
);
|
|
95
|
+
return C(i);
|
|
96
|
+
}
|
|
97
|
+
async function tt(e, t, s, o) {
|
|
98
|
+
const i = await h(
|
|
99
|
+
`/${encodeURIComponent(e)}/promotions${g(o == null ? void 0 : o.fields)}`,
|
|
100
|
+
s,
|
|
101
|
+
{ method: "DELETE", body: JSON.stringify({ promo_codes: t }) }
|
|
102
|
+
);
|
|
103
|
+
return C(i);
|
|
104
|
+
}
|
|
105
|
+
function et(e) {
|
|
106
|
+
const {
|
|
107
|
+
backendUrl: t,
|
|
108
|
+
publishableApiKey: s,
|
|
109
|
+
credentials: o,
|
|
110
|
+
authorization: i,
|
|
111
|
+
headers: a
|
|
112
|
+
} = e;
|
|
113
|
+
return F(
|
|
114
|
+
() => ({
|
|
115
|
+
backendUrl: t,
|
|
116
|
+
publishableApiKey: s,
|
|
117
|
+
credentials: o,
|
|
118
|
+
authorization: i,
|
|
119
|
+
headers: a
|
|
120
|
+
}),
|
|
121
|
+
[t, s, o, i, a]
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
function rt(e) {
|
|
125
|
+
const {
|
|
126
|
+
cartId: t,
|
|
127
|
+
onCartIdChange: s,
|
|
128
|
+
fields: o,
|
|
129
|
+
enabled: i = !0
|
|
130
|
+
} = e, a = et(e), $ = y(s);
|
|
131
|
+
$.current = s;
|
|
132
|
+
const [U, m] = b(null), [T, E] = b(!1), [v, w] = b(!1), [A, p] = b(null), O = y(null);
|
|
133
|
+
O.current = U;
|
|
134
|
+
const L = y(o);
|
|
135
|
+
L.current = o;
|
|
136
|
+
const l = u(() => ({ fields: L.current }), []), f = u((r) => {
|
|
137
|
+
const n = r instanceof Error ? r.message : "Cart request failed";
|
|
138
|
+
throw p(n), r;
|
|
139
|
+
}, []), S = u(() => {
|
|
140
|
+
var n;
|
|
141
|
+
if (t && String(t).trim() !== "") return t;
|
|
142
|
+
const r = (n = O.current) == null ? void 0 : n.id;
|
|
143
|
+
return r && String(r).trim() !== "" ? r : null;
|
|
144
|
+
}, [t]), P = u(async () => {
|
|
145
|
+
const r = S();
|
|
146
|
+
if (!r)
|
|
147
|
+
return m(null), null;
|
|
148
|
+
E(!0), p(null);
|
|
149
|
+
try {
|
|
150
|
+
const { cart: n } = await R(r, a, l());
|
|
151
|
+
return m(n), n;
|
|
152
|
+
} catch (n) {
|
|
153
|
+
return f(n), null;
|
|
154
|
+
} finally {
|
|
155
|
+
E(!1);
|
|
156
|
+
}
|
|
157
|
+
}, [a, f, l, S]), d = u(
|
|
158
|
+
async (r) => {
|
|
159
|
+
const n = S();
|
|
160
|
+
if (!n) {
|
|
161
|
+
const c = new Error("No cart id: call createCart or ensureCart first.");
|
|
162
|
+
throw f(c), c;
|
|
163
|
+
}
|
|
164
|
+
w(!0), p(null);
|
|
165
|
+
try {
|
|
166
|
+
const { cart: c } = await r(n);
|
|
167
|
+
return m(c), c;
|
|
168
|
+
} catch (c) {
|
|
169
|
+
throw f(c), c;
|
|
170
|
+
} finally {
|
|
171
|
+
w(!1);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
[f, S]
|
|
175
|
+
), I = u(
|
|
176
|
+
async (r) => {
|
|
177
|
+
var n;
|
|
178
|
+
w(!0), p(null);
|
|
179
|
+
try {
|
|
180
|
+
const { cart: c } = await W(a, r);
|
|
181
|
+
return m(c), (n = $.current) == null || n.call($, c.id), c;
|
|
182
|
+
} catch (c) {
|
|
183
|
+
throw f(c), c;
|
|
184
|
+
} finally {
|
|
185
|
+
w(!1);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
[a, f]
|
|
189
|
+
), x = u(
|
|
190
|
+
async (r) => {
|
|
191
|
+
const n = S();
|
|
192
|
+
if (n) {
|
|
193
|
+
w(!0), p(null);
|
|
194
|
+
try {
|
|
195
|
+
const { cart: c } = await R(n, a, l());
|
|
196
|
+
return m(c), c;
|
|
197
|
+
} catch (c) {
|
|
198
|
+
throw f(c), c;
|
|
199
|
+
} finally {
|
|
200
|
+
w(!1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return I(r);
|
|
204
|
+
},
|
|
205
|
+
[a, I, f, l, S]
|
|
206
|
+
), J = u(
|
|
207
|
+
(r) => d((n) => V(n, r, a, l())),
|
|
208
|
+
[a, l, d]
|
|
209
|
+
), N = u(
|
|
210
|
+
(r) => d((n) => X(n, r, a, l())),
|
|
211
|
+
[a, l, d]
|
|
212
|
+
), k = u(
|
|
213
|
+
(r, n) => d(
|
|
214
|
+
(c) => Y(c, r, n, a, l())
|
|
215
|
+
),
|
|
216
|
+
[a, l, d]
|
|
217
|
+
), z = u(
|
|
218
|
+
(r) => d((n) => Z(n, r, a, l())),
|
|
219
|
+
[a, l, d]
|
|
220
|
+
), _ = u(
|
|
221
|
+
(r) => d((n) => q(n, r, a, l())),
|
|
222
|
+
[a, l, d]
|
|
223
|
+
), D = u(
|
|
224
|
+
(r) => d(
|
|
225
|
+
(n) => tt(n, r, a, l())
|
|
226
|
+
),
|
|
227
|
+
[a, l, d]
|
|
228
|
+
);
|
|
229
|
+
j(() => {
|
|
230
|
+
if (!i) return;
|
|
231
|
+
if (!t) {
|
|
232
|
+
m(null);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
let r = !1;
|
|
236
|
+
return E(!0), p(null), R(t, a, l()).then(({ cart: n }) => {
|
|
237
|
+
r || m(n);
|
|
238
|
+
}).catch((n) => {
|
|
239
|
+
r || (p(n instanceof Error ? n.message : "Cart request failed"), m(null));
|
|
240
|
+
}).finally(() => {
|
|
241
|
+
r || E(!1);
|
|
242
|
+
}), () => {
|
|
243
|
+
r = !0;
|
|
244
|
+
};
|
|
245
|
+
}, [t, a, i, l]);
|
|
246
|
+
const M = u(() => p(null), []);
|
|
247
|
+
return {
|
|
248
|
+
cart: U,
|
|
249
|
+
loading: T,
|
|
250
|
+
mutating: v,
|
|
251
|
+
error: A,
|
|
252
|
+
refresh: P,
|
|
253
|
+
createCart: I,
|
|
254
|
+
ensureCart: x,
|
|
255
|
+
updateCart: J,
|
|
256
|
+
addLineItem: N,
|
|
257
|
+
updateLineItem: k,
|
|
258
|
+
removeLineItem: z,
|
|
259
|
+
applyPromotions: _,
|
|
260
|
+
removePromotions: D,
|
|
261
|
+
clearError: M
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
B as MEDUSA_STORE_CART_DEFAULT_FIELDS,
|
|
266
|
+
X as medusaCartAddLineItem,
|
|
267
|
+
q as medusaCartApplyPromotions,
|
|
268
|
+
W as medusaCartCreate,
|
|
269
|
+
Z as medusaCartRemoveLineItem,
|
|
270
|
+
tt as medusaCartRemovePromotions,
|
|
271
|
+
R as medusaCartRetrieve,
|
|
272
|
+
V as medusaCartUpdate,
|
|
273
|
+
Y as medusaCartUpdateLineItem,
|
|
274
|
+
rt as useCart
|
|
275
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(d,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],o):(d=typeof globalThis<"u"?globalThis:d||self,o(d.MedusaCartLogic={},d.React))})(this,function(d,o){"use strict";const $="*items,*items.variant,*items.variant.product,*items.product,*promotions,*region";function M(e){return e.replace(/\/$/,"")}function J(e){const t=e.trim();return t.toLowerCase().startsWith("bearer ")?t:`Bearer ${t}`}function N(e){const t={"Content-Type":"application/json","x-publishable-api-key":e.publishableApiKey,...e.headers};return e.authorization&&(t.Authorization=J(e.authorization)),t}async function z(e){const t=await e.json().catch(()=>({}));return typeof t.message=="string"?t.message:typeof t.error=="string"?t.error:`Cart request failed (${e.status})`}async function C(e,t,r){const s=`${M(t.backendUrl)}/store/carts${e}`,l=t.credentials??"include";return fetch(s,{...r,credentials:l,headers:{...N(t),...r.headers}})}async function h(e){if(!e.ok)throw new Error(await z(e));return e.json()}function S(e){const t=(e==null?void 0:e.trim())||$,r=new URLSearchParams;return r.set("fields",t),`?${r.toString()}`}async function U(e,t){const r=await C("",e,{method:"POST",body:JSON.stringify(t??{})});return h(r)}async function R(e,t,r){const s=await C(`/${encodeURIComponent(e)}${S(r==null?void 0:r.fields)}`,t,{method:"GET"});return h(s)}async function k(e,t,r,s){const l=await C(`/${encodeURIComponent(e)}${S(s==null?void 0:s.fields)}`,r,{method:"POST",body:JSON.stringify(t)});return h(l)}async function L(e,t,r,s){const l=await C(`/${encodeURIComponent(e)}/line-items${S(s==null?void 0:s.fields)}`,r,{method:"POST",body:JSON.stringify(t)});return h(l)}async function T(e,t,r,s,l){const i=await C(`/${encodeURIComponent(e)}/line-items/${encodeURIComponent(t)}${S(l==null?void 0:l.fields)}`,s,{method:"POST",body:JSON.stringify(r)});return h(i)}async function O(e,t,r,s){const l=await C(`/${encodeURIComponent(e)}/line-items/${encodeURIComponent(t)}${S(s==null?void 0:s.fields)}`,r,{method:"DELETE"});return h(l)}async function A(e,t,r,s){const l=await C(`/${encodeURIComponent(e)}/promotions${S(s==null?void 0:s.fields)}`,r,{method:"POST",body:JSON.stringify({promo_codes:t})});return h(l)}async function v(e,t,r,s){const l=await C(`/${encodeURIComponent(e)}/promotions${S(s==null?void 0:s.fields)}`,r,{method:"DELETE",body:JSON.stringify({promo_codes:t})});return h(l)}function j(e){const{backendUrl:t,publishableApiKey:r,credentials:s,authorization:l,headers:i}=e;return o.useMemo(()=>({backendUrl:t,publishableApiKey:r,credentials:s,authorization:l,headers:i}),[t,r,s,l,i])}function F(e){const{cartId:t,onCartIdChange:r,fields:s,enabled:l=!0}=e,i=j(e),E=o.useRef(r);E.current=r;const[P,p]=o.useState(null),[B,y]=o.useState(!1),[K,g]=o.useState(!1),[G,b]=o.useState(null),_=o.useRef(null);_.current=P;const D=o.useRef(s);D.current=s;const u=o.useCallback(()=>({fields:D.current}),[]),f=o.useCallback(a=>{const n=a instanceof Error?a.message:"Cart request failed";throw b(n),a},[]),w=o.useCallback(()=>{var n;if(t&&String(t).trim()!=="")return t;const a=(n=_.current)==null?void 0:n.id;return a&&String(a).trim()!==""?a:null},[t]),H=o.useCallback(async()=>{const a=w();if(!a)return p(null),null;y(!0),b(null);try{const{cart:n}=await R(a,i,u());return p(n),n}catch(n){return f(n),null}finally{y(!1)}},[i,f,u,w]),m=o.useCallback(async a=>{const n=w();if(!n){const c=new Error("No cart id: call createCart or ensureCart first.");throw f(c),c}g(!0),b(null);try{const{cart:c}=await a(n);return p(c),c}catch(c){throw f(c),c}finally{g(!1)}},[f,w]),I=o.useCallback(async a=>{var n;g(!0),b(null);try{const{cart:c}=await U(i,a);return p(c),(n=E.current)==null||n.call(E,c.id),c}catch(c){throw f(c),c}finally{g(!1)}},[i,f]),Q=o.useCallback(async a=>{const n=w();if(n){g(!0),b(null);try{const{cart:c}=await R(n,i,u());return p(c),c}catch(c){throw f(c),c}finally{g(!1)}}return I(a)},[i,I,f,u,w]),W=o.useCallback(a=>m(n=>k(n,a,i,u())),[i,u,m]),V=o.useCallback(a=>m(n=>L(n,a,i,u())),[i,u,m]),X=o.useCallback((a,n)=>m(c=>T(c,a,n,i,u())),[i,u,m]),Y=o.useCallback(a=>m(n=>O(n,a,i,u())),[i,u,m]),Z=o.useCallback(a=>m(n=>A(n,a,i,u())),[i,u,m]),x=o.useCallback(a=>m(n=>v(n,a,i,u())),[i,u,m]);o.useEffect(()=>{if(!l)return;if(!t){p(null);return}let a=!1;return y(!0),b(null),R(t,i,u()).then(({cart:n})=>{a||p(n)}).catch(n=>{a||(b(n instanceof Error?n.message:"Cart request failed"),p(null))}).finally(()=>{a||y(!1)}),()=>{a=!0}},[t,i,l,u]);const q=o.useCallback(()=>b(null),[]);return{cart:P,loading:B,mutating:K,error:G,refresh:H,createCart:I,ensureCart:Q,updateCart:W,addLineItem:V,updateLineItem:X,removeLineItem:Y,applyPromotions:Z,removePromotions:x,clearError:q}}d.MEDUSA_STORE_CART_DEFAULT_FIELDS=$,d.medusaCartAddLineItem=L,d.medusaCartApplyPromotions=A,d.medusaCartCreate=U,d.medusaCartRemoveLineItem=O,d.medusaCartRemovePromotions=v,d.medusaCartRetrieve=R,d.medusaCartUpdate=k,d.medusaCartUpdateLineItem=T,d.useCart=F,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "medusa-cart-logic",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React hook and helpers for Medusa Store cart (line items, promotions, cookie credentials).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/ui-library.umd.cjs",
|
|
7
|
+
"module": "./dist/ui-library.js",
|
|
8
|
+
"types": "./dist/cart-logic/src/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/ui-library.js",
|
|
12
|
+
"require": "./dist/ui-library.umd.cjs",
|
|
13
|
+
"types": "./dist/cart-logic/src/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "tsc && vite build",
|
|
22
|
+
"preview": "vite preview"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
26
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"@types/react": "^18.0.0 || ^19.0.0",
|
|
31
|
+
"@types/react-dom": "^18.0.0 || ^19.0.0",
|
|
32
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
33
|
+
"react": "^19.0.0",
|
|
34
|
+
"react-dom": "^19.0.0",
|
|
35
|
+
"typescript": "^5.0.0",
|
|
36
|
+
"vite": "^5.0.0",
|
|
37
|
+
"vite-plugin-dts": "^3.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|