medusa-storefront-data 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.
Files changed (105) hide show
  1. package/dist/config.d.ts +3 -0
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +31 -0
  4. package/dist/cookies.d.ts +23 -0
  5. package/dist/cookies.d.ts.map +1 -0
  6. package/dist/cookies.js +140 -0
  7. package/dist/server/cart.d.ts +92 -0
  8. package/dist/server/cart.d.ts.map +1 -0
  9. package/dist/server/cart.js +827 -0
  10. package/dist/server/categories.d.ts +3 -0
  11. package/dist/server/categories.d.ts.map +1 -0
  12. package/dist/server/categories.js +71 -0
  13. package/dist/server/collections.d.ts +8 -0
  14. package/dist/server/collections.d.ts.map +1 -0
  15. package/dist/server/collections.js +84 -0
  16. package/dist/server/customer-registration.d.ts +142 -0
  17. package/dist/server/customer-registration.d.ts.map +1 -0
  18. package/dist/server/customer-registration.js +295 -0
  19. package/dist/server/customer.d.ts +48 -0
  20. package/dist/server/customer.d.ts.map +1 -0
  21. package/dist/server/customer.js +462 -0
  22. package/dist/server/dynamic-config.d.ts +125 -0
  23. package/dist/server/dynamic-config.d.ts.map +1 -0
  24. package/dist/server/dynamic-config.js +263 -0
  25. package/dist/server/fulfillment.d.ts +4 -0
  26. package/dist/server/fulfillment.d.ts.map +1 -0
  27. package/dist/server/fulfillment.js +72 -0
  28. package/dist/server/guest.d.ts +109 -0
  29. package/dist/server/guest.d.ts.map +1 -0
  30. package/dist/server/guest.js +304 -0
  31. package/dist/server/index.d.ts +21 -0
  32. package/dist/server/index.d.ts.map +1 -0
  33. package/dist/server/index.js +20 -0
  34. package/dist/server/locale-actions.d.ts +14 -0
  35. package/dist/server/locale-actions.d.ts.map +1 -0
  36. package/dist/server/locale-actions.js +63 -0
  37. package/dist/server/locales.d.ts +10 -0
  38. package/dist/server/locales.d.ts.map +1 -0
  39. package/dist/server/locales.js +20 -0
  40. package/dist/server/notifications.d.ts +2 -0
  41. package/dist/server/notifications.d.ts.map +1 -0
  42. package/dist/server/notifications.js +20 -0
  43. package/dist/server/onboarding.d.ts +2 -0
  44. package/dist/server/onboarding.d.ts.map +1 -0
  45. package/dist/server/onboarding.js +8 -0
  46. package/dist/server/orders.d.ts +69 -0
  47. package/dist/server/orders.d.ts.map +1 -0
  48. package/dist/server/orders.js +371 -0
  49. package/dist/server/payment-details.d.ts +5 -0
  50. package/dist/server/payment-details.d.ts.map +1 -0
  51. package/dist/server/payment-details.js +53 -0
  52. package/dist/server/payment.d.ts +2 -0
  53. package/dist/server/payment.d.ts.map +1 -0
  54. package/dist/server/payment.js +25 -0
  55. package/dist/server/products.d.ts +58 -0
  56. package/dist/server/products.d.ts.map +1 -0
  57. package/dist/server/products.js +285 -0
  58. package/dist/server/regions.d.ts +5 -0
  59. package/dist/server/regions.d.ts.map +1 -0
  60. package/dist/server/regions.js +54 -0
  61. package/dist/server/returns.d.ts +29 -0
  62. package/dist/server/returns.d.ts.map +1 -0
  63. package/dist/server/returns.js +236 -0
  64. package/dist/server/swaps.d.ts +14 -0
  65. package/dist/server/swaps.d.ts.map +1 -0
  66. package/dist/server/swaps.js +123 -0
  67. package/dist/server/variants.d.ts +3 -0
  68. package/dist/server/variants.d.ts.map +1 -0
  69. package/dist/server/variants.js +26 -0
  70. package/dist/util/get-locale-header.d.ts +4 -0
  71. package/dist/util/get-locale-header.d.ts.map +1 -0
  72. package/dist/util/get-locale-header.js +7 -0
  73. package/dist/util/medusa-error.d.ts +2 -0
  74. package/dist/util/medusa-error.d.ts.map +1 -0
  75. package/dist/util/medusa-error.js +18 -0
  76. package/package.json +152 -0
  77. package/src/config.ts +39 -0
  78. package/src/cookies.ts +171 -0
  79. package/src/middleware.ts +2 -0
  80. package/src/server/cart.ts +1054 -0
  81. package/src/server/categories.ts +94 -0
  82. package/src/server/collections.ts +113 -0
  83. package/src/server/customer-registration.ts +349 -0
  84. package/src/server/customer.ts +581 -0
  85. package/src/server/dynamic-config.ts +403 -0
  86. package/src/server/fulfillment.ts +97 -0
  87. package/src/server/guest.ts +333 -0
  88. package/src/server/index.ts +21 -0
  89. package/src/server/locale-actions.ts +74 -0
  90. package/src/server/locales.ts +28 -0
  91. package/src/server/notifications.ts +22 -0
  92. package/src/server/onboarding.ts +9 -0
  93. package/src/server/orders.ts +467 -0
  94. package/src/server/payment-details.ts +69 -0
  95. package/src/server/payment.ts +35 -0
  96. package/src/server/products.ts +378 -0
  97. package/src/server/regions.ts +66 -0
  98. package/src/server/returns.ts +294 -0
  99. package/src/server/swaps.ts +150 -0
  100. package/src/server/variants.ts +38 -0
  101. package/src/server/wishlist.ts +64 -0
  102. package/src/services/middleware.ts +54 -0
  103. package/src/util/get-locale-header.ts +8 -0
  104. package/src/util/medusa-error.ts +19 -0
  105. package/src/util/sort-products.ts +47 -0
