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,333 @@
1
+ "use server"
2
+
3
+ import { cookies, headers } from "next/headers"
4
+ import { getGuestAuthHeaders } from "../cookies"
5
+ import { sdk } from "../config"
6
+
7
+ const BACKEND_URL = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000"
8
+ const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
9
+
10
+ /**
11
+ * Compatibility wrapper for sendOTP
12
+ */
13
+ export async function sendOTP(email: string, type: string = "email_verification") {
14
+ const res = await requestGuestOTP(email)
15
+ // The previous project expected a token or success.
16
+ // We return a dummy token if successful because the UI checks for it.
17
+ if (res.success) {
18
+ return { success: true, token: "otp_sent_successfully" }
19
+ }
20
+ return res
21
+ }
22
+
23
+ /**
24
+ * Compatibility wrapper for verifyOTP
25
+ */
26
+ export async function verifyOTP(email: string, token: string, otp: string) {
27
+ // The previous implementation used 3 args, but guest.ts uses 2.
28
+ // We ignore the mid 'token' as per user's working code.
29
+ return await verifyGuestOTP(email, otp)
30
+ }
31
+
32
+ import { retrieveOrder } from "./orders"
33
+
34
+ /**
35
+ * Compatibility wrapper for listGuestOrders
36
+ */
37
+ export async function listGuestOrders(token: string) {
38
+ const res = await getGuestOrders(token)
39
+ if (!res.success) {
40
+ throw new Error(res.error || "Failed to fetch orders")
41
+ }
42
+
43
+ // Fetch full details for each order to ensure latest fulfillment_status is available
44
+ // because the custom list endpoint might not return all fields correctly.
45
+ const fullOrders = await Promise.all(
46
+ (res.orders || []).map(async (order: any) => {
47
+ try {
48
+ return await retrieveOrder(order.id)
49
+ } catch (e) {
50
+ return order
51
+ }
52
+ })
53
+ )
54
+
55
+ return { orders: fullOrders, count: fullOrders.length }
56
+ }
57
+
58
+ export async function requestGuestOTP(email: string) {
59
+ const headers: Record<string, string> = {
60
+ "Content-Type": "application/json",
61
+ }
62
+ if (PUBLISHABLE_KEY) headers["x-publishable-api-key"] = PUBLISHABLE_KEY
63
+
64
+ try {
65
+ const res = await fetch(`${BACKEND_URL}/store/otp/request`, {
66
+ method: "POST",
67
+ headers,
68
+ body: JSON.stringify({ email }),
69
+ cache: "no-store",
70
+ })
71
+
72
+ if (!res.ok) {
73
+ const errorData = await res.json().catch(() => ({}))
74
+ throw new Error(errorData.message || "Failed to send OTP")
75
+ }
76
+ return { success: true }
77
+ } catch (error: any) {
78
+ return { success: false, error: error.message }
79
+ }
80
+ }
81
+
82
+ export async function verifyGuestOTP(email: string, otp: string) {
83
+ const headers: Record<string, string> = {
84
+ "Content-Type": "application/json",
85
+ }
86
+ if (PUBLISHABLE_KEY) headers["x-publishable-api-key"] = PUBLISHABLE_KEY
87
+
88
+ try {
89
+ const res = await fetch(`${BACKEND_URL}/store/otp/verify`, {
90
+ method: "POST",
91
+ headers,
92
+ body: JSON.stringify({ email, otp }),
93
+ cache: "no-store",
94
+ })
95
+
96
+ if (!res.ok) {
97
+ const errorData = await res.json().catch(() => ({}))
98
+ throw new Error(errorData.message || "Failed to verify OTP")
99
+ }
100
+
101
+ const data = await res.json()
102
+
103
+ if (data.token) {
104
+ const cookieStore = await cookies()
105
+ cookieStore.set("_medusa_guest_jwt", data.token, {
106
+ httpOnly: true,
107
+ secure: process.env.NODE_ENV === "production",
108
+ maxAge: 60 * 60 * 24, // 1 day
109
+ path: "/",
110
+ })
111
+ } else {
112
+ throw new Error("No token returned")
113
+ }
114
+
115
+ return { success: true, token: data.token }
116
+ } catch (error: any) {
117
+ return { success: false, error: error.message }
118
+ }
119
+ }
120
+
121
+ export async function logoutGuest() {
122
+ const cookieStore = await cookies()
123
+ cookieStore.set("_medusa_guest_jwt", "", {
124
+ maxAge: -1,
125
+ path: "/",
126
+ })
127
+ return { success: true }
128
+ }
129
+
130
+ export async function getGuestOrders(token?: string) {
131
+ const cookieStore = await cookies()
132
+ const headersStore = await headers()
133
+ const authHeader = headersStore.get("authorization")
134
+ const headerToken = authHeader?.startsWith("Bearer ") ? authHeader.split(" ")[1] : undefined
135
+
136
+ const authToken = token || headerToken || cookieStore.get("_medusa_guest_jwt")?.value
137
+ if (!authToken) return { success: false, error: "Unauthorized" }
138
+
139
+ const requestHeaders: Record<string, string> = {
140
+ "Authorization": `Bearer ${authToken}`
141
+ }
142
+ if (PUBLISHABLE_KEY) requestHeaders["x-publishable-api-key"] = PUBLISHABLE_KEY
143
+
144
+ try {
145
+ const response = await sdk.client.fetch<any>(`/store/guest-orders`, {
146
+ method: "GET",
147
+ headers: requestHeaders,
148
+ query: {
149
+ fields: "id,display_id,status,fulfillment_status,payment_status,created_at,currency_code,total,subtotal,tax_total,shipping_total,discount_total,metadata,email,*items,*items.variant,*items.variant.product"
150
+ },
151
+ cache: "no-store",
152
+ })
153
+
154
+ return { success: true, orders: response.orders || [] }
155
+ } catch (error: any) {
156
+ return { success: false, error: error.message }
157
+ }
158
+ }
159
+
160
+ export async function getGuestOrder(id: string, token?: string) {
161
+ const cookieStore = await cookies()
162
+ const headersStore = await headers()
163
+ const authHeader = headersStore.get("authorization")
164
+ const headerToken = authHeader?.startsWith("Bearer ") ? authHeader.split(" ")[1] : undefined
165
+
166
+ const authToken = token || headerToken || cookieStore.get("_medusa_guest_jwt")?.value
167
+ if (!authToken) return { success: false, error: "Unauthorized" }
168
+
169
+ const requestHeaders: Record<string, string> = {
170
+ "Authorization": `Bearer ${authToken}`
171
+ }
172
+ if (PUBLISHABLE_KEY) requestHeaders["x-publishable-api-key"] = PUBLISHABLE_KEY
173
+
174
+ try {
175
+ const response = await sdk.client.fetch<any>(`/store/orders/${id}`, {
176
+ method: "GET",
177
+ headers: requestHeaders,
178
+ query: {
179
+ fields: "*payment_collections.payments,*items,*items.metadata,*items.variant,*items.variant.images,*items.variant.product,*items.variant.product.thumbnail,*items.variant.product.images,*items.product,*items.product.thumbnail,*items.product.images,*fulfillments,*fulfillments.items,*fulfillments.location_id,*returns,*returns.items,*cart,*shipping_address,*billing_address,*region,*shipping_methods"
180
+ },
181
+ cache: "no-store"
182
+ })
183
+
184
+ if (response.order) {
185
+ return { success: true, order: response.order }
186
+ } else {
187
+ return { success: false, error: "Order not found" }
188
+ }
189
+ } catch (error: any) {
190
+ return { success: false, error: error.message }
191
+ }
192
+ }
193
+
194
+ export async function cancelGuestOrder(orderId: string, token?: string) {
195
+ const authHeaders = await getGuestAuthHeaders()
196
+ const authToken = token || (authHeaders as any).authorization?.split(" ")[1]
197
+
198
+ if (!authToken) return { success: false, error: "Unauthorized" }
199
+
200
+ const requestHeaders: Record<string, string> = {
201
+ "Authorization": `Bearer ${authToken}`
202
+ }
203
+ if (PUBLISHABLE_KEY) requestHeaders["x-publishable-api-key"] = PUBLISHABLE_KEY
204
+
205
+ try {
206
+ const response = await fetch(`${BACKEND_URL}/store/guest-orders/${orderId}/cancel`, {
207
+ method: "POST",
208
+ headers: {
209
+ "Content-Type": "application/json",
210
+ ...requestHeaders
211
+ },
212
+ cache: "no-store",
213
+ })
214
+
215
+ if (!response.ok) {
216
+ const errorData = await response.json().catch(() => ({}))
217
+ throw new Error(errorData.message || "Failed to cancel order")
218
+ }
219
+
220
+ return { success: true }
221
+ } catch (error: any) {
222
+ return { success: false, error: error.message }
223
+ }
224
+ }
225
+
226
+ export async function getGuestOrderInvoice(orderId: string, token?: string) {
227
+ const authHeaders = await getGuestAuthHeaders()
228
+ const authToken = token || (authHeaders as any).authorization?.split(" ")[1]
229
+
230
+ if (!authToken) return { success: false, error: "Unauthorized" }
231
+
232
+ const requestHeaders: Record<string, string> = {
233
+ "Authorization": `Bearer ${authToken}`
234
+ }
235
+ if (PUBLISHABLE_KEY) requestHeaders["x-publishable-api-key"] = PUBLISHABLE_KEY
236
+
237
+ try {
238
+ const response = await fetch(`${BACKEND_URL}/store/guest/invoice/download/${orderId}`, {
239
+ method: "GET",
240
+ headers: requestHeaders,
241
+ cache: "no-store",
242
+ })
243
+
244
+ if (!response.ok) {
245
+ const errorData = await response.json().catch(() => ({}))
246
+ throw new Error(errorData.message || "Failed to get invoice")
247
+ }
248
+
249
+ const contentType = response.headers.get("content-type")
250
+ if (contentType && contentType.includes("application/pdf")) {
251
+ const arrayBuffer = await response.arrayBuffer()
252
+ const base64 = Buffer.from(arrayBuffer).toString('base64')
253
+ return { success: true, type: 'pdf', data: base64 }
254
+ } else {
255
+ const data = await response.json()
256
+ return { success: true, type: 'json', data }
257
+ }
258
+
259
+ } catch (error: any) {
260
+ return { success: false, error: error.message }
261
+ }
262
+ }
263
+
264
+ export async function guestReorder(orderId: string, token?: string) {
265
+ const authHeaders = await getGuestAuthHeaders()
266
+ const authToken = token || (authHeaders as any).authorization?.split(" ")[1]
267
+
268
+ if (!authToken) return { success: false, error: "Unauthorized" }
269
+
270
+ const requestHeaders: Record<string, string> = {
271
+ "Authorization": `Bearer ${authToken}`
272
+ }
273
+ if (PUBLISHABLE_KEY) requestHeaders["x-publishable-api-key"] = PUBLISHABLE_KEY
274
+
275
+ try {
276
+ const response = await fetch(`${BACKEND_URL}/store/guest-orders/${orderId}/reorder`, {
277
+ method: "POST",
278
+ headers: {
279
+ "Content-Type": "application/json",
280
+ ...requestHeaders
281
+ },
282
+ cache: "no-store",
283
+ })
284
+
285
+ if (!response.ok) {
286
+ const errorData = await response.json().catch(() => ({}))
287
+ throw new Error(errorData.message || "Failed to reorder")
288
+ }
289
+
290
+ const data = await response.json()
291
+ return { success: true, data }
292
+ } catch (error: any) {
293
+ return { success: false, error: error.message }
294
+ }
295
+ }
296
+
297
+ export async function createGuestReturn(orderId: string, items: { id: string, quantity: number }[], reason_id?: string, note?: string, token?: string) {
298
+ const authHeaders = await getGuestAuthHeaders()
299
+ const authToken = token || (authHeaders as any).authorization?.split(" ")[1]
300
+
301
+ if (!authToken) return { success: false, error: "Unauthorized" }
302
+
303
+ const requestHeaders: Record<string, string> = {
304
+ "Authorization": `Bearer ${authToken}`
305
+ }
306
+ if (PUBLISHABLE_KEY) requestHeaders["x-publishable-api-key"] = PUBLISHABLE_KEY
307
+
308
+ try {
309
+ const response = await fetch(`${BACKEND_URL}/store/guest-orders/${orderId}/returns`, {
310
+ method: "POST",
311
+ headers: {
312
+ "Content-Type": "application/json",
313
+ ...requestHeaders
314
+ },
315
+ body: JSON.stringify({
316
+ items,
317
+ reason_id,
318
+ note
319
+ }),
320
+ cache: "no-store",
321
+ })
322
+
323
+ if (!response.ok) {
324
+ const errorData = await response.json().catch(() => ({}))
325
+ throw new Error(errorData.message || "Failed to create return request")
326
+ }
327
+
328
+ const data = await response.json()
329
+ return { success: true, data }
330
+ } catch (error: any) {
331
+ return { success: false, error: error.message }
332
+ }
333
+ }
@@ -0,0 +1,21 @@
1
+ export * from "./cart"
2
+ export * from "./categories"
3
+ export * from "./collections"
4
+ export * from "./customer-registration"
5
+ export * from "./customer"
6
+ export * from "./dynamic-config"
7
+ export * from "./fulfillment"
8
+ export * from "./guest"
9
+ export * from "./locale-actions"
10
+ export * from "./locales"
11
+ export * from "./notifications"
12
+ export * from "./onboarding"
13
+ export * from "./orders"
14
+ export * from "./payment-details"
15
+ export * from "./payment"
16
+ export * from "./products"
17
+ export * from "./regions"
18
+ export * from "./returns"
19
+ export * from "./swaps"
20
+ export * from "./variants"
21
+ export * from "./wishlist"
@@ -0,0 +1,74 @@
1
+ "use server"
2
+
3
+ import { sdk } from "../config"
4
+ import { revalidateTag } from "next/cache"
5
+ import { cookies as nextCookies } from "next/headers"
6
+ import { getAuthHeaders, getCacheTag, getCartId } from "../cookies"
7
+
8
+ const LOCALE_COOKIE_NAME = "_medusa_locale"
9
+
10
+ /**
11
+ * Gets the current locale from cookies
12
+ */
13
+ export const getLocale = async (): Promise<string | null> => {
14
+ try {
15
+ const cookies = await nextCookies()
16
+ return cookies.get(LOCALE_COOKIE_NAME)?.value ?? null
17
+ } catch {
18
+ return null
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Sets the locale cookie
24
+ */
25
+ export const setLocaleCookie = async (locale: string) => {
26
+ const cookies = await nextCookies()
27
+ cookies.set(LOCALE_COOKIE_NAME, locale, {
28
+ maxAge: 60 * 60 * 24 * 365, // 1 year
29
+ httpOnly: false, // Allow client-side access
30
+ sameSite: "strict",
31
+ secure: process.env.NODE_ENV === "production",
32
+ })
33
+ }
34
+
35
+ /**
36
+ * Updates the locale preference via SDK and stores in cookie.
37
+ * Also updates the cart with the new locale if one exists.
38
+ */
39
+ export const updateLocale = async (localeCode: string): Promise<string> => {
40
+ await setLocaleCookie(localeCode)
41
+
42
+ // Update cart with the new locale if a cart exists
43
+ const cartId = await getCartId()
44
+ if (cartId) {
45
+ const headers = {
46
+ ...(await getAuthHeaders()),
47
+ }
48
+
49
+ await sdk.store.cart.update(cartId, { locale: localeCode }, {}, headers)
50
+
51
+ const cartCacheTag = await getCacheTag("carts")
52
+ if (cartCacheTag) {
53
+ revalidateTag(cartCacheTag)
54
+ }
55
+ }
56
+
57
+ // Revalidate relevant caches to refresh content
58
+ const productsCacheTag = await getCacheTag("products")
59
+ if (productsCacheTag) {
60
+ revalidateTag(productsCacheTag)
61
+ }
62
+
63
+ const categoriesCacheTag = await getCacheTag("categories")
64
+ if (categoriesCacheTag) {
65
+ revalidateTag(categoriesCacheTag)
66
+ }
67
+
68
+ const collectionsCacheTag = await getCacheTag("collections")
69
+ if (collectionsCacheTag) {
70
+ revalidateTag(collectionsCacheTag)
71
+ }
72
+
73
+ return localeCode
74
+ }
@@ -0,0 +1,28 @@
1
+ "use server"
2
+
3
+ import { sdk } from "../config"
4
+ import { getCacheOptions } from "../cookies"
5
+
6
+ export type Locale = {
7
+ code: string
8
+ name: string
9
+ }
10
+
11
+ /**
12
+ * Fetches available locales from the backend.
13
+ * Returns null if the endpoint returns 404 (locales not configured).
14
+ */
15
+ export const listLocales = async (): Promise<Locale[] | null> => {
16
+ const next = {
17
+ ...(await getCacheOptions("locales")),
18
+ }
19
+
20
+ return sdk.client
21
+ .fetch<{ locales: Locale[] }>(`/store/locales`, {
22
+ method: "GET",
23
+ next,
24
+ cache: "force-cache",
25
+ })
26
+ .then(({ locales }) => locales)
27
+ .catch(() => null)
28
+ }
@@ -0,0 +1,22 @@
1
+ "use server"
2
+
3
+ import { sdk } from "../config"
4
+ import { getAuthHeaders } from "../cookies"
5
+
6
+ export async function registerNotificationToken(token: string, deviceInfo?: any) {
7
+ const headers = (await getAuthHeaders()) as Record<string, string>
8
+
9
+ try {
10
+ const result = await sdk.client.fetch<any>(`/store/notification-tokens`, {
11
+ method: "POST",
12
+ body: {
13
+ token,
14
+ device_info: deviceInfo,
15
+ },
16
+ headers,
17
+ })
18
+ return result
19
+ } catch (error: any) {
20
+ return null
21
+ }
22
+ }
@@ -0,0 +1,9 @@
1
+ "use server"
2
+ import { cookies as nextCookies } from "next/headers"
3
+ import { redirect } from "next/navigation"
4
+
5
+ export async function resetOnboardingState(orderId: string) {
6
+ const cookies = await nextCookies()
7
+ cookies.set("_medusa_onboarding", "false", { maxAge: -1 })
8
+ redirect(`http://localhost:7001/a/orders/${orderId}`)
9
+ }