@spree/next 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/actions/addresses.js.map +1 -1
- package/dist/actions/auth.js +13 -4
- package/dist/actions/auth.js.map +1 -1
- package/dist/actions/cart.d.ts +28 -13
- package/dist/actions/cart.js +124 -23
- package/dist/actions/cart.js.map +1 -1
- package/dist/actions/credit-cards.js.map +1 -1
- package/dist/actions/gift-cards.js.map +1 -1
- package/dist/actions/orders.js.map +1 -1
- package/dist/actions/payment-sessions.js +25 -9
- package/dist/actions/payment-sessions.js.map +1 -1
- package/dist/actions/payment-setup-sessions.js.map +1 -1
- package/dist/index.d.ts +6 -21
- package/dist/index.js +108 -56
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/actions/checkout.d.ts +0 -33
- package/dist/actions/checkout.js +0 -127
- package/dist/actions/checkout.js.map +0 -1
package/README.md
CHANGED
|
@@ -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 { Address, 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: Address[] }> {\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<Address> {\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<Address> {\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<Address> {\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 { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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\n// --- Checkout Options (combined cart + access tokens for checkout/payment actions) ---\n\nexport async function getCheckoutOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\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 { Address, 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: Address[] }> {\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<Address> {\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<Address> {\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<Address> {\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 { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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 getCartIdCookieName(): string {\n return `${getCartCookieName()}_id`;\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// --- Cart ID (prefixed ID stored alongside token for REST API) ---\n\nexport async function getCartId(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartIdCookieName())?.value;\n}\n\nexport async function setCartId(id: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), id, {\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 clearCartId(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), '', {\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\n// --- Cart Options (combined cart + access tokens for cart/checkout/payment actions) ---\n\nexport async function getCartOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\n}\n\n// --- Cart ID (required) ---\n\nexport async function requireCartId(): Promise<string> {\n const cartId = await getCartId();\n if (!cartId) throw new Error('No cart found');\n return cartId;\n}\n"],"mappings":"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","names":[]}
|
package/dist/actions/auth.js
CHANGED
|
@@ -48,6 +48,9 @@ function getCartCookieName() {
|
|
|
48
48
|
return DEFAULT_CART_COOKIE;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
function getCartIdCookieName() {
|
|
52
|
+
return `${getCartCookieName()}_id`;
|
|
53
|
+
}
|
|
51
54
|
function getAccessTokenCookieName() {
|
|
52
55
|
try {
|
|
53
56
|
return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;
|
|
@@ -59,6 +62,10 @@ async function getCartToken() {
|
|
|
59
62
|
const cookieStore = await cookies();
|
|
60
63
|
return cookieStore.get(getCartCookieName())?.value;
|
|
61
64
|
}
|
|
65
|
+
async function getCartId() {
|
|
66
|
+
const cookieStore = await cookies();
|
|
67
|
+
return cookieStore.get(getCartIdCookieName())?.value;
|
|
68
|
+
}
|
|
62
69
|
async function getAccessToken() {
|
|
63
70
|
const cookieStore = await cookies();
|
|
64
71
|
return cookieStore.get(getAccessTokenCookieName())?.value;
|
|
@@ -132,9 +139,10 @@ async function login(email, password) {
|
|
|
132
139
|
const result = await getClient().auth.login({ email, password });
|
|
133
140
|
await setAccessToken(result.token);
|
|
134
141
|
const cartToken = await getCartToken();
|
|
135
|
-
|
|
142
|
+
const cartId = await getCartId();
|
|
143
|
+
if (cartToken && cartId) {
|
|
136
144
|
try {
|
|
137
|
-
await getClient().
|
|
145
|
+
await getClient().carts.associate(cartId, {
|
|
138
146
|
token: result.token,
|
|
139
147
|
spreeToken: cartToken
|
|
140
148
|
});
|
|
@@ -156,9 +164,10 @@ async function register(params) {
|
|
|
156
164
|
const result = await getClient().customers.create(params);
|
|
157
165
|
await setAccessToken(result.token);
|
|
158
166
|
const cartToken = await getCartToken();
|
|
159
|
-
|
|
167
|
+
const cartId = await getCartId();
|
|
168
|
+
if (cartToken && cartId) {
|
|
160
169
|
try {
|
|
161
|
-
await getClient().
|
|
170
|
+
await getClient().carts.associate(cartId, {
|
|
162
171
|
token: result.token,
|
|
163
172
|
spreeToken: cartToken
|
|
164
173
|
});
|
package/dist/actions/auth.js.map
CHANGED
|
@@ -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 { Customer } 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 spreeToken: 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 params: {\n email: string;\n password: string;\n password_confirmation: string;\n first_name?: string;\n last_name?: string;\n phone?: string;\n accepts_email_marketing?: boolean;\n metadata?: Record<string, unknown>;\n }\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().customers.create(params);\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 spreeToken: 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<Customer | 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: {\n first_name?: string;\n last_name?: string;\n email?: string;\n password?: string;\n password_confirmation?: string;\n /** Required when changing email or password */\n current_password?: string;\n phone?: string;\n accepts_email_marketing?: boolean;\n metadata?: Record<string, unknown>;\n }\n): Promise<Customer> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().customer.update(data, options);\n });\n revalidateTag('customer');\n return result;\n}\n","import { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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\n// --- Checkout Options (combined cart + access tokens for checkout/payment actions) ---\n\nexport async function getCheckoutOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\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 { Customer } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { setAccessToken, clearAccessToken, getAccessToken, getCartToken, getCartId } 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 const cartId = await getCartId();\n if (cartToken && cartId) {\n try {\n await getClient().carts.associate(cartId, {\n token: result.token,\n spreeToken: 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 params: {\n email: string;\n password: string;\n password_confirmation: string;\n first_name?: string;\n last_name?: string;\n phone?: string;\n accepts_email_marketing?: boolean;\n metadata?: Record<string, unknown>;\n }\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().customers.create(params);\n await setAccessToken(result.token);\n\n // Associate guest cart\n const cartToken = await getCartToken();\n const cartId = await getCartId();\n if (cartToken && cartId) {\n try {\n await getClient().carts.associate(cartId, {\n token: result.token,\n spreeToken: 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<Customer | 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: {\n first_name?: string;\n last_name?: string;\n email?: string;\n password?: string;\n password_confirmation?: string;\n /** Required when changing email or password */\n current_password?: string;\n phone?: string;\n accepts_email_marketing?: boolean;\n metadata?: Record<string, unknown>;\n }\n): Promise<Customer> {\n const result = await withAuthRefresh(async (options) => {\n return getClient().customer.update(data, options);\n });\n revalidateTag('customer');\n return result;\n}\n","import { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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 getCartIdCookieName(): string {\n return `${getCartCookieName()}_id`;\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// --- Cart ID (prefixed ID stored alongside token for REST API) ---\n\nexport async function getCartId(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartIdCookieName())?.value;\n}\n\nexport async function setCartId(id: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), id, {\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 clearCartId(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), '', {\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\n// --- Cart Options (combined cart + access tokens for cart/checkout/payment actions) ---\n\nexport async function getCartOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\n}\n\n// --- Cart ID (required) ---\n\nexport async function requireCartId(): Promise<string> {\n const cartId = await getCartId();\n if (!cartId) throw new Error('No cart found');\n return cartId;\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":[]}
|
package/dist/actions/cart.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cart, CreateCartParams } from '@spree/sdk';
|
|
1
|
+
import { Cart, Order, CreateCartParams, ListResponse, Shipment, UpdateCartParams } from '@spree/sdk';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Get the current cart. Returns null if no cart exists.
|
|
@@ -6,7 +6,7 @@ import { Cart, CreateCartParams } from '@spree/sdk';
|
|
|
6
6
|
declare function getCart(): Promise<Cart | null>;
|
|
7
7
|
/**
|
|
8
8
|
* Get existing cart or create a new one.
|
|
9
|
-
* @param params - Optional cart creation params (metadata,
|
|
9
|
+
* @param params - Optional cart creation params (metadata, items)
|
|
10
10
|
*/
|
|
11
11
|
declare function getOrCreateCart(params?: CreateCartParams): Promise<Cart>;
|
|
12
12
|
/**
|
|
@@ -17,16 +17,6 @@ declare function addItem(variantId: string, quantity?: number, metadata?: Record
|
|
|
17
17
|
/**
|
|
18
18
|
* Update a line item in the cart (quantity and/or metadata).
|
|
19
19
|
* Returns the updated cart with recalculated totals.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // Update quantity only
|
|
23
|
-
* await updateItem(lineItemId, { quantity: 3 })
|
|
24
|
-
*
|
|
25
|
-
* // Update metadata only
|
|
26
|
-
* await updateItem(lineItemId, { metadata: { gift_message: 'Happy Birthday!' } })
|
|
27
|
-
*
|
|
28
|
-
* // Update both
|
|
29
|
-
* await updateItem(lineItemId, { quantity: 2, metadata: { engraving: 'J.D.' } })
|
|
30
20
|
*/
|
|
31
21
|
declare function updateItem(lineItemId: string, params: {
|
|
32
22
|
quantity?: number;
|
|
@@ -46,5 +36,30 @@ declare function clearCart(): Promise<void>;
|
|
|
46
36
|
* Call this after login/register when the user has an existing guest cart.
|
|
47
37
|
*/
|
|
48
38
|
declare function associateCart(): Promise<Cart | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Update cart info (email, addresses, special instructions).
|
|
41
|
+
*/
|
|
42
|
+
declare function updateCart(params: UpdateCartParams): Promise<Cart>;
|
|
43
|
+
/**
|
|
44
|
+
* Get shipments with shipping rates for the current cart.
|
|
45
|
+
*/
|
|
46
|
+
declare function getShipments(): Promise<ListResponse<Shipment>>;
|
|
47
|
+
/**
|
|
48
|
+
* Select a shipping rate for a shipment.
|
|
49
|
+
* Returns the updated cart with recalculated totals.
|
|
50
|
+
*/
|
|
51
|
+
declare function selectShippingRate(shipmentId: string, shippingRateId: string): Promise<Cart>;
|
|
52
|
+
/**
|
|
53
|
+
* Apply a coupon code to the cart.
|
|
54
|
+
*/
|
|
55
|
+
declare function applyCoupon(code: string): Promise<Cart>;
|
|
56
|
+
/**
|
|
57
|
+
* Remove a coupon code from the cart.
|
|
58
|
+
*/
|
|
59
|
+
declare function removeCoupon(code: string): Promise<Cart>;
|
|
60
|
+
/**
|
|
61
|
+
* Complete the checkout and place the order.
|
|
62
|
+
*/
|
|
63
|
+
declare function complete(): Promise<Order>;
|
|
49
64
|
|
|
50
|
-
export { addItem, associateCart, clearCart, getCart, getOrCreateCart, removeItem, updateItem };
|
|
65
|
+
export { addItem, applyCoupon, associateCart, clearCart, complete, getCart, getOrCreateCart, getShipments, removeCoupon, removeItem, selectShippingRate, updateCart, updateItem };
|
package/dist/actions/cart.js
CHANGED
|
@@ -48,6 +48,9 @@ function getCartCookieName() {
|
|
|
48
48
|
return DEFAULT_CART_COOKIE;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
function getCartIdCookieName() {
|
|
52
|
+
return `${getCartCookieName()}_id`;
|
|
53
|
+
}
|
|
51
54
|
function getAccessTokenCookieName() {
|
|
52
55
|
try {
|
|
53
56
|
return getConfig().accessTokenCookieName ?? DEFAULT_ACCESS_TOKEN_COOKIE;
|
|
@@ -76,22 +79,65 @@ async function clearCartToken() {
|
|
|
76
79
|
path: "/"
|
|
77
80
|
});
|
|
78
81
|
}
|
|
82
|
+
async function getCartId() {
|
|
83
|
+
const cookieStore = await cookies();
|
|
84
|
+
return cookieStore.get(getCartIdCookieName())?.value;
|
|
85
|
+
}
|
|
86
|
+
async function setCartId(id) {
|
|
87
|
+
const cookieStore = await cookies();
|
|
88
|
+
cookieStore.set(getCartIdCookieName(), id, {
|
|
89
|
+
httpOnly: true,
|
|
90
|
+
secure: process.env.NODE_ENV === "production",
|
|
91
|
+
sameSite: "lax",
|
|
92
|
+
path: "/",
|
|
93
|
+
maxAge: CART_TOKEN_MAX_AGE
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function clearCartId() {
|
|
97
|
+
const cookieStore = await cookies();
|
|
98
|
+
cookieStore.set(getCartIdCookieName(), "", {
|
|
99
|
+
maxAge: -1,
|
|
100
|
+
path: "/"
|
|
101
|
+
});
|
|
102
|
+
}
|
|
79
103
|
async function getAccessToken() {
|
|
80
104
|
const cookieStore = await cookies();
|
|
81
105
|
return cookieStore.get(getAccessTokenCookieName())?.value;
|
|
82
106
|
}
|
|
107
|
+
async function getCartOptions() {
|
|
108
|
+
const spreeToken = await getCartToken();
|
|
109
|
+
const token = await getAccessToken();
|
|
110
|
+
return { spreeToken, token };
|
|
111
|
+
}
|
|
112
|
+
async function requireCartId() {
|
|
113
|
+
const cartId = await getCartId();
|
|
114
|
+
if (!cartId) throw new Error("No cart found");
|
|
115
|
+
return cartId;
|
|
116
|
+
}
|
|
83
117
|
|
|
84
118
|
// src/actions/cart.ts
|
|
85
119
|
async function getCart() {
|
|
86
120
|
const spreeToken = await getCartToken();
|
|
87
121
|
const token = await getAccessToken();
|
|
88
|
-
|
|
122
|
+
const cartId = await getCartId();
|
|
123
|
+
if (!cartId && !token) return null;
|
|
89
124
|
try {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (spreeToken) {
|
|
93
|
-
await clearCartToken();
|
|
125
|
+
if (cartId) {
|
|
126
|
+
return await getClient().carts.get(cartId, { spreeToken, token });
|
|
94
127
|
}
|
|
128
|
+
if (token) {
|
|
129
|
+
const response = await getClient().carts.list({ token });
|
|
130
|
+
if (response.data.length > 0) {
|
|
131
|
+
const cart = response.data[0];
|
|
132
|
+
await setCartId(cart.id);
|
|
133
|
+
if (cart.token) await setCartToken(cart.token);
|
|
134
|
+
return cart;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
} catch {
|
|
139
|
+
await clearCartToken();
|
|
140
|
+
await clearCartId();
|
|
95
141
|
return null;
|
|
96
142
|
}
|
|
97
143
|
}
|
|
@@ -100,72 +146,127 @@ async function getOrCreateCart(params) {
|
|
|
100
146
|
if (existing) return existing;
|
|
101
147
|
const token = await getAccessToken();
|
|
102
148
|
const cartParams = params && Object.keys(params).length > 0 ? params : void 0;
|
|
103
|
-
const cart = await getClient().
|
|
149
|
+
const cart = await getClient().carts.create(cartParams, token ? { token } : void 0);
|
|
104
150
|
if (cart.token) {
|
|
105
151
|
await setCartToken(cart.token);
|
|
106
152
|
}
|
|
153
|
+
await setCartId(cart.id);
|
|
107
154
|
revalidateTag("cart");
|
|
108
155
|
return cart;
|
|
109
156
|
}
|
|
110
157
|
async function addItem(variantId, quantity = 1, metadata) {
|
|
111
|
-
await getOrCreateCart();
|
|
158
|
+
const cart = await getOrCreateCart();
|
|
112
159
|
const spreeToken = await getCartToken();
|
|
113
160
|
const token = await getAccessToken();
|
|
114
|
-
const
|
|
161
|
+
const updatedCart = await getClient().carts.items.create(
|
|
162
|
+
cart.id,
|
|
115
163
|
{ variant_id: variantId, quantity, metadata },
|
|
116
164
|
{ spreeToken, token }
|
|
117
165
|
);
|
|
118
166
|
revalidateTag("cart");
|
|
119
|
-
return
|
|
167
|
+
return updatedCart;
|
|
120
168
|
}
|
|
121
169
|
async function updateItem(lineItemId, params) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
const options = await getCartOptions();
|
|
171
|
+
const cartId = await requireCartId();
|
|
172
|
+
const cart = await getClient().carts.items.update(
|
|
173
|
+
cartId,
|
|
126
174
|
lineItemId,
|
|
127
175
|
params,
|
|
128
|
-
|
|
176
|
+
options
|
|
129
177
|
);
|
|
130
178
|
revalidateTag("cart");
|
|
131
179
|
return cart;
|
|
132
180
|
}
|
|
133
181
|
async function removeItem(lineItemId) {
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
const cart = await getClient().cart.items.delete(lineItemId, {
|
|
138
|
-
spreeToken,
|
|
139
|
-
token
|
|
140
|
-
});
|
|
182
|
+
const options = await getCartOptions();
|
|
183
|
+
const cartId = await requireCartId();
|
|
184
|
+
const cart = await getClient().carts.items.delete(cartId, lineItemId, options);
|
|
141
185
|
revalidateTag("cart");
|
|
142
186
|
return cart;
|
|
143
187
|
}
|
|
144
188
|
async function clearCart() {
|
|
145
189
|
await clearCartToken();
|
|
190
|
+
await clearCartId();
|
|
146
191
|
revalidateTag("cart");
|
|
147
192
|
}
|
|
148
193
|
async function associateCart() {
|
|
149
194
|
const spreeToken = await getCartToken();
|
|
150
195
|
const token = await getAccessToken();
|
|
151
|
-
|
|
196
|
+
const cartId = await getCartId();
|
|
197
|
+
if (!cartId || !token) return null;
|
|
152
198
|
try {
|
|
153
|
-
const result = await getClient().
|
|
199
|
+
const result = await getClient().carts.associate(cartId, { spreeToken, token });
|
|
154
200
|
revalidateTag("cart");
|
|
155
201
|
return result;
|
|
156
202
|
} catch {
|
|
157
203
|
await clearCartToken();
|
|
204
|
+
await clearCartId();
|
|
158
205
|
revalidateTag("cart");
|
|
159
206
|
return null;
|
|
160
207
|
}
|
|
161
208
|
}
|
|
209
|
+
async function updateCart(params) {
|
|
210
|
+
const options = await getCartOptions();
|
|
211
|
+
const cartId = await requireCartId();
|
|
212
|
+
const result = await getClient().carts.update(cartId, params, options);
|
|
213
|
+
revalidateTag("checkout");
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
async function getShipments() {
|
|
217
|
+
const options = await getCartOptions();
|
|
218
|
+
const cartId = await requireCartId();
|
|
219
|
+
return getClient().carts.shipments.list(cartId, options);
|
|
220
|
+
}
|
|
221
|
+
async function selectShippingRate(shipmentId, shippingRateId) {
|
|
222
|
+
const options = await getCartOptions();
|
|
223
|
+
const cartId = await requireCartId();
|
|
224
|
+
const result = await getClient().carts.shipments.update(
|
|
225
|
+
cartId,
|
|
226
|
+
shipmentId,
|
|
227
|
+
{ selected_shipping_rate_id: shippingRateId },
|
|
228
|
+
options
|
|
229
|
+
);
|
|
230
|
+
revalidateTag("checkout");
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
async function applyCoupon(code) {
|
|
234
|
+
const options = await getCartOptions();
|
|
235
|
+
const cartId = await requireCartId();
|
|
236
|
+
const result = await getClient().carts.couponCodes.apply(cartId, code, options);
|
|
237
|
+
revalidateTag("checkout");
|
|
238
|
+
revalidateTag("cart");
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
async function removeCoupon(code) {
|
|
242
|
+
const options = await getCartOptions();
|
|
243
|
+
const cartId = await requireCartId();
|
|
244
|
+
const result = await getClient().carts.couponCodes.remove(cartId, code, options);
|
|
245
|
+
revalidateTag("checkout");
|
|
246
|
+
revalidateTag("cart");
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
async function complete() {
|
|
250
|
+
const options = await getCartOptions();
|
|
251
|
+
const cartId = await requireCartId();
|
|
252
|
+
const result = await getClient().carts.complete(cartId, options);
|
|
253
|
+
revalidateTag("checkout");
|
|
254
|
+
revalidateTag("cart");
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
162
257
|
export {
|
|
163
258
|
addItem,
|
|
259
|
+
applyCoupon,
|
|
164
260
|
associateCart,
|
|
165
261
|
clearCart,
|
|
262
|
+
complete,
|
|
166
263
|
getCart,
|
|
167
264
|
getOrCreateCart,
|
|
265
|
+
getShipments,
|
|
266
|
+
removeCoupon,
|
|
168
267
|
removeItem,
|
|
268
|
+
selectShippingRate,
|
|
269
|
+
updateCart,
|
|
169
270
|
updateItem
|
|
170
271
|
};
|
|
171
272
|
//# sourceMappingURL=cart.js.map
|
package/dist/actions/cart.js.map
CHANGED
|
@@ -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 { Cart, CreateCartParams } 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<Cart | null> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n if (!spreeToken && !token) return null;\n\n try {\n return await getClient().cart.get({ spreeToken, token });\n } catch {\n // Cart not found (e.g., order was completed) — clear stale token\n if (spreeToken) {\n await clearCartToken();\n }\n return null;\n }\n}\n\n/**\n * Get existing cart or create a new one.\n * @param params - Optional cart creation params (metadata, line_items)\n */\nexport async function getOrCreateCart(\n params?: CreateCartParams\n): Promise<Cart> {\n const existing = await getCart();\n if (existing) return existing;\n\n const token = await getAccessToken();\n const cartParams = params && Object.keys(params).length > 0 ? params : undefined;\n const cart = await getClient().cart.create(cartParams, 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 * Returns the updated cart with recalculated totals.\n */\nexport async function addItem(\n variantId: string,\n quantity: number = 1,\n metadata?: Record<string, unknown>\n): Promise<Cart> {\n await getOrCreateCart();\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n\n const cart = await getClient().cart.items.create(\n { variant_id: variantId, quantity, metadata },\n { spreeToken, token }\n );\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Update a line item in the cart (quantity and/or metadata).\n * Returns the updated cart with recalculated totals.\n *\n * @example\n * // Update quantity only\n * await updateItem(lineItemId, { quantity: 3 })\n *\n * // Update metadata only\n * await updateItem(lineItemId, { metadata: { gift_message: 'Happy Birthday!' } })\n *\n * // Update both\n * await updateItem(lineItemId, { quantity: 2, metadata: { engraving: 'J.D.' } })\n */\nexport async function updateItem(\n lineItemId: string,\n params: { quantity?: number; metadata?: Record<string, unknown> }\n): Promise<Cart> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n if (!spreeToken && !token) throw new Error('No cart found');\n\n const cart = await getClient().cart.items.update(\n lineItemId,\n params,\n { spreeToken, token }\n );\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Remove a line item from the cart.\n * Returns the updated cart with recalculated totals.\n */\nexport async function removeItem(lineItemId: string): Promise<Cart> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n if (!spreeToken && !token) throw new Error('No cart found');\n\n const cart = await getClient().cart.items.delete(lineItemId, {\n spreeToken,\n token,\n });\n\n revalidateTag('cart');\n return 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<Cart | null> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n if (!spreeToken || !token) return null;\n\n try {\n const result = await getClient().cart.associate({ spreeToken, 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 { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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\n// --- Checkout Options (combined cart + access tokens for checkout/payment actions) ---\n\nexport async function getCheckoutOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\n}\n"],"mappings":"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","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 { Cart, Order, Shipment, CreateCartParams, UpdateCartParams, ListResponse } from '@spree/sdk';\nimport { getClient } from '../config';\nimport {\n getCartToken, setCartToken, clearCartToken,\n getCartId, setCartId, clearCartId,\n getAccessToken,\n getCartOptions, requireCartId,\n} from '../cookies';\n\n/**\n * Get the current cart. Returns null if no cart exists.\n */\nexport async function getCart(): Promise<Cart | null> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n const cartId = await getCartId();\n\n if (!cartId && !token) return null;\n\n try {\n if (cartId) {\n return await getClient().carts.get(cartId, { spreeToken, token });\n }\n\n // Authenticated user without stored cart ID — find their most recent cart\n if (token) {\n const response = await getClient().carts.list({ token });\n if (response.data.length > 0) {\n const cart = response.data[0];\n await setCartId(cart.id);\n if (cart.token) await setCartToken(cart.token);\n return cart;\n }\n }\n\n return null;\n } catch {\n // Cart not found (e.g., order was completed) — clear stale cookies\n await clearCartToken();\n await clearCartId();\n return null;\n }\n}\n\n/**\n * Get existing cart or create a new one.\n * @param params - Optional cart creation params (metadata, items)\n */\nexport async function getOrCreateCart(\n params?: CreateCartParams\n): Promise<Cart> {\n const existing = await getCart();\n if (existing) return existing;\n\n const token = await getAccessToken();\n const cartParams = params && Object.keys(params).length > 0 ? params : undefined;\n const cart = await getClient().carts.create(cartParams, token ? { token } : undefined);\n\n if (cart.token) {\n await setCartToken(cart.token);\n }\n await setCartId(cart.id);\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Add an item to the cart. Creates a cart if none exists.\n * Returns the updated cart with recalculated totals.\n */\nexport async function addItem(\n variantId: string,\n quantity: number = 1,\n metadata?: Record<string, unknown>\n): Promise<Cart> {\n const cart = await getOrCreateCart();\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n\n const updatedCart = await getClient().carts.items.create(\n cart.id,\n { variant_id: variantId, quantity, metadata },\n { spreeToken, token }\n );\n\n revalidateTag('cart');\n return updatedCart;\n}\n\n/**\n * Update a line item in the cart (quantity and/or metadata).\n * Returns the updated cart with recalculated totals.\n */\nexport async function updateItem(\n lineItemId: string,\n params: { quantity?: number; metadata?: Record<string, unknown> }\n): Promise<Cart> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n\n const cart = await getClient().carts.items.update(\n cartId,\n lineItemId,\n params,\n options\n );\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Remove a line item from the cart.\n * Returns the updated cart with recalculated totals.\n */\nexport async function removeItem(lineItemId: string): Promise<Cart> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n\n const cart = await getClient().carts.items.delete(cartId, lineItemId, options);\n\n revalidateTag('cart');\n return cart;\n}\n\n/**\n * Clear the cart (abandons the current cart).\n */\nexport async function clearCart(): Promise<void> {\n await clearCartToken();\n await clearCartId();\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<Cart | null> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n const cartId = await getCartId();\n if (!cartId || !token) return null;\n\n try {\n const result = await getClient().carts.associate(cartId, { spreeToken, token });\n revalidateTag('cart');\n return result;\n } catch {\n // Cart might already belong to another user — clear it\n await clearCartToken();\n await clearCartId();\n revalidateTag('cart');\n return null;\n }\n}\n\n/**\n * Update cart info (email, addresses, special instructions).\n */\nexport async function updateCart(\n params: UpdateCartParams\n): Promise<Cart> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n const result = await getClient().carts.update(cartId, params, options);\n revalidateTag('checkout');\n return result;\n}\n\n/**\n * Get shipments with shipping rates for the current cart.\n */\nexport async function getShipments(): Promise<ListResponse<Shipment>> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n return getClient().carts.shipments.list(cartId, options);\n}\n\n/**\n * Select a shipping rate for a shipment.\n * Returns the updated cart with recalculated totals.\n */\nexport async function selectShippingRate(\n shipmentId: string,\n shippingRateId: string\n): Promise<Cart> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n const result = await getClient().carts.shipments.update(\n cartId,\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 cart.\n */\nexport async function applyCoupon(\n code: string\n): Promise<Cart> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n const result = await getClient().carts.couponCodes.apply(cartId, code, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n\n/**\n * Remove a coupon code from the cart.\n */\nexport async function removeCoupon(\n code: string\n): Promise<Cart> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n const result = await getClient().carts.couponCodes.remove(cartId, code, 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(): Promise<Order> {\n const options = await getCartOptions();\n const cartId = await requireCartId();\n const result = await getClient().carts.complete(cartId, options);\n revalidateTag('checkout');\n revalidateTag('cart');\n return result;\n}\n","import { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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 getCartIdCookieName(): string {\n return `${getCartCookieName()}_id`;\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// --- Cart ID (prefixed ID stored alongside token for REST API) ---\n\nexport async function getCartId(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartIdCookieName())?.value;\n}\n\nexport async function setCartId(id: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), id, {\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 clearCartId(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), '', {\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\n// --- Cart Options (combined cart + access tokens for cart/checkout/payment actions) ---\n\nexport async function getCartOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\n}\n\n// --- Cart ID (required) ---\n\nexport async function requireCartId(): Promise<string> {\n const cartId = await getCartId();\n if (!cartId) throw new Error('No cart found');\n return cartId;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/actions/credit-cards.ts","../../src/auth-helpers.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { CreditCard } from '@spree/sdk';\nimport { withAuthRefresh } from '../auth-helpers';\nimport { getClient } from '../config';\n\n/**\n * List the authenticated customer's credit cards.\n */\nexport async function listCreditCards(): Promise<{ data: CreditCard[] }> {\n return withAuthRefresh(async (options) => {\n return getClient().customer.creditCards.list(undefined, options);\n });\n}\n\n/**\n * Delete a credit card.\n */\nexport async function deleteCreditCard(id: string): Promise<void> {\n await withAuthRefresh(async (options) => {\n return getClient().customer.creditCards.delete(id, options);\n });\n revalidateTag('credit-cards');\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 { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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\n// --- Checkout Options (combined cart + access tokens for checkout/payment actions) ---\n\nexport async function getCheckoutOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/actions/credit-cards.ts","../../src/auth-helpers.ts","../../src/config.ts","../../src/cookies.ts"],"sourcesContent":["'use server';\n\nimport { revalidateTag } from 'next/cache';\nimport type { CreditCard } from '@spree/sdk';\nimport { withAuthRefresh } from '../auth-helpers';\nimport { getClient } from '../config';\n\n/**\n * List the authenticated customer's credit cards.\n */\nexport async function listCreditCards(): Promise<{ data: CreditCard[] }> {\n return withAuthRefresh(async (options) => {\n return getClient().customer.creditCards.list(undefined, options);\n });\n}\n\n/**\n * Delete a credit card.\n */\nexport async function deleteCreditCard(id: string): Promise<void> {\n await withAuthRefresh(async (options) => {\n return getClient().customer.creditCards.delete(id, options);\n });\n revalidateTag('credit-cards');\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 { createClient, type Client } from '@spree/sdk';\nimport type { SpreeNextConfig } from './types';\n\nlet _client: Client | 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 = createClient({\n baseUrl: config.baseUrl,\n publishableKey: config.publishableKey,\n });\n}\n\n/**\n * Get the Client instance. Auto-initializes from env vars if needed.\n * @internal\n */\nexport function getClient(): Client {\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 getCartIdCookieName(): string {\n return `${getCartCookieName()}_id`;\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// --- Cart ID (prefixed ID stored alongside token for REST API) ---\n\nexport async function getCartId(): Promise<string | undefined> {\n const cookieStore = await cookies();\n return cookieStore.get(getCartIdCookieName())?.value;\n}\n\nexport async function setCartId(id: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), id, {\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 clearCartId(): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.set(getCartIdCookieName(), '', {\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\n// --- Cart Options (combined cart + access tokens for cart/checkout/payment actions) ---\n\nexport async function getCartOptions(): Promise<{\n spreeToken: string | undefined;\n token: string | undefined;\n}> {\n const spreeToken = await getCartToken();\n const token = await getAccessToken();\n return { spreeToken, token };\n}\n\n// --- Cart ID (required) ---\n\nexport async function requireCartId(): Promise<string> {\n const cartId = await getCartId();\n if (!cartId) throw new Error('No cart found');\n return cartId;\n}\n"],"mappings":"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","names":[]}
|