@@ -0,0 +1,294 @@
1
+ "use server"
2
+
3
+ import { sdk } from "../config"
4
+ import { HttpTypes } from "@medusajs/types"
5
+ import { cookies } from "next/headers"
6
+ import { getAuthHeaders, getCacheOptions } from "../cookies"
7
+ import { revalidateTag } from "next/cache"
8
+
9
+ export const listReturnReasons = async () => {
10
+ const headers = {
11
+ ...(await getAuthHeaders()),
12
+ }
13
+
14
+ const next = {
15
+ ...(await getCacheOptions("return_reasons")),
16
+ }
17
+
18
+ return sdk.client
19
+ .fetch<HttpTypes.StoreReturnReasonListResponse>(`/store/return-reasons`, {
20
+ method: "GET",
21
+ headers,
22
+ next,
23
+ cache: "force-cache", // Reasons change rarely
24
+ })
25
+ .then(({ return_reasons }) => return_reasons)
26
+ .catch(() => [])
27
+ }
28
+
29
+ export const listReturnShippingOptions = async (
30
+ cartId: string,
31
+ regionId?: string,
32
+ productIds?: string[]
33
+ ): Promise<HttpTypes.StoreShippingOption[]> => {
34
+ const headers = {
35
+ ...(await getAuthHeaders()),
36
+ }
37
+
38
+ const next = {
39
+ ...(await getCacheOptions("fulfillment")),
40
+ }
41
+
42
+ // Strategy 1: Try with cart_id (preferred as it has context)
43
+ if (cartId) {
44
+ try {
45
+ const { shipping_options } = await sdk.client.fetch<HttpTypes.StoreShippingOptionListResponse>(
46
+ `/store/shipping-options`,
47
+ {
48
+ method: "GET",
49
+ query: {
50
+ cart_id: cartId,
51
+ is_return: true,
52
+ },
53
+ headers,
54
+ next,
55
+ cache: "no-store",
56
+ }
57
+ )
58
+
59
+ if (shipping_options.length > 0) {
60
+ return shipping_options as unknown as HttpTypes.StoreShippingOption[]
61
+ }
62
+ } catch (e) {
63
+ // Fallback strategy
64
+ }
65
+ }
66
+
67
+ // Strategy 2: Fallback - Create ephemeral cart if region_id is available
68
+ // This is required because /store/shipping-options STRICTLY requires a cart_id for return options
69
+ if (regionId) {
70
+ try {
71
+ // Create a temporary cart for this region to get valid shipping options
72
+ const { cart } = await sdk.store.cart.create(
73
+ { region_id: regionId },
74
+ {},
75
+ headers
76
+ )
77
+
78
+ if (cart?.id) {
79
+ const { shipping_options } = await sdk.client.fetch<HttpTypes.StoreShippingOptionListResponse>(
80
+ `/store/shipping-options`,
81
+ {
82
+ method: "GET",
83
+ query: {
84
+ cart_id: cart.id,
85
+ is_return: true,
86
+ },
87
+ headers,
88
+ next,
89
+ cache: "no-store",
90
+ }
91
+ )
92
+ return shipping_options as unknown as HttpTypes.StoreShippingOption[]
93
+ }
94
+ } catch (e) {
95
+ // Silence error
96
+ }
97
+ }
98
+
99
+ return []
100
+ }
101
+
102
+ export const createReturnRequest = async (
103
+ state: {
104
+ success: boolean
105
+ error: string | null
106
+ return: HttpTypes.StoreReturn | null
107
+ },
108
+ formData: FormData
109
+ ): Promise<{
110
+ success: boolean
111
+ error: string | null
112
+ return: HttpTypes.StoreReturn | null
113
+ }> => {
114
+ const orderId = formData.get("order_id") as string
115
+ const returnShippingOptionId = formData.get("return_shipping_option_id") as string
116
+ const itemsJson = formData.get("items") as string
117
+ const note = formData.get("note") as string
118
+ const locationId = formData.get("location_id") as string
119
+
120
+ if (!orderId) return { success: false, error: "Order ID is required", return: null }
121
+ if (!itemsJson) return { success: false, error: "Items are required", return: null }
122
+
123
+ let items: any[] = []
124
+ try {
125
+ items = JSON.parse(itemsJson)
126
+ } catch (e) {
127
+ return { success: false, error: "Invalid items data", return: null }
128
+ }
129
+
130
+ if (items.length === 0) {
131
+ return { success: false, error: "At least one item must be selected", return: null }
132
+ }
133
+
134
+ const headers = {
135
+ ...(await getAuthHeaders()),
136
+ }
137
+
138
+ try {
139
+ // Standard Medusa v2 payload
140
+ const payload: any = {
141
+ order_id: orderId,
142
+ items: items.map((item) => ({
143
+ id: item.id,
144
+ quantity: item.quantity,
145
+ reason_id: item.return_reason_id || undefined,
146
+ note: (note && note.trim().length > 0) ? note : undefined,
147
+ })),
148
+ ...(returnShippingOptionId ? {
149
+ return_shipping: {
150
+ option_id: returnShippingOptionId,
151
+ location_id: locationId || (items[0] as any).location_id || "default_location"
152
+ }
153
+ } : {}),
154
+ // Optional: keep global note if supported
155
+ note: (note && note.trim().length > 0) ? note : "Return request",
156
+ }
157
+
158
+ // Double check location_id is NOT nested incorrectly if already set above
159
+ // The plugin expects return_shipping.location_id
160
+
161
+ // Check for any available guest tokens
162
+ const cookieStore = await cookies()
163
+ const guestToken = cookieStore.get("_medusa_guest_jwt")?.value
164
+ || cookieStore.get("_medusa_guest_token")?.value
165
+ || cookieStore.get("guest_id")?.value
166
+
167
+ const token = cookieStore.get("_medusa_jwt")?.value
168
+
169
+ let returnData: HttpTypes.StoreReturn
170
+
171
+ // If we have a guest token, try the guest specific endpoint
172
+ if (guestToken && !token) {
173
+ try {
174
+ const response = await sdk.client.fetch<any>(
175
+ `/store/guest-orders/${orderId}/returns`,
176
+ {
177
+ method: "POST",
178
+ body: payload,
179
+ headers: {
180
+ ...headers,
181
+ "Authorization": `Bearer ${guestToken}`
182
+ },
183
+ cache: "no-store",
184
+ }
185
+ )
186
+ returnData = response.return || response
187
+ } catch (e) {
188
+ // Fallback to standard if guest endpoint fails
189
+ const response = await sdk.client.fetch<{ return: HttpTypes.StoreReturn }>(
190
+ `/store/returns`,
191
+ {
192
+ method: "POST",
193
+ body: payload,
194
+ headers,
195
+ cache: "no-store",
196
+ }
197
+ )
198
+ returnData = response.return
199
+ }
200
+ } else {
201
+ // Standard flow
202
+ const response = await sdk.client.fetch<{ return: HttpTypes.StoreReturn }>(
203
+ `/store/returns`,
204
+ {
205
+ method: "POST",
206
+ body: payload,
207
+ headers,
208
+ cache: "no-store",
209
+ }
210
+ )
211
+ returnData = response.return
212
+ }
213
+
214
+ revalidateTag("orders")
215
+
216
+ return { success: true, error: null, return: returnData }
217
+ } catch (error: any) {
218
+ return { success: false, error: error.message || "Failed to create return request", return: null }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Link a payment method to a return (for Refund Destination)
224
+ */
225
+ export const updateReturnPayment = async (returnId: string, paymentId: string) => {
226
+ const headers = {
227
+ ...(await getAuthHeaders()),
228
+ }
229
+
230
+ try {
231
+ const response = await sdk.client.fetch<any>(
232
+ `/store/refund-payment-mapping/${returnId}`,
233
+ {
234
+ method: "PUT",
235
+ body: { payment_id: paymentId },
236
+ headers,
237
+ cache: "no-store",
238
+ }
239
+ )
240
+ return { success: true, data: response }
241
+ } catch (error: any) {
242
+ console.error("Update return payment fail:", error)
243
+ return { success: false, error: error.message }
244
+ }
245
+ }
246
+
247
+ /**
248
+ * List returns for an order or customer
249
+ */
250
+ export const listReturns = async (orderId?: string) => {
251
+ const headers = {
252
+ ...(await getAuthHeaders()),
253
+ }
254
+
255
+ // Authenticated flow
256
+ const authHeaders = headers as any
257
+ if (authHeaders.authorization || authHeaders.Authorization) {
258
+ try {
259
+ const response = await sdk.client.fetch<any>(`/store/returns`, {
260
+ method: "GET",
261
+ query: orderId ? { order_id: orderId } : {},
262
+ headers,
263
+ cache: "no-store",
264
+ })
265
+ return response
266
+ } catch (e) {
267
+ return { returns: [], count: 0 }
268
+ }
269
+ }
270
+
271
+ // Guest flow
272
+ if (orderId) {
273
+ const cookieStore = await cookies()
274
+ const guestToken = cookieStore.get("_medusa_guest_jwt")?.value || cookieStore.get("_medusa_guest_token")?.value
275
+
276
+ if (guestToken) {
277
+ try {
278
+ const response = await sdk.client.fetch<any>(`/store/guest-orders/${orderId}/returns`, {
279
+ method: "GET",
280
+ headers: {
281
+ ...headers,
282
+ "Authorization": `Bearer ${guestToken}`
283
+ },
284
+ cache: "no-store",
285
+ })
286
+ return response
287
+ } catch (e) {
288
+ return { returns: [], count: 0 }
289
+ }
290
+ }
291
+ }
292
+
293
+ return { returns: [], count: 0 }
294
+ }
@@ -0,0 +1,150 @@
1
+
2
+ "use server"
3
+
4
+ import { sdk } from "../config"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import { cookies } from "next/headers"
7
+ import { getAuthHeaders } from "../cookies"
8
+ import { revalidateTag } from "next/cache"
9
+
10
+ /**
11
+ * Create an exchange (swap) request.
12
+ * Supports both authenticated customers and guest users.
13
+ */
14
+ export const createSwapRequest = async (
15
+ prevState: any,
16
+ formData: FormData
17
+ ): Promise<{
18
+ success: boolean
19
+ error: string | null
20
+ swap: any | null
21
+ }> => {
22
+ const orderId = formData.get("order_id") as string
23
+ const returnItemsJson = formData.get("return_items") as string
24
+ const newItemsJson = formData.get("new_items") as string
25
+ const reason = formData.get("reason") as string
26
+ const note = formData.get("note") as string
27
+
28
+ if (!orderId) return { success: false, error: "Order ID is required", swap: null }
29
+ if (!returnItemsJson) return { success: false, error: "Items to return are required", swap: null }
30
+ if (!newItemsJson) return { success: false, error: "New items are required", swap: null }
31
+
32
+ let returnItems: any[] = []
33
+ let newItems: any[] = []
34
+ try {
35
+ returnItems = JSON.parse(returnItemsJson)
36
+ newItems = JSON.parse(newItemsJson)
37
+ } catch (e) {
38
+ return { success: false, error: "Invalid items data format", swap: null }
39
+ }
40
+
41
+ if (returnItems.length === 0) {
42
+ return { success: false, error: "At least one item must be selected for return", swap: null }
43
+ }
44
+
45
+ const headers = {
46
+ ...(await getAuthHeaders()),
47
+ }
48
+
49
+ try {
50
+ const payload = {
51
+ order_id: orderId,
52
+ return_items: returnItems,
53
+ new_items: newItems,
54
+ reason: reason || "Size exchange",
55
+ note: note || "Exchange requested from storefront",
56
+ }
57
+
58
+ const cookieStore = await cookies()
59
+ const guestToken = cookieStore.get("_medusa_guest_jwt")?.value || cookieStore.get("_medusa_guest_token")?.value
60
+ const token = cookieStore.get("_medusa_jwt")?.value
61
+
62
+ let swapData: any
63
+
64
+ // Check if we should use the guest endpoint
65
+ if (guestToken && !token) {
66
+
67
+ const response = await sdk.client.fetch<any>(
68
+ `/store/guest-orders/${orderId}/swaps`,
69
+ {
70
+ method: "POST",
71
+ body: {
72
+ return_items: payload.return_items,
73
+ new_items: payload.new_items,
74
+ reason: payload.reason,
75
+ note: payload.note,
76
+ },
77
+ headers: {
78
+ ...headers,
79
+ "Authorization": `Bearer ${guestToken}`
80
+ },
81
+ cache: "no-store",
82
+ }
83
+ )
84
+ swapData = response.swap || response
85
+ } else {
86
+
87
+ const response = await sdk.client.fetch<any>(
88
+ `/store/swaps`,
89
+ {
90
+ method: "POST",
91
+ body: payload,
92
+ headers,
93
+ cache: "no-store",
94
+ }
95
+ )
96
+ swapData = response.swap || response
97
+ }
98
+
99
+ revalidateTag("orders")
100
+
101
+ return { success: true, error: null, swap: swapData }
102
+ } catch (error: any) {
103
+ // Extract error message from response if possible
104
+ let errorMsg = "Failed to create exchange request"
105
+ if (error.response?.data?.message) {
106
+ errorMsg = error.response.data.message
107
+ } else if (error.message) {
108
+ errorMsg = error.message
109
+ }
110
+
111
+ return { success: false, error: errorMsg, swap: null }
112
+ }
113
+ }
114
+
115
+ /**
116
+ * List swaps for an order or customer
117
+ */
118
+ export const listSwaps = async (orderId?: string) => {
119
+ const headers = {
120
+ ...(await getAuthHeaders()),
121
+ }
122
+
123
+ // Authenticated flow
124
+ if ((headers as any).authorization) {
125
+ return sdk.client.fetch<any>(`/store/swaps`, {
126
+ method: "GET",
127
+ query: orderId ? { order_id: orderId } : {},
128
+ headers,
129
+ cache: "no-store",
130
+ })
131
+ }
132
+
133
+ // Guest flow
134
+ if (orderId) {
135
+ const cookieStore = await cookies()
136
+ const guestToken = cookieStore.get("_medusa_guest_jwt")?.value || cookieStore.get("_medusa_guest_token")?.value
137
+ if (guestToken) {
138
+ return sdk.client.fetch<any>(`/store/guest-orders/${orderId}/swaps`, {
139
+ method: "GET",
140
+ headers: {
141
+ ...headers,
142
+ "Authorization": `Bearer ${guestToken}`
143
+ },
144
+ cache: "no-store",
145
+ })
146
+ }
147
+ }
148
+
149
+ return { swaps: [], count: 0 }
150
+ }
@@ -0,0 +1,38 @@
1
+ "use server"
2
+
3
+ import { sdk } from "../config"
4
+ import { HttpTypes } from "@medusajs/types"
5
+
6
+ import { getAuthHeaders, getCacheOptions } from "../cookies"
7
+
8
+ export const retrieveVariant = async (
9
+ variant_id: string
10
+ ): Promise<HttpTypes.StoreProductVariant | null> => {
11
+ const authHeaders = await getAuthHeaders()
12
+
13
+ if (!authHeaders) return null
14
+
15
+ const headers = {
16
+ ...authHeaders,
17
+ }
18
+
19
+ const next = {
20
+ ...(await getCacheOptions("variants")),
21
+ }
22
+
23
+ return await sdk.client
24
+ .fetch<{ variant: HttpTypes.StoreProductVariant }>(
25
+ `/store/product-variants/${variant_id}`,
26
+ {
27
+ method: "GET",
28
+ query: {
29
+ fields: "*images",
30
+ },
31
+ headers,
32
+ next,
33
+ cache: "force-cache",
34
+ }
35
+ )
36
+ .then(({ variant }) => variant)
37
+ .catch(() => null)
38
+ }
@@ -0,0 +1,64 @@
1
+ "use server";
2
+
3
+ import { HttpTypes } from "@medusajs/types";
4
+ import { sdk } from "../config";
5
+ import { getRegion } from "./regions";
6
+ import { fetchWishlist } from "medusa-wishlist-logic/server";
7
+
8
+ /** Wishlist entries enriched with full Medusa product payloads. */
9
+ export async function getWishlist(includeDetails = true, countryCode?: string) {
10
+ const result = await fetchWishlist(includeDetails);
11
+ if (!result.success) {
12
+ return { success: false as const, error: result.error, data: [] as unknown[] };
13
+ }
14
+
15
+ let wishlistItems = result.data as Array<Record<string, unknown>>;
16
+
17
+ if (!includeDetails || wishlistItems.length === 0) {
18
+ return { success: true as const, data: wishlistItems };
19
+ }
20
+
21
+ const productIds = wishlistItems
22
+ .map((item) => {
23
+ const id = item.product_id ?? item.id;
24
+ return typeof id === "string" ? id : null;
25
+ })
26
+ .filter((id): id is string => Boolean(id));
27
+
28
+ if (productIds.length === 0) {
29
+ return { success: true as const, data: wishlistItems };
30
+ }
31
+
32
+ try {
33
+ let regionId: string | undefined;
34
+ if (countryCode) {
35
+ const region = await getRegion(countryCode);
36
+ regionId = region?.id;
37
+ }
38
+
39
+ const productsResponse = await sdk.store.product.list(
40
+ {
41
+ id: productIds,
42
+ fields:
43
+ "*thumbnail,*images,*variants,*variants.options,*options,*options.values,*variants.calculated_price,*variants.inventory_quantity,*variants.manage_inventory,*variants.allow_backorder",
44
+ ...(regionId ? { region_id: regionId } : {}),
45
+ },
46
+ { next: { tags: ["products"] } }
47
+ );
48
+
49
+ const fullProducts = productsResponse.products as HttpTypes.StoreProduct[];
50
+
51
+ wishlistItems = wishlistItems.map((item) => {
52
+ const itemId = (item.product_id ?? item.id) as string;
53
+ const fullProduct = fullProducts.find((p) => p.id === itemId);
54
+ if (fullProduct) {
55
+ return { ...item, product: fullProduct };
56
+ }
57
+ return item;
58
+ });
59
+ } catch {
60
+ // Return API wishlist without enrichment
61
+ }
62
+
63
+ return { success: true as const, data: wishlistItems };
64
+ }
@@ -0,0 +1,54 @@
1
+ /** Edge-safe Medusa store fetches (no Node SDK). */
2
+
3
+ export interface MedusaMiddlewareClientOptions {
4
+ backendUrl: string;
5
+ publishableApiKey: string;
6
+ }
7
+
8
+ function normalizeBackendUrl(backendUrl: string): string {
9
+ return backendUrl.replace(/\/$/, "");
10
+ }
11
+
12
+ export async function medusaListRegions(
13
+ options: MedusaMiddlewareClientOptions,
14
+ init?: RequestInit
15
+ ): Promise<{ regions: unknown[] }> {
16
+ const url = `${normalizeBackendUrl(options.backendUrl)}/store/regions`;
17
+ const response = await fetch(url, {
18
+ ...init,
19
+ headers: {
20
+ "x-publishable-api-key": options.publishableApiKey,
21
+ ...(init?.headers as Record<string, string> | undefined),
22
+ },
23
+ });
24
+
25
+ const json = (await response.json()) as { regions?: unknown[]; message?: string };
26
+ if (!response.ok) {
27
+ throw new Error(json.message || "Failed to fetch regions");
28
+ }
29
+ return { regions: json.regions ?? [] };
30
+ }
31
+
32
+ export async function medusaMergeCarts(
33
+ options: MedusaMiddlewareClientOptions & {
34
+ authorization: string;
35
+ cartId?: string;
36
+ },
37
+ init?: RequestInit
38
+ ): Promise<Response> {
39
+ const url = `${normalizeBackendUrl(options.backendUrl)}/store/carts/merge`;
40
+ return fetch(url, {
41
+ method: "POST",
42
+ ...init,
43
+ headers: {
44
+ "Content-Type": "application/json",
45
+ "x-publishable-api-key": options.publishableApiKey,
46
+ Authorization: options.authorization.startsWith("Bearer ")
47
+ ? options.authorization
48
+ : `Bearer ${options.authorization}`,
49
+ ...(init?.headers as Record<string, string> | undefined),
50
+ },
51
+ body: options.cartId ? JSON.stringify({ cart_id: options.cartId }) : init?.body,
52
+ cache: init?.cache ?? "no-store",
53
+ });
54
+ }
@@ -0,0 +1,8 @@
1
+ import { getLocale } from "../server/locale-actions"
2
+
3
+ export async function getLocaleHeader() {
4
+ const locale = await getLocale()
5
+ return {
6
+ "x-medusa-locale": locale,
7
+ } as const
8
+ }
@@ -0,0 +1,19 @@
1
+ export default function medusaError(error: any): never {
2
+ if (error.response) {
3
+ // The request was made and the server responded with a status code
4
+ // that falls out of the range of 2xx
5
+ const u = new URL(error.config.url, error.config.baseURL)
6
+
7
+
8
+ // Extracting the error message from the response data
9
+ const message = error.response.data.message || error.response.data
10
+
11
+ throw new Error(message.charAt(0).toUpperCase() + message.slice(1) + ".")
12
+ } else if (error.request) {
13
+ // The request was made but no response was received
14
+ throw new Error("No response received: " + error.request)
15
+ } else {
16
+ // Something happened in setting up the request that triggered an Error
17
+ throw new Error("Error setting up the request: " + error.message)
18
+ }
19
+ }
@@ -0,0 +1,47 @@
1
+ import { HttpTypes } from "@medusajs/types";
2
+
3
+ export type SortOptions =
4
+ | "price_asc"
5
+ | "price_desc"
6
+ | "created_at_desc"
7
+ | "created_at_asc"
8
+ | "bestsellers"
9
+ | "created_at";
10
+
11
+ interface MinPricedProduct extends HttpTypes.StoreProduct {
12
+ _minPrice?: number;
13
+ }
14
+
15
+ export function sortProducts(
16
+ products: HttpTypes.StoreProduct[],
17
+ sortBy: SortOptions
18
+ ): HttpTypes.StoreProduct[] {
19
+ const sortedProducts = products as MinPricedProduct[];
20
+
21
+ if (["price_asc", "price_desc"].includes(sortBy)) {
22
+ sortedProducts.forEach((product) => {
23
+ if (product.variants && product.variants.length > 0) {
24
+ product._minPrice = Math.min(
25
+ ...product.variants.map(
26
+ (variant) => variant?.calculated_price?.calculated_amount || 0
27
+ )
28
+ );
29
+ } else {
30
+ product._minPrice = Infinity;
31
+ }
32
+ });
33
+
34
+ sortedProducts.sort((a, b) => {
35
+ const diff = a._minPrice! - b._minPrice!;
36
+ return sortBy === "price_asc" ? diff : -diff;
37
+ });
38
+ }
39
+
40
+ if (sortBy === "created_at") {
41
+ sortedProducts.sort((a, b) => {
42
+ return new Date(b.created_at!).getTime() - new Date(a.created_at!).getTime();
43
+ });
44
+ }
45
+
46
+ return sortedProducts;
47
+ }