@spree/next 0.1.2 → 0.2.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.
package/README.md CHANGED
@@ -198,6 +198,32 @@ const orders = await listOrders();
198
198
  const order = await getOrder(orderId);
199
199
  ```
200
200
 
201
+ ### Payment Sessions
202
+
203
+ ```typescript
204
+ import {
205
+ createPaymentSession,
206
+ getPaymentSession,
207
+ updatePaymentSession,
208
+ completePaymentSession,
209
+ } from '@spree/next';
210
+
211
+ // Create a payment session (initializes provider-specific session)
212
+ const session = await createPaymentSession(orderId, { payment_method_id: 'pm_123' });
213
+
214
+ // Access provider data (e.g., Stripe client secret)
215
+ const clientSecret = session.external_data.client_secret;
216
+
217
+ // Get a payment session
218
+ const session = await getPaymentSession(orderId, sessionId);
219
+
220
+ // Update a payment session
221
+ await updatePaymentSession(orderId, sessionId, { amount: '50.00' });
222
+
223
+ // Complete a payment session (confirms payment with provider)
224
+ await completePaymentSession(orderId, sessionId, { session_result: 'success' });
225
+ ```
226
+
201
227
  ### Credit Cards & Gift Cards
202
228
 
203
229
  ```typescript
@@ -229,6 +255,7 @@ import type {
229
255
  StoreProduct,
230
256
  StoreOrder,
231
257
  StoreLineItem,
258
+ StorePaymentSession,
232
259
  StoreTaxon,
233
260
  PaginatedResponse,
234
261
  SpreeError,
@@ -14,18 +14,18 @@ function initSpreeNext(config) {
14
14
  _config = config;
15
15
  _client = createSpreeClient({
16
16
  baseUrl: config.baseUrl,
17
- apiKey: config.apiKey
17
+ publishableKey: config.publishableKey
18
18
  });
19
19
  }
20
20
  function getClient() {
21
21
  if (!_client) {
22
22
  const baseUrl = process.env.SPREE_API_URL;
23
- const apiKey = process.env.SPREE_API_KEY;
24
- if (baseUrl && apiKey) {
25
- initSpreeNext({ baseUrl, apiKey });
23
+ const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;
24
+ if (baseUrl && publishableKey) {
25
+ initSpreeNext({ baseUrl, publishableKey });
26
26
  } else {
27
27
  throw new Error(
28
- "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables."
28
+ "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables."
29
29
  );
30
30
  }
31
31
  }
@@ -84,7 +84,7 @@ async function getAuthOptions() {
84
84
  const now = Math.floor(Date.now() / 1e3);
85
85
  if (exp && exp - now < 3600) {
86
86
  try {
87
- const refreshed = await getClient().auth.refresh({ token });
87
+ const refreshed = await getClient().store.auth.refresh({ token });
88
88
  await setAccessToken(refreshed.token);
89
89
  return { token: refreshed.token };
90
90
  } catch {
@@ -104,7 +104,7 @@ async function withAuthRefresh(fn) {
104
104
  } catch (error) {
105
105
  if (error instanceof SpreeError && error.status === 401) {
106
106
  try {
107
- const refreshed = await getClient().auth.refresh({ token: options.token });
107
+ const refreshed = await getClient().store.auth.refresh({ token: options.token });
108
108
  await setAccessToken(refreshed.token);
109
109
  return await fn({ token: refreshed.token });
110
110
  } catch {
@@ -119,31 +119,31 @@ async function withAuthRefresh(fn) {
119
119
  // src/actions/addresses.ts
120
120
  async function listAddresses() {
121
121
  return withAuthRefresh(async (options) => {
122
- return getClient().customer.addresses.list(void 0, options);
122
+ return getClient().store.customer.addresses.list(void 0, options);
123
123
  });
124
124
  }
125
125
  async function getAddress(id) {
126
126
  return withAuthRefresh(async (options) => {
127
- return getClient().customer.addresses.get(id, options);
127
+ return getClient().store.customer.addresses.get(id, options);
128
128
  });
129
129
  }
130
130
  async function createAddress(params) {
131
131
  const result = await withAuthRefresh(async (options) => {
132
- return getClient().customer.addresses.create(params, options);
132
+ return getClient().store.customer.addresses.create(params, options);
133
133
  });
134
134
  revalidateTag("addresses");
135
135
  return result;
136
136
  }
137
137
  async function updateAddress(id, params) {
138
138
  const result = await withAuthRefresh(async (options) => {
139
- return getClient().customer.addresses.update(id, params, options);
139
+ return getClient().store.customer.addresses.update(id, params, options);
140
140
  });
141
141
  revalidateTag("addresses");
142
142
  return result;
143
143
  }
144
144
  async function deleteAddress(id) {
145
145
  await withAuthRefresh(async (options) => {
146
- return getClient().customer.addresses.delete(id, options);
146
+ return getClient().store.customer.addresses.delete(id, options);
147
147
  });
148
148
  revalidateTag("addresses");
149
149
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/actions/addresses.ts","../../src/auth-helpers.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreAddress, AddressParams } from '@spree/sdk';\nimport { withAuthRefresh } from '../auth-helpers';\nimport { getClient } from '../config';\n\n/**\n * List the authenticated customer's addresses.\n */\nexport async function listAddresses(): Promise<{ data: StoreAddress[] }> {\n return withAuthRefresh(async (options) => {\n return getClient().customer.addresses.list(undefined, options);\n });\n}\n\n/**\n * Get a single address by ID.\n */\nexport async function getAddress(id: string): Promise<StoreAddress> {\n return withAuthRefresh(async (options) => {\n return getClient().customer.addresses.get(id, options);\n });\n}\n\n/**\n * Create a new address for the customer.\n */\nexport async function createAddress(params: AddressParams): Promise<StoreAddress> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().customer.addresses.create(params, options);\n });\n revalidateTag('addresses');\n return result;\n}\n\n/**\n * Update an existing address.\n */\nexport async function updateAddress(\n id: string,\n params: Partial<AddressParams>\n): Promise<StoreAddress> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().customer.addresses.update(id, params, options);\n });\n revalidateTag('addresses');\n return result;\n}\n\n/**\n * Delete an address.\n */\nexport async function deleteAddress(id: string): Promise<void> {\n await withAuthRefresh(async (options) => {\n return getClient().customer.addresses.delete(id, options);\n });\n revalidateTag('addresses');\n}\n","import { SpreeError } from '@spree/sdk';\nimport type { RequestOptions } from '@spree/sdk';\nimport { getClient } from './config';\nimport { getAccessToken, setAccessToken, clearAccessToken } from './cookies';\n\n/**\n * Get auth request options from the current JWT token.\n * Proactively refreshes the token if it expires within 1 hour.\n */\nexport async function getAuthOptions(): Promise<RequestOptions> {\n const token = await getAccessToken();\n if (!token) {\n return {};\n }\n\n // Check if token is close to expiry by decoding JWT payload\n try {\n const payload = JSON.parse(atob(token.split('.')[1]));\n const exp = payload.exp;\n const now = Math.floor(Date.now() / 1000);\n\n // Refresh if token expires in less than 1 hour\n if (exp && exp - now < 3600) {\n try {\n const refreshed = await getClient().auth.refresh({ token });\n await setAccessToken(refreshed.token);\n return { token: refreshed.token };\n } catch {\n // Refresh failed — use existing token, it might still work\n }\n }\n } catch {\n // Can't decode JWT — use it as-is, the server will reject if invalid\n }\n\n return { token };\n}\n\n/**\n * Execute an authenticated request with automatic token refresh on 401.\n * @param fn - Function that takes RequestOptions and returns a promise\n * @returns The result of the function\n * @throws SpreeError if auth fails after refresh attempt\n */\nexport async function withAuthRefresh<T>(\n fn: (options: RequestOptions) => Promise<T>\n): Promise<T> {\n const options = await getAuthOptions();\n\n if (!options.token) {\n throw new Error('Not authenticated');\n }\n\n try {\n return await fn(options);\n } catch (error: unknown) {\n // If 401, try refreshing the token once\n if (error instanceof SpreeError && error.status === 401) {\n try {\n const refreshed = await getClient().auth.refresh({ token: options.token });\n await setAccessToken(refreshed.token);\n return await fn({ token: refreshed.token });\n } catch {\n // Refresh failed — clear token and rethrow\n await clearAccessToken();\n throw error;\n }\n }\n throw error;\n }\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_API_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n apiKey: config.apiKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const apiKey = process.env.SPREE_API_KEY;\n if (baseUrl && apiKey) {\n initSpreeNext({ baseUrl, apiKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/actions/addresses.ts","../../src/auth-helpers.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreAddress, AddressParams } from '@spree/sdk';\nimport { withAuthRefresh } from '../auth-helpers';\nimport { getClient } from '../config';\n\n/**\n * List the authenticated customer's addresses.\n */\nexport async function listAddresses(): Promise<{ data: StoreAddress[] }> {\n return withAuthRefresh(async (options) => {\n return getClient().store.customer.addresses.list(undefined, options);\n });\n}\n\n/**\n * Get a single address by ID.\n */\nexport async function getAddress(id: string): Promise<StoreAddress> {\n return withAuthRefresh(async (options) => {\n return getClient().store.customer.addresses.get(id, options);\n });\n}\n\n/**\n * Create a new address for the customer.\n */\nexport async function createAddress(params: AddressParams): Promise<StoreAddress> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().store.customer.addresses.create(params, options);\n });\n revalidateTag('addresses');\n return result;\n}\n\n/**\n * Update an existing address.\n */\nexport async function updateAddress(\n id: string,\n params: Partial<AddressParams>\n): Promise<StoreAddress> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().store.customer.addresses.update(id, params, options);\n });\n revalidateTag('addresses');\n return result;\n}\n\n/**\n * Delete an address.\n */\nexport async function deleteAddress(id: string): Promise<void> {\n await withAuthRefresh(async (options) => {\n return getClient().store.customer.addresses.delete(id, options);\n });\n revalidateTag('addresses');\n}\n","import { SpreeError } from '@spree/sdk';\nimport type { RequestOptions } from '@spree/sdk';\nimport { getClient } from './config';\nimport { getAccessToken, setAccessToken, clearAccessToken } from './cookies';\n\n/**\n * Get auth request options from the current JWT token.\n * Proactively refreshes the token if it expires within 1 hour.\n */\nexport async function getAuthOptions(): Promise<RequestOptions> {\n const token = await getAccessToken();\n if (!token) {\n return {};\n }\n\n // Check if token is close to expiry by decoding JWT payload\n try {\n const payload = JSON.parse(atob(token.split('.')[1]));\n const exp = payload.exp;\n const now = Math.floor(Date.now() / 1000);\n\n // Refresh if token expires in less than 1 hour\n if (exp && exp - now < 3600) {\n try {\n const refreshed = await getClient().store.auth.refresh({ token });\n await setAccessToken(refreshed.token);\n return { token: refreshed.token };\n } catch {\n // Refresh failed — use existing token, it might still work\n }\n }\n } catch {\n // Can't decode JWT — use it as-is, the server will reject if invalid\n }\n\n return { token };\n}\n\n/**\n * Execute an authenticated request with automatic token refresh on 401.\n * @param fn - Function that takes RequestOptions and returns a promise\n * @returns The result of the function\n * @throws SpreeError if auth fails after refresh attempt\n */\nexport async function withAuthRefresh<T>(\n fn: (options: RequestOptions) => Promise<T>\n): Promise<T> {\n const options = await getAuthOptions();\n\n if (!options.token) {\n throw new Error('Not authenticated');\n }\n\n try {\n return await fn(options);\n } catch (error: unknown) {\n // If 401, try refreshing the token once\n if (error instanceof SpreeError && error.status === 401) {\n try {\n const refreshed = await getClient().store.auth.refresh({ token: options.token });\n await setAccessToken(refreshed.token);\n return await fn({ token: refreshed.token });\n } catch {\n // Refresh failed — clear token and rethrow\n await clearAccessToken();\n throw error;\n }\n }\n throw error;\n }\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_PUBLISHABLE_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;\n if (baseUrl && publishableKey) {\n initSpreeNext({ baseUrl, publishableKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n"],"mappings":"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","names":[]}
@@ -11,18 +11,18 @@ function initSpreeNext(config) {
11
11
  _config = config;
12
12
  _client = createSpreeClient({
13
13
  baseUrl: config.baseUrl,
14
- apiKey: config.apiKey
14
+ publishableKey: config.publishableKey
15
15
  });
16
16
  }
17
17
  function getClient() {
18
18
  if (!_client) {
19
19
  const baseUrl = process.env.SPREE_API_URL;
20
- const apiKey = process.env.SPREE_API_KEY;
21
- if (baseUrl && apiKey) {
22
- initSpreeNext({ baseUrl, apiKey });
20
+ const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;
21
+ if (baseUrl && publishableKey) {
22
+ initSpreeNext({ baseUrl, publishableKey });
23
23
  } else {
24
24
  throw new Error(
25
- "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables."
25
+ "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables."
26
26
  );
27
27
  }
28
28
  }
@@ -94,7 +94,7 @@ async function getAuthOptions() {
94
94
  const now = Math.floor(Date.now() / 1e3);
95
95
  if (exp && exp - now < 3600) {
96
96
  try {
97
- const refreshed = await getClient().auth.refresh({ token });
97
+ const refreshed = await getClient().store.auth.refresh({ token });
98
98
  await setAccessToken(refreshed.token);
99
99
  return { token: refreshed.token };
100
100
  } catch {
@@ -114,7 +114,7 @@ async function withAuthRefresh(fn) {
114
114
  } catch (error) {
115
115
  if (error instanceof SpreeError && error.status === 401) {
116
116
  try {
117
- const refreshed = await getClient().auth.refresh({ token: options.token });
117
+ const refreshed = await getClient().store.auth.refresh({ token: options.token });
118
118
  await setAccessToken(refreshed.token);
119
119
  return await fn({ token: refreshed.token });
120
120
  } catch {
@@ -129,12 +129,12 @@ async function withAuthRefresh(fn) {
129
129
  // src/actions/auth.ts
130
130
  async function login(email, password) {
131
131
  try {
132
- const result = await getClient().auth.login({ email, password });
132
+ const result = await getClient().store.auth.login({ email, password });
133
133
  await setAccessToken(result.token);
134
134
  const cartToken = await getCartToken();
135
135
  if (cartToken) {
136
136
  try {
137
- await getClient().cart.associate({
137
+ await getClient().store.cart.associate({
138
138
  token: result.token,
139
139
  orderToken: cartToken
140
140
  });
@@ -153,7 +153,7 @@ async function login(email, password) {
153
153
  }
154
154
  async function register(email, password, passwordConfirmation) {
155
155
  try {
156
- const result = await getClient().auth.register({
156
+ const result = await getClient().store.auth.register({
157
157
  email,
158
158
  password,
159
159
  password_confirmation: passwordConfirmation
@@ -162,7 +162,7 @@ async function register(email, password, passwordConfirmation) {
162
162
  const cartToken = await getCartToken();
163
163
  if (cartToken) {
164
164
  try {
165
- await getClient().cart.associate({
165
+ await getClient().store.cart.associate({
166
166
  token: result.token,
167
167
  orderToken: cartToken
168
168
  });
@@ -191,7 +191,7 @@ async function getCustomer() {
191
191
  if (!token) return null;
192
192
  try {
193
193
  return await withAuthRefresh(async (options) => {
194
- return getClient().customer.get(options);
194
+ return getClient().store.customer.get(options);
195
195
  });
196
196
  } catch {
197
197
  await clearAccessToken();
@@ -200,7 +200,7 @@ async function getCustomer() {
200
200
  }
201
201
  async function updateCustomer(data) {
202
202
  const result = await withAuthRefresh(async (options) => {
203
- return getClient().customer.update(data, options);
203
+ return getClient().store.customer.update(data, options);
204
204
  });
205
205
  revalidateTag("customer");
206
206
  return result;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/actions/auth.ts","../../src/config.ts","../../src/cookies.ts","../../src/auth-helpers.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreCustomer } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { setAccessToken, clearAccessToken, getAccessToken, getCartToken } from '../cookies';\nimport { withAuthRefresh } from '../auth-helpers';\n\n/**\n * Login with email and password.\n * Automatically associates any guest cart with the authenticated user.\n */\nexport async function login(\n email: string,\n password: string\n): Promise<{ success: boolean; user?: { id: string; email: string; first_name?: string | null; last_name?: string | null }; error?: string }> {\n try {\n const result = await getClient().auth.login({ email, password });\n await setAccessToken(result.token);\n\n // Associate guest cart if one exists\n const cartToken = await getCartToken();\n if (cartToken) {\n try {\n await getClient().cart.associate({\n token: result.token,\n orderToken: cartToken,\n });\n } catch {\n // Cart association failure is non-fatal\n }\n }\n\n revalidateTag('customer');\n revalidateTag('cart');\n return { success: true, user: result.user };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Invalid email or password',\n };\n }\n}\n\n/**\n * Register a new customer account.\n * Automatically associates any guest cart with the new account.\n */\nexport async function register(\n email: string,\n password: string,\n passwordConfirmation: string\n): Promise<{ success: boolean; user?: { id: string; email: string; first_name?: string | null; last_name?: string | null }; error?: string }> {\n try {\n const result = await getClient().auth.register({\n email,\n password,\n password_confirmation: passwordConfirmation,\n });\n await setAccessToken(result.token);\n\n // Associate guest cart\n const cartToken = await getCartToken();\n if (cartToken) {\n try {\n await getClient().cart.associate({\n token: result.token,\n orderToken: cartToken,\n });\n } catch {\n // Non-fatal\n }\n }\n\n revalidateTag('customer');\n revalidateTag('cart');\n return { success: true, user: result.user };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Registration failed',\n };\n }\n}\n\n/**\n * Logout the current user.\n */\nexport async function logout(): Promise<void> {\n await clearAccessToken();\n revalidateTag('customer');\n revalidateTag('cart');\n revalidateTag('addresses');\n revalidateTag('credit-cards');\n}\n\n/**\n * Get the currently authenticated customer. Returns null if not logged in.\n */\nexport async function getCustomer(): Promise<StoreCustomer | null> {\n const token = await getAccessToken();\n if (!token) return null;\n\n try {\n return await withAuthRefresh(async (options) => {\n return getClient().customer.get(options);\n });\n } catch {\n await clearAccessToken();\n return null;\n }\n}\n\n/**\n * Update the current customer's profile.\n */\nexport async function updateCustomer(\n data: { first_name?: string; last_name?: string; email?: string }\n): Promise<StoreCustomer> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().customer.update(data, options);\n });\n revalidateTag('customer');\n return result;\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_API_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n apiKey: config.apiKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const apiKey = process.env.SPREE_API_KEY;\n if (baseUrl && apiKey) {\n initSpreeNext({ baseUrl, apiKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n","import { SpreeError } from '@spree/sdk';\nimport type { RequestOptions } from '@spree/sdk';\nimport { getClient } from './config';\nimport { getAccessToken, setAccessToken, clearAccessToken } from './cookies';\n\n/**\n * Get auth request options from the current JWT token.\n * Proactively refreshes the token if it expires within 1 hour.\n */\nexport async function getAuthOptions(): Promise<RequestOptions> {\n const token = await getAccessToken();\n if (!token) {\n return {};\n }\n\n // Check if token is close to expiry by decoding JWT payload\n try {\n const payload = JSON.parse(atob(token.split('.')[1]));\n const exp = payload.exp;\n const now = Math.floor(Date.now() / 1000);\n\n // Refresh if token expires in less than 1 hour\n if (exp && exp - now < 3600) {\n try {\n const refreshed = await getClient().auth.refresh({ token });\n await setAccessToken(refreshed.token);\n return { token: refreshed.token };\n } catch {\n // Refresh failed — use existing token, it might still work\n }\n }\n } catch {\n // Can't decode JWT — use it as-is, the server will reject if invalid\n }\n\n return { token };\n}\n\n/**\n * Execute an authenticated request with automatic token refresh on 401.\n * @param fn - Function that takes RequestOptions and returns a promise\n * @returns The result of the function\n * @throws SpreeError if auth fails after refresh attempt\n */\nexport async function withAuthRefresh<T>(\n fn: (options: RequestOptions) => Promise<T>\n): Promise<T> {\n const options = await getAuthOptions();\n\n if (!options.token) {\n throw new Error('Not authenticated');\n }\n\n try {\n return await fn(options);\n } catch (error: unknown) {\n // If 401, try refreshing the token once\n if (error instanceof SpreeError && error.status === 401) {\n try {\n const refreshed = await getClient().auth.refresh({ token: options.token });\n await setAccessToken(refreshed.token);\n return await fn({ token: refreshed.token });\n } catch {\n // Refresh failed — clear token and rethrow\n await clearAccessToken();\n throw error;\n }\n }\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/actions/auth.ts","../../src/config.ts","../../src/cookies.ts","../../src/auth-helpers.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreCustomer } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { setAccessToken, clearAccessToken, getAccessToken, getCartToken } from '../cookies';\nimport { withAuthRefresh } from '../auth-helpers';\n\n/**\n * Login with email and password.\n * Automatically associates any guest cart with the authenticated user.\n */\nexport async function login(\n email: string,\n password: string\n): Promise<{ success: boolean; user?: { id: string; email: string; first_name?: string | null; last_name?: string | null }; error?: string }> {\n try {\n const result = await getClient().store.auth.login({ email, password });\n await setAccessToken(result.token);\n\n // Associate guest cart if one exists\n const cartToken = await getCartToken();\n if (cartToken) {\n try {\n await getClient().store.cart.associate({\n token: result.token,\n orderToken: cartToken,\n });\n } catch {\n // Cart association failure is non-fatal\n }\n }\n\n revalidateTag('customer');\n revalidateTag('cart');\n return { success: true, user: result.user };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Invalid email or password',\n };\n }\n}\n\n/**\n * Register a new customer account.\n * Automatically associates any guest cart with the new account.\n */\nexport async function register(\n email: string,\n password: string,\n passwordConfirmation: string\n): Promise<{ success: boolean; user?: { id: string; email: string; first_name?: string | null; last_name?: string | null }; error?: string }> {\n try {\n const result = await getClient().store.auth.register({\n email,\n password,\n password_confirmation: passwordConfirmation,\n });\n await setAccessToken(result.token);\n\n // Associate guest cart\n const cartToken = await getCartToken();\n if (cartToken) {\n try {\n await getClient().store.cart.associate({\n token: result.token,\n orderToken: cartToken,\n });\n } catch {\n // Non-fatal\n }\n }\n\n revalidateTag('customer');\n revalidateTag('cart');\n return { success: true, user: result.user };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Registration failed',\n };\n }\n}\n\n/**\n * Logout the current user.\n */\nexport async function logout(): Promise<void> {\n await clearAccessToken();\n revalidateTag('customer');\n revalidateTag('cart');\n revalidateTag('addresses');\n revalidateTag('credit-cards');\n}\n\n/**\n * Get the currently authenticated customer. Returns null if not logged in.\n */\nexport async function getCustomer(): Promise<StoreCustomer | null> {\n const token = await getAccessToken();\n if (!token) return null;\n\n try {\n return await withAuthRefresh(async (options) => {\n return getClient().store.customer.get(options);\n });\n } catch {\n await clearAccessToken();\n return null;\n }\n}\n\n/**\n * Update the current customer's profile.\n */\nexport async function updateCustomer(\n data: { first_name?: string; last_name?: string; email?: string }\n): Promise<StoreCustomer> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().store.customer.update(data, options);\n });\n revalidateTag('customer');\n return result;\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_PUBLISHABLE_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;\n if (baseUrl && publishableKey) {\n initSpreeNext({ baseUrl, publishableKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n","import { SpreeError } from '@spree/sdk';\nimport type { RequestOptions } from '@spree/sdk';\nimport { getClient } from './config';\nimport { getAccessToken, setAccessToken, clearAccessToken } from './cookies';\n\n/**\n * Get auth request options from the current JWT token.\n * Proactively refreshes the token if it expires within 1 hour.\n */\nexport async function getAuthOptions(): Promise<RequestOptions> {\n const token = await getAccessToken();\n if (!token) {\n return {};\n }\n\n // Check if token is close to expiry by decoding JWT payload\n try {\n const payload = JSON.parse(atob(token.split('.')[1]));\n const exp = payload.exp;\n const now = Math.floor(Date.now() / 1000);\n\n // Refresh if token expires in less than 1 hour\n if (exp && exp - now < 3600) {\n try {\n const refreshed = await getClient().store.auth.refresh({ token });\n await setAccessToken(refreshed.token);\n return { token: refreshed.token };\n } catch {\n // Refresh failed — use existing token, it might still work\n }\n }\n } catch {\n // Can't decode JWT — use it as-is, the server will reject if invalid\n }\n\n return { token };\n}\n\n/**\n * Execute an authenticated request with automatic token refresh on 401.\n * @param fn - Function that takes RequestOptions and returns a promise\n * @returns The result of the function\n * @throws SpreeError if auth fails after refresh attempt\n */\nexport async function withAuthRefresh<T>(\n fn: (options: RequestOptions) => Promise<T>\n): Promise<T> {\n const options = await getAuthOptions();\n\n if (!options.token) {\n throw new Error('Not authenticated');\n }\n\n try {\n return await fn(options);\n } catch (error: unknown) {\n // If 401, try refreshing the token once\n if (error instanceof SpreeError && error.status === 401) {\n try {\n const refreshed = await getClient().store.auth.refresh({ token: options.token });\n await setAccessToken(refreshed.token);\n return await fn({ token: refreshed.token });\n } catch {\n // Refresh failed — clear token and rethrow\n await clearAccessToken();\n throw error;\n }\n }\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -11,18 +11,18 @@ function initSpreeNext(config) {
11
11
  _config = config;
12
12
  _client = createSpreeClient({
13
13
  baseUrl: config.baseUrl,
14
- apiKey: config.apiKey
14
+ publishableKey: config.publishableKey
15
15
  });
16
16
  }
17
17
  function getClient() {
18
18
  if (!_client) {
19
19
  const baseUrl = process.env.SPREE_API_URL;
20
- const apiKey = process.env.SPREE_API_KEY;
21
- if (baseUrl && apiKey) {
22
- initSpreeNext({ baseUrl, apiKey });
20
+ const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;
21
+ if (baseUrl && publishableKey) {
22
+ initSpreeNext({ baseUrl, publishableKey });
23
23
  } else {
24
24
  throw new Error(
25
- "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables."
25
+ "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables."
26
26
  );
27
27
  }
28
28
  }
@@ -87,7 +87,7 @@ async function getCart() {
87
87
  const token = await getAccessToken();
88
88
  if (!orderToken && !token) return null;
89
89
  try {
90
- return await getClient().cart.get({ orderToken, token });
90
+ return await getClient().store.cart.get({ orderToken, token });
91
91
  } catch {
92
92
  return null;
93
93
  }
@@ -96,7 +96,7 @@ async function getOrCreateCart() {
96
96
  const existing = await getCart();
97
97
  if (existing) return existing;
98
98
  const token = await getAccessToken();
99
- const cart = await getClient().cart.create(token ? { token } : void 0);
99
+ const cart = await getClient().store.cart.create(token ? { token } : void 0);
100
100
  if (cart.token) {
101
101
  await setCartToken(cart.token);
102
102
  }
@@ -107,7 +107,7 @@ async function addItem(variantId, quantity = 1) {
107
107
  const cart = await getOrCreateCart();
108
108
  const orderToken = cart.token;
109
109
  const token = await getAccessToken();
110
- const lineItem = await getClient().orders.lineItems.create(
110
+ const lineItem = await getClient().store.orders.lineItems.create(
111
111
  cart.id,
112
112
  { variant_id: variantId, quantity },
113
113
  { orderToken, token }
@@ -119,8 +119,8 @@ async function updateItem(lineItemId, quantity) {
119
119
  const orderToken = await getCartToken();
120
120
  const token = await getAccessToken();
121
121
  if (!orderToken && !token) throw new Error("No cart found");
122
- const cart = await getClient().cart.get({ orderToken, token });
123
- const lineItem = await getClient().orders.lineItems.update(
122
+ const cart = await getClient().store.cart.get({ orderToken, token });
123
+ const lineItem = await getClient().store.orders.lineItems.update(
124
124
  cart.id,
125
125
  lineItemId,
126
126
  { quantity },
@@ -133,8 +133,8 @@ async function removeItem(lineItemId) {
133
133
  const orderToken = await getCartToken();
134
134
  const token = await getAccessToken();
135
135
  if (!orderToken && !token) throw new Error("No cart found");
136
- const cart = await getClient().cart.get({ orderToken, token });
137
- await getClient().orders.lineItems.delete(cart.id, lineItemId, {
136
+ const cart = await getClient().store.cart.get({ orderToken, token });
137
+ await getClient().store.orders.lineItems.delete(cart.id, lineItemId, {
138
138
  orderToken,
139
139
  token
140
140
  });
@@ -149,7 +149,7 @@ async function associateCart() {
149
149
  const token = await getAccessToken();
150
150
  if (!orderToken || !token) return null;
151
151
  try {
152
- const result = await getClient().cart.associate({ orderToken, token });
152
+ const result = await getClient().store.cart.associate({ orderToken, token });
153
153
  revalidateTag("cart");
154
154
  return result;
155
155
  } catch {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/actions/cart.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreOrder, StoreLineItem } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { getCartToken, setCartToken, clearCartToken, getAccessToken } from '../cookies';\n\n/**\n * Get the current cart. Returns null if no cart exists.\n */\nexport async function getCart(): Promise<(StoreOrder & { token: string }) | null> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken && !token) return null;\n\n try {\n return await getClient().cart.get({ orderToken, token });\n } catch {\n return null;\n }\n}\n\n/**\n * Get existing cart or create a new one.\n */\nexport async function getOrCreateCart(): Promise<StoreOrder & { token: string }> {\n const existing = await getCart();\n if (existing) return existing;\n\n const token = await getAccessToken();\n const cart = await getClient().cart.create(token ? { token } : undefined);\n\n if (cart.token) {\n await setCartToken(cart.token);\n }\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Add an item to the cart. Creates a cart if none exists.\n */\nexport async function addItem(\n variantId: string,\n quantity: number = 1\n): Promise<StoreLineItem> {\n const cart = await getOrCreateCart();\n const orderToken = cart.token;\n const token = await getAccessToken();\n\n const lineItem = await getClient().orders.lineItems.create(\n cart.id,\n { variant_id: variantId, quantity },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return lineItem;\n}\n\n/**\n * Update a line item quantity in the cart.\n */\nexport async function updateItem(\n lineItemId: string,\n quantity: number\n): Promise<StoreLineItem> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken && !token) throw new Error('No cart found');\n\n const cart = await getClient().cart.get({ orderToken, token });\n\n const lineItem = await getClient().orders.lineItems.update(\n cart.id,\n lineItemId,\n { quantity },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return lineItem;\n}\n\n/**\n * Remove a line item from the cart.\n */\nexport async function removeItem(lineItemId: string): Promise<void> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken && !token) throw new Error('No cart found');\n\n const cart = await getClient().cart.get({ orderToken, token });\n\n await getClient().orders.lineItems.delete(cart.id, lineItemId, {\n orderToken,\n token,\n });\n\n revalidateTag('cart');\n}\n\n/**\n * Clear the cart (abandons the current cart).\n */\nexport async function clearCart(): Promise<void> {\n await clearCartToken();\n revalidateTag('cart');\n}\n\n/**\n * Associate a guest cart with the currently authenticated user.\n * Call this after login/register when the user has an existing guest cart.\n */\nexport async function associateCart(): Promise<(StoreOrder & { token: string }) | null> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken || !token) return null;\n\n try {\n const result = await getClient().cart.associate({ orderToken, token });\n revalidateTag('cart');\n return result;\n } catch {\n // Cart might already belong to another user — clear it\n await clearCartToken();\n revalidateTag('cart');\n return null;\n }\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_API_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n apiKey: config.apiKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const apiKey = process.env.SPREE_API_KEY;\n if (baseUrl && apiKey) {\n initSpreeNext({ baseUrl, apiKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n"],"mappings":";;;AAEA,SAAS,qBAAqB;;;ACF9B,SAAS,yBAA2C;AAGpD,IAAI,UAA8B;AAClC,IAAI,UAAkC;AAO/B,SAAS,cAAc,QAA+B;AAC3D,YAAU;AACV,YAAU,kBAAkB;AAAA,IAC1B,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO;AAAA,EACjB,CAAC;AACH;AAMO,SAAS,YAAyB;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,SAAS,QAAQ,IAAI;AAC3B,QAAI,WAAW,QAAQ;AACrB,oBAAc,EAAE,SAAS,OAAO,CAAC;AAAA,IACnC,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,YAA6B;AAC3C,MAAI,CAAC,SAAS;AACZ,cAAU;AAAA,EACZ;AACA,SAAO;AACT;;;AC/CA,SAAS,eAAe;AAGxB,IAAM,sBAAsB;AAC5B,IAAM,8BAA8B;AACpC,IAAM,qBAAqB,KAAK,KAAK,KAAK;AAC1C,IAAM,uBAAuB,KAAK,KAAK,KAAK;AAE5C,SAAS,oBAA4B;AACnC,MAAI;AACF,WAAO,UAAU,EAAE,kBAAkB;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,2BAAmC;AAC1C,MAAI;AACF,WAAO,UAAU,EAAE,yBAAyB;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,eAAsB,eAA4C;AAChE,QAAM,cAAc,MAAM,QAAQ;AAClC,SAAO,YAAY,IAAI,kBAAkB,CAAC,GAAG;AAC/C;AAEA,eAAsB,aAAa,OAA8B;AAC/D,QAAM,cAAc,MAAM,QAAQ;AAClC,cAAY,IAAI,kBAAkB,GAAG,OAAO;AAAA,IAC1C,UAAU;AAAA,IACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AACH;AAEA,eAAsB,iBAAgC;AACpD,QAAM,cAAc,MAAM,QAAQ;AAClC,cAAY,IAAI,kBAAkB,GAAG,IAAI;AAAA,IACvC,QAAQ;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AACH;AAIA,eAAsB,iBAA8C;AAClE,QAAM,cAAc,MAAM,QAAQ;AAClC,SAAO,YAAY,IAAI,yBAAyB,CAAC,GAAG;AACtD;;;AF7CA,eAAsB,UAA4D;AAChF,QAAM,aAAa,MAAM,aAAa;AACtC,QAAM,QAAQ,MAAM,eAAe;AACnC,MAAI,CAAC,cAAc,CAAC,MAAO,QAAO;AAElC,MAAI;AACF,WAAO,MAAM,UAAU,EAAE,KAAK,IAAI,EAAE,YAAY,MAAM,CAAC;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,kBAA2D;AAC/E,QAAM,WAAW,MAAM,QAAQ;AAC/B,MAAI,SAAU,QAAO;AAErB,QAAM,QAAQ,MAAM,eAAe;AACnC,QAAM,OAAO,MAAM,UAAU,EAAE,KAAK,OAAO,QAAQ,EAAE,MAAM,IAAI,MAAS;AAExE,MAAI,KAAK,OAAO;AACd,UAAM,aAAa,KAAK,KAAK;AAAA,EAC/B;AAEA,gBAAc,MAAM;AACpB,SAAO;AACT;AAKA,eAAsB,QACpB,WACA,WAAmB,GACK;AACxB,QAAM,OAAO,MAAM,gBAAgB;AACnC,QAAM,aAAa,KAAK;AACxB,QAAM,QAAQ,MAAM,eAAe;AAEnC,QAAM,WAAW,MAAM,UAAU,EAAE,OAAO,UAAU;AAAA,IAClD,KAAK;AAAA,IACL,EAAE,YAAY,WAAW,SAAS;AAAA,IAClC,EAAE,YAAY,MAAM;AAAA,EACtB;AAEA,gBAAc,MAAM;AACpB,SAAO;AACT;AAKA,eAAsB,WACpB,YACA,UACwB;AACxB,QAAM,aAAa,MAAM,aAAa;AACtC,QAAM,QAAQ,MAAM,eAAe;AACnC,MAAI,CAAC,cAAc,CAAC,MAAO,OAAM,IAAI,MAAM,eAAe;AAE1D,QAAM,OAAO,MAAM,UAAU,EAAE,KAAK,IAAI,EAAE,YAAY,MAAM,CAAC;AAE7D,QAAM,WAAW,MAAM,UAAU,EAAE,OAAO,UAAU;AAAA,IAClD,KAAK;AAAA,IACL;AAAA,IACA,EAAE,SAAS;AAAA,IACX,EAAE,YAAY,MAAM;AAAA,EACtB;AAEA,gBAAc,MAAM;AACpB,SAAO;AACT;AAKA,eAAsB,WAAW,YAAmC;AAClE,QAAM,aAAa,MAAM,aAAa;AACtC,QAAM,QAAQ,MAAM,eAAe;AACnC,MAAI,CAAC,cAAc,CAAC,MAAO,OAAM,IAAI,MAAM,eAAe;AAE1D,QAAM,OAAO,MAAM,UAAU,EAAE,KAAK,IAAI,EAAE,YAAY,MAAM,CAAC;AAE7D,QAAM,UAAU,EAAE,OAAO,UAAU,OAAO,KAAK,IAAI,YAAY;AAAA,IAC7D;AAAA,IACA;AAAA,EACF,CAAC;AAED,gBAAc,MAAM;AACtB;AAKA,eAAsB,YAA2B;AAC/C,QAAM,eAAe;AACrB,gBAAc,MAAM;AACtB;AAMA,eAAsB,gBAAkE;AACtF,QAAM,aAAa,MAAM,aAAa;AACtC,QAAM,QAAQ,MAAM,eAAe;AACnC,MAAI,CAAC,cAAc,CAAC,MAAO,QAAO;AAElC,MAAI;AACF,UAAM,SAAS,MAAM,UAAU,EAAE,KAAK,UAAU,EAAE,YAAY,MAAM,CAAC;AACrE,kBAAc,MAAM;AACpB,WAAO;AAAA,EACT,QAAQ;AAEN,UAAM,eAAe;AACrB,kBAAc,MAAM;AACpB,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/actions/cart.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreOrder, StoreLineItem } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { getCartToken, setCartToken, clearCartToken, getAccessToken } from '../cookies';\n\n/**\n * Get the current cart. Returns null if no cart exists.\n */\nexport async function getCart(): Promise<(StoreOrder & { token: string }) | null> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken && !token) return null;\n\n try {\n return await getClient().store.cart.get({ orderToken, token });\n } catch {\n return null;\n }\n}\n\n/**\n * Get existing cart or create a new one.\n */\nexport async function getOrCreateCart(): Promise<StoreOrder & { token: string }> {\n const existing = await getCart();\n if (existing) return existing;\n\n const token = await getAccessToken();\n const cart = await getClient().store.cart.create(token ? { token } : undefined);\n\n if (cart.token) {\n await setCartToken(cart.token);\n }\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Add an item to the cart. Creates a cart if none exists.\n */\nexport async function addItem(\n variantId: string,\n quantity: number = 1\n): Promise<StoreLineItem> {\n const cart = await getOrCreateCart();\n const orderToken = cart.token;\n const token = await getAccessToken();\n\n const lineItem = await getClient().store.orders.lineItems.create(\n cart.id,\n { variant_id: variantId, quantity },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return lineItem;\n}\n\n/**\n * Update a line item quantity in the cart.\n */\nexport async function updateItem(\n lineItemId: string,\n quantity: number\n): Promise<StoreLineItem> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken && !token) throw new Error('No cart found');\n\n const cart = await getClient().store.cart.get({ orderToken, token });\n\n const lineItem = await getClient().store.orders.lineItems.update(\n cart.id,\n lineItemId,\n { quantity },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return lineItem;\n}\n\n/**\n * Remove a line item from the cart.\n */\nexport async function removeItem(lineItemId: string): Promise<void> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken && !token) throw new Error('No cart found');\n\n const cart = await getClient().store.cart.get({ orderToken, token });\n\n await getClient().store.orders.lineItems.delete(cart.id, lineItemId, {\n orderToken,\n token,\n });\n\n revalidateTag('cart');\n}\n\n/**\n * Clear the cart (abandons the current cart).\n */\nexport async function clearCart(): Promise<void> {\n await clearCartToken();\n revalidateTag('cart');\n}\n\n/**\n * Associate a guest cart with the currently authenticated user.\n * Call this after login/register when the user has an existing guest cart.\n */\nexport async function associateCart(): Promise<(StoreOrder & { token: string }) | null> {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n if (!orderToken || !token) return null;\n\n try {\n const result = await getClient().store.cart.associate({ orderToken, token });\n revalidateTag('cart');\n return result;\n } catch {\n // Cart might already belong to another user — clear it\n await clearCartToken();\n revalidateTag('cart');\n return null;\n }\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_PUBLISHABLE_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;\n if (baseUrl && publishableKey) {\n initSpreeNext({ baseUrl, publishableKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n"],"mappings":"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","names":[]}
@@ -11,18 +11,18 @@ function initSpreeNext(config) {
11
11
  _config = config;
12
12
  _client = createSpreeClient({
13
13
  baseUrl: config.baseUrl,
14
- apiKey: config.apiKey
14
+ publishableKey: config.publishableKey
15
15
  });
16
16
  }
17
17
  function getClient() {
18
18
  if (!_client) {
19
19
  const baseUrl = process.env.SPREE_API_URL;
20
- const apiKey = process.env.SPREE_API_KEY;
21
- if (baseUrl && apiKey) {
22
- initSpreeNext({ baseUrl, apiKey });
20
+ const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;
21
+ if (baseUrl && publishableKey) {
22
+ initSpreeNext({ baseUrl, publishableKey });
23
23
  } else {
24
24
  throw new Error(
25
- "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables."
25
+ "@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables."
26
26
  );
27
27
  }
28
28
  }
@@ -72,7 +72,7 @@ async function getCheckoutOptions() {
72
72
  }
73
73
  async function getCheckout(orderId) {
74
74
  const options = await getCheckoutOptions();
75
- return getClient().orders.get(
75
+ return getClient().store.orders.get(
76
76
  orderId,
77
77
  { includes: "line_items,shipments,ship_address,bill_address" },
78
78
  options
@@ -80,29 +80,29 @@ async function getCheckout(orderId) {
80
80
  }
81
81
  async function updateAddresses(orderId, params) {
82
82
  const options = await getCheckoutOptions();
83
- const result = await getClient().orders.update(orderId, params, options);
83
+ const result = await getClient().store.orders.update(orderId, params, options);
84
84
  revalidateTag("checkout");
85
85
  return result;
86
86
  }
87
87
  async function advance(orderId) {
88
88
  const options = await getCheckoutOptions();
89
- const result = await getClient().orders.advance(orderId, options);
89
+ const result = await getClient().store.orders.advance(orderId, options);
90
90
  revalidateTag("checkout");
91
91
  return result;
92
92
  }
93
93
  async function next(orderId) {
94
94
  const options = await getCheckoutOptions();
95
- const result = await getClient().orders.next(orderId, options);
95
+ const result = await getClient().store.orders.next(orderId, options);
96
96
  revalidateTag("checkout");
97
97
  return result;
98
98
  }
99
99
  async function getShipments(orderId) {
100
100
  const options = await getCheckoutOptions();
101
- return getClient().orders.shipments.list(orderId, options);
101
+ return getClient().store.orders.shipments.list(orderId, options);
102
102
  }
103
103
  async function selectShippingRate(orderId, shipmentId, shippingRateId) {
104
104
  const options = await getCheckoutOptions();
105
- const result = await getClient().orders.shipments.update(
105
+ const result = await getClient().store.orders.shipments.update(
106
106
  orderId,
107
107
  shipmentId,
108
108
  { selected_shipping_rate_id: shippingRateId },
@@ -113,21 +113,21 @@ async function selectShippingRate(orderId, shipmentId, shippingRateId) {
113
113
  }
114
114
  async function applyCoupon(orderId, code) {
115
115
  const options = await getCheckoutOptions();
116
- const result = await getClient().orders.couponCodes.apply(orderId, code, options);
116
+ const result = await getClient().store.orders.couponCodes.apply(orderId, code, options);
117
117
  revalidateTag("checkout");
118
118
  revalidateTag("cart");
119
119
  return result;
120
120
  }
121
121
  async function removeCoupon(orderId, promotionId) {
122
122
  const options = await getCheckoutOptions();
123
- const result = await getClient().orders.couponCodes.remove(orderId, promotionId, options);
123
+ const result = await getClient().store.orders.couponCodes.remove(orderId, promotionId, options);
124
124
  revalidateTag("checkout");
125
125
  revalidateTag("cart");
126
126
  return result;
127
127
  }
128
128
  async function complete(orderId) {
129
129
  const options = await getCheckoutOptions();
130
- const result = await getClient().orders.complete(orderId, options);
130
+ const result = await getClient().store.orders.complete(orderId, options);
131
131
  revalidateTag("checkout");
132
132
  revalidateTag("cart");
133
133
  return result;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/actions/checkout.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreOrder, StoreShipment, AddressParams } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { getCartToken, getAccessToken } from '../cookies';\n\nasync function getCheckoutOptions() {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n return { orderToken, token };\n}\n\n/**\n * Get the current checkout order state.\n * Includes line_items, shipments, and addresses by default.\n */\nexport async function getCheckout(\n orderId: string\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n return getClient().orders.get(\n orderId,\n { includes: 'line_items,shipments,ship_address,bill_address' },\n options\n );\n}\n\n/**\n * Update shipping and/or billing addresses on the order.\n */\nexport async function updateAddresses(\n orderId: string,\n params: {\n email?: string;\n ship_address?: AddressParams;\n bill_address?: AddressParams;\n ship_address_id?: string;\n bill_address_id?: string;\n }\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.update(orderId, params, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Advance the checkout to the next step.\n */\nexport async function advance(orderId: string): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.advance(orderId, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Move the checkout to the next step (alias for advance).\n */\nexport async function next(orderId: string): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.next(orderId, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Get shipments for the order (includes available shipping rates).\n */\nexport async function getShipments(\n orderId: string\n): Promise<{ data: StoreShipment[] }> {\n const options = await getCheckoutOptions();\n return getClient().orders.shipments.list(orderId, options);\n}\n\n/**\n * Select a shipping rate for a shipment.\n */\nexport async function selectShippingRate(\n orderId: string,\n shipmentId: string,\n shippingRateId: string\n): Promise<StoreShipment> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.shipments.update(\n orderId,\n shipmentId,\n { selected_shipping_rate_id: shippingRateId },\n options\n );\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Apply a coupon code to the order.\n */\nexport async function applyCoupon(\n orderId: string,\n code: string\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.couponCodes.apply(orderId, code, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n\n/**\n * Remove a coupon/promotion from the order.\n */\nexport async function removeCoupon(\n orderId: string,\n promotionId: string\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.couponCodes.remove(orderId, promotionId, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n\n/**\n * Complete the checkout and place the order.\n */\nexport async function complete(orderId: string): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().orders.complete(orderId, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_API_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n apiKey: config.apiKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const apiKey = process.env.SPREE_API_KEY;\n if (baseUrl && apiKey) {\n initSpreeNext({ baseUrl, apiKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_API_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n"],"mappings":";;;AAEA,SAAS,qBAAqB;;;ACF9B,SAAS,yBAA2C;AAGpD,IAAI,UAA8B;AAClC,IAAI,UAAkC;AAO/B,SAAS,cAAc,QAA+B;AAC3D,YAAU;AACV,YAAU,kBAAkB;AAAA,IAC1B,SAAS,OAAO;AAAA,IAChB,QAAQ,OAAO;AAAA,EACjB,CAAC;AACH;AAMO,SAAS,YAAyB;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,SAAS,QAAQ,IAAI;AAC3B,QAAI,WAAW,QAAQ;AACrB,oBAAc,EAAE,SAAS,OAAO,CAAC;AAAA,IACnC,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,YAA6B;AAC3C,MAAI,CAAC,SAAS;AACZ,cAAU;AAAA,EACZ;AACA,SAAO;AACT;;;AC/CA,SAAS,eAAe;AAGxB,IAAM,sBAAsB;AAC5B,IAAM,8BAA8B;AACpC,IAAM,qBAAqB,KAAK,KAAK,KAAK;AAC1C,IAAM,uBAAuB,KAAK,KAAK,KAAK;AAE5C,SAAS,oBAA4B;AACnC,MAAI;AACF,WAAO,UAAU,EAAE,kBAAkB;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,2BAAmC;AAC1C,MAAI;AACF,WAAO,UAAU,EAAE,yBAAyB;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIA,eAAsB,eAA4C;AAChE,QAAM,cAAc,MAAM,QAAQ;AAClC,SAAO,YAAY,IAAI,kBAAkB,CAAC,GAAG;AAC/C;AAuBA,eAAsB,iBAA8C;AAClE,QAAM,cAAc,MAAM,QAAQ;AAClC,SAAO,YAAY,IAAI,yBAAyB,CAAC,GAAG;AACtD;;;AFhDA,eAAe,qBAAqB;AAClC,QAAM,aAAa,MAAM,aAAa;AACtC,QAAM,QAAQ,MAAM,eAAe;AACnC,SAAO,EAAE,YAAY,MAAM;AAC7B;AAMA,eAAsB,YACpB,SACqB;AACrB,QAAM,UAAU,MAAM,mBAAmB;AACzC,SAAO,UAAU,EAAE,OAAO;AAAA,IACxB;AAAA,IACA,EAAE,UAAU,iDAAiD;AAAA,IAC7D;AAAA,EACF;AACF;AAKA,eAAsB,gBACpB,SACA,QAOqB;AACrB,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,OAAO,SAAS,QAAQ,OAAO;AACvE,gBAAc,UAAU;AACxB,SAAO;AACT;AAKA,eAAsB,QAAQ,SAAsC;AAClE,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,QAAQ,SAAS,OAAO;AAChE,gBAAc,UAAU;AACxB,SAAO;AACT;AAKA,eAAsB,KAAK,SAAsC;AAC/D,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,KAAK,SAAS,OAAO;AAC7D,gBAAc,UAAU;AACxB,SAAO;AACT;AAKA,eAAsB,aACpB,SACoC;AACpC,QAAM,UAAU,MAAM,mBAAmB;AACzC,SAAO,UAAU,EAAE,OAAO,UAAU,KAAK,SAAS,OAAO;AAC3D;AAKA,eAAsB,mBACpB,SACA,YACA,gBACwB;AACxB,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,UAAU;AAAA,IAChD;AAAA,IACA;AAAA,IACA,EAAE,2BAA2B,eAAe;AAAA,IAC5C;AAAA,EACF;AACA,gBAAc,UAAU;AACxB,SAAO;AACT;AAKA,eAAsB,YACpB,SACA,MACqB;AACrB,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,YAAY,MAAM,SAAS,MAAM,OAAO;AAChF,gBAAc,UAAU;AACxB,gBAAc,MAAM;AACpB,SAAO;AACT;AAKA,eAAsB,aACpB,SACA,aACqB;AACrB,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,YAAY,OAAO,SAAS,aAAa,OAAO;AACxF,gBAAc,UAAU;AACxB,gBAAc,MAAM;AACpB,SAAO;AACT;AAKA,eAAsB,SAAS,SAAsC;AACnE,QAAM,UAAU,MAAM,mBAAmB;AACzC,QAAM,SAAS,MAAM,UAAU,EAAE,OAAO,SAAS,SAAS,OAAO;AACjE,gBAAc,UAAU;AACxB,gBAAc,MAAM;AACpB,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/actions/checkout.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { StoreOrder, StoreShipment, AddressParams } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { getCartToken, getAccessToken } from '../cookies';\n\nasync function getCheckoutOptions() {\n const orderToken = await getCartToken();\n const token = await getAccessToken();\n return { orderToken, token };\n}\n\n/**\n * Get the current checkout order state.\n * Includes line_items, shipments, and addresses by default.\n */\nexport async function getCheckout(\n orderId: string\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n return getClient().store.orders.get(\n orderId,\n { includes: 'line_items,shipments,ship_address,bill_address' },\n options\n );\n}\n\n/**\n * Update shipping and/or billing addresses on the order.\n */\nexport async function updateAddresses(\n orderId: string,\n params: {\n email?: string;\n ship_address?: AddressParams;\n bill_address?: AddressParams;\n ship_address_id?: string;\n bill_address_id?: string;\n }\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.update(orderId, params, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Advance the checkout to the next step.\n */\nexport async function advance(orderId: string): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.advance(orderId, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Move the checkout to the next step (alias for advance).\n */\nexport async function next(orderId: string): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.next(orderId, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Get shipments for the order (includes available shipping rates).\n */\nexport async function getShipments(\n orderId: string\n): Promise<{ data: StoreShipment[] }> {\n const options = await getCheckoutOptions();\n return getClient().store.orders.shipments.list(orderId, options);\n}\n\n/**\n * Select a shipping rate for a shipment.\n */\nexport async function selectShippingRate(\n orderId: string,\n shipmentId: string,\n shippingRateId: string\n): Promise<StoreShipment> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.shipments.update(\n orderId,\n shipmentId,\n { selected_shipping_rate_id: shippingRateId },\n options\n );\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Apply a coupon code to the order.\n */\nexport async function applyCoupon(\n orderId: string,\n code: string\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.couponCodes.apply(orderId, code, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n\n/**\n * Remove a coupon/promotion from the order.\n */\nexport async function removeCoupon(\n orderId: string,\n promotionId: string\n): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.couponCodes.remove(orderId, promotionId, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n\n/**\n * Complete the checkout and place the order.\n */\nexport async function complete(orderId: string): Promise<StoreOrder> {\n const options = await getCheckoutOptions();\n const result = await getClient().store.orders.complete(orderId, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n","import { createSpreeClient, type SpreeClient } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: SpreeClient | null = null;\nlet _config: SpreeNextConfig | null = null;\n\n/**\n * Initialize the Spree Next.js integration.\n * Call this once in your app (e.g., in `lib/storefront.ts`).\n * If not called, the client will auto-initialize from SPREE_API_URL and SPREE_PUBLISHABLE_KEY env vars.\n */\nexport function initSpreeNext(config: SpreeNextConfig): void {\n _config = config;\n _client = createSpreeClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the SpreeClient instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): SpreeClient {\n if (!_client) {\n const baseUrl = process.env.SPREE_API_URL;\n const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;\n if (baseUrl && publishableKey) {\n initSpreeNext({ baseUrl, publishableKey });\n } else {\n throw new Error(\n '@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables.'\n );\n }\n }\n return _client!;\n}\n\n/**\n * Get the current config. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getConfig(): SpreeNextConfig {\n if (!_config) {\n getClient(); // triggers auto-init\n }\n return _config!;\n}\n\n/**\n * Reset the client (useful for testing).\n * @internal\n */\nexport function resetClient(): void {\n _client = null;\n _config = null;\n}\n","import { cookies } from 'next/headers';\nimport { getConfig } from './config';\n\nconst DEFAULT_CART_COOKIE = '_spree_cart_token';\nconst DEFAULT_ACCESS_TOKEN_COOKIE = '_spree_jwt';\nconst CART_TOKEN_MAX_AGE = 60 * 60 * 24 * 30; // 30 days\nconst ACCESS_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; // 7 days\n\nfunction getCartCookieName(): string {\n try {\n return getConfig().cartCookieName ?? DEFAULT_CART_COOKIE;\n } catch {\n return DEFAULT_CART_COOKIE;\n }\n}\n\nfunction getAccessTokenCookieName(): string {\n try {\n return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;\n } catch {\n return DEFAULT_ACCESS_TOKEN_COOKIE;\n }\n}\n\n// --- Cart Token ---\n\nexport async function getCartToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartCookieName())?.value;\n}\n\nexport async function setCartToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: CART_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearCartToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n\n// --- Access Token (JWT) ---\n\nexport async function getAccessToken(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getAccessTokenCookieName())?.value;\n}\n\nexport async function setAccessToken(token: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), token, {\n httpOnly: true,\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n maxAge: ACCESS_TOKEN_MAX_AGE,\n });\n}\n\nexport async function clearAccessToken(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getAccessTokenCookieName(), '', {\n maxAge: -1,\n path: '/',\n });\n}\n"],"mappings":"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","names":[]}