@spree/next 0.5.0 → 0.6.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/LICENSE +21 -0
- package/README.md +43 -8
- package/dist/actions/cart.d.ts +18 -4
- package/dist/actions/cart.js +7 -6
- package/dist/actions/cart.js.map +1 -1
- package/dist/actions/locale.d.ts +10 -0
- package/dist/actions/locale.js +59 -0
- package/dist/actions/locale.js.map +1 -0
- package/dist/actions/orders.d.ts +2 -2
- package/dist/actions/orders.js.map +1 -1
- package/dist/data/countries.d.ts +2 -0
- package/dist/data/countries.js +26 -8
- package/dist/data/countries.js.map +1 -1
- package/dist/data/currencies.d.ts +1 -0
- package/dist/data/currencies.js +24 -4
- package/dist/data/currencies.js.map +1 -1
- package/dist/data/locales.d.ts +1 -0
- package/dist/data/locales.js +24 -4
- package/dist/data/locales.js.map +1 -1
- package/dist/data/products.d.ts +5 -2
- package/dist/data/products.js +28 -12
- package/dist/data/products.js.map +1 -1
- package/dist/data/store.d.ts +1 -0
- package/dist/data/store.js +24 -4
- package/dist/data/store.js.map +1 -1
- package/dist/data/taxonomies.d.ts +2 -0
- package/dist/data/taxonomies.js +26 -8
- package/dist/data/taxonomies.js.map +1 -1
- package/dist/data/taxons.d.ts +6 -3
- package/dist/data/taxons.js +28 -12
- package/dist/data/taxons.js.map +1 -1
- package/dist/index.d.ts +31 -8
- package/dist/index.js +65 -60
- package/dist/index.js.map +1 -1
- package/dist/locale.d.ts +12 -0
- package/dist/locale.js +52 -0
- package/dist/locale.js.map +1 -0
- package/dist/middleware.d.ts +26 -0
- package/dist/middleware.js +28 -0
- package/dist/middleware.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/package.json +10 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vendo Connect Inc., Vendo Sp. z o.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ import { listProducts, getProduct, listTaxons } from '@spree/next';
|
|
|
40
40
|
|
|
41
41
|
export default async function ProductsPage() {
|
|
42
42
|
const products = await listProducts({ per_page: 12 });
|
|
43
|
-
const categories = await listTaxons({
|
|
43
|
+
const categories = await listTaxons({ depth_eq: 1 });
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<div>
|
|
@@ -94,7 +94,7 @@ Plain async functions for reading data in Server Components. Wrap with `"use cac
|
|
|
94
94
|
import { listProducts, getProduct, getProductFilters } from '@spree/next';
|
|
95
95
|
|
|
96
96
|
const products = await listProducts({ per_page: 25, includes: 'variants,images' });
|
|
97
|
-
const product = await getProduct('
|
|
97
|
+
const product = await getProduct('spree-tote');
|
|
98
98
|
const filters = await getProductFilters({ taxon_id: 'txn_123' });
|
|
99
99
|
```
|
|
100
100
|
|
|
@@ -104,7 +104,7 @@ const filters = await getProductFilters({ taxon_id: 'txn_123' });
|
|
|
104
104
|
import { listTaxons, getTaxon, listTaxonProducts } from '@spree/next';
|
|
105
105
|
import { listTaxonomies, getTaxonomy } from '@spree/next';
|
|
106
106
|
|
|
107
|
-
const taxons = await listTaxons({
|
|
107
|
+
const taxons = await listTaxons({ depth_eq: 1 });
|
|
108
108
|
const taxon = await getTaxon('categories/clothing');
|
|
109
109
|
const products = await listTaxonProducts('categories/clothing', { per_page: 12 });
|
|
110
110
|
|
|
@@ -133,8 +133,9 @@ import { getCart, getOrCreateCart, addItem, updateItem, removeItem, clearCart }
|
|
|
133
133
|
|
|
134
134
|
const cart = await getCart();
|
|
135
135
|
const cart = await getOrCreateCart();
|
|
136
|
-
await addItem(variantId, quantity);
|
|
137
|
-
await updateItem(lineItemId, quantity);
|
|
136
|
+
await addItem(variantId, quantity, { gift_message: 'Happy Birthday!' });
|
|
137
|
+
await updateItem(lineItemId, { quantity: 3 });
|
|
138
|
+
await updateItem(lineItemId, { metadata: { engraving: 'J.D.' } });
|
|
138
139
|
await removeItem(lineItemId);
|
|
139
140
|
await clearCart();
|
|
140
141
|
```
|
|
@@ -239,11 +240,45 @@ const giftCard = await getGiftCard(giftCardId);
|
|
|
239
240
|
|
|
240
241
|
## Localization
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
### Automatic (recommended)
|
|
244
|
+
|
|
245
|
+
Data functions automatically read locale and country from cookies. Use the included middleware to handle URL-based routing and cookie persistence:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
// middleware.ts
|
|
249
|
+
import { createSpreeMiddleware } from '@spree/next/middleware';
|
|
250
|
+
|
|
251
|
+
export default createSpreeMiddleware({
|
|
252
|
+
defaultCountry: 'us',
|
|
253
|
+
defaultLocale: 'en',
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
export const config = {
|
|
257
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\..*$).*)'],
|
|
258
|
+
};
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Data functions work without any locale arguments:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
const products = await listProducts({ per_page: 10 });
|
|
265
|
+
const taxon = await getTaxon('categories/clothing');
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Use the `setLocale` server action in country/language switchers:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { setLocale } from '@spree/next';
|
|
272
|
+
|
|
273
|
+
await setLocale({ country: 'de', locale: 'de' });
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Manual override
|
|
277
|
+
|
|
278
|
+
You can still pass locale options explicitly — they override auto-detected values:
|
|
243
279
|
|
|
244
280
|
```typescript
|
|
245
|
-
const products = await listProducts({ per_page: 10 }, { locale: 'fr',
|
|
246
|
-
const taxon = await getTaxon('categories/clothing', {}, { locale: 'de', currency: 'EUR' });
|
|
281
|
+
const products = await listProducts({ per_page: 10 }, { locale: 'fr', country: 'FR' });
|
|
247
282
|
```
|
|
248
283
|
|
|
249
284
|
## TypeScript
|
package/dist/actions/cart.d.ts
CHANGED
|
@@ -8,20 +8,34 @@ declare function getCart(): Promise<(StoreOrder & {
|
|
|
8
8
|
}) | null>;
|
|
9
9
|
/**
|
|
10
10
|
* Get existing cart or create a new one.
|
|
11
|
+
* @param metadata - Optional metadata to set on the cart when creating a new one
|
|
11
12
|
*/
|
|
12
|
-
declare function getOrCreateCart(): Promise<StoreOrder & {
|
|
13
|
+
declare function getOrCreateCart(metadata?: Record<string, unknown>): Promise<StoreOrder & {
|
|
13
14
|
token: string;
|
|
14
15
|
}>;
|
|
15
16
|
/**
|
|
16
17
|
* Add an item to the cart. Creates a cart if none exists.
|
|
17
18
|
* Returns the updated order with recalculated totals.
|
|
18
19
|
*/
|
|
19
|
-
declare function addItem(variantId: string, quantity?: number): Promise<StoreOrder>;
|
|
20
|
+
declare function addItem(variantId: string, quantity?: number, metadata?: Record<string, unknown>): Promise<StoreOrder>;
|
|
20
21
|
/**
|
|
21
|
-
* Update a line item
|
|
22
|
+
* Update a line item in the cart (quantity and/or metadata).
|
|
22
23
|
* Returns the updated order with recalculated totals.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Update quantity only
|
|
27
|
+
* await updateItem(lineItemId, { quantity: 3 })
|
|
28
|
+
*
|
|
29
|
+
* // Update metadata only
|
|
30
|
+
* await updateItem(lineItemId, { metadata: { gift_message: 'Happy Birthday!' } })
|
|
31
|
+
*
|
|
32
|
+
* // Update both
|
|
33
|
+
* await updateItem(lineItemId, { quantity: 2, metadata: { engraving: 'J.D.' } })
|
|
23
34
|
*/
|
|
24
|
-
declare function updateItem(lineItemId: string,
|
|
35
|
+
declare function updateItem(lineItemId: string, params: {
|
|
36
|
+
quantity?: number;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
}): Promise<StoreOrder>;
|
|
25
39
|
/**
|
|
26
40
|
* Remove a line item from the cart.
|
|
27
41
|
* Returns the updated order with recalculated totals.
|
package/dist/actions/cart.js
CHANGED
|
@@ -95,30 +95,31 @@ async function getCart() {
|
|
|
95
95
|
return null;
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
-
async function getOrCreateCart() {
|
|
98
|
+
async function getOrCreateCart(metadata) {
|
|
99
99
|
const existing = await getCart();
|
|
100
100
|
if (existing) return existing;
|
|
101
101
|
const token = await getAccessToken();
|
|
102
|
-
const
|
|
102
|
+
const cartParams = metadata ? { metadata } : void 0;
|
|
103
|
+
const cart = await getClient().store.cart.create(cartParams, token ? { token } : void 0);
|
|
103
104
|
if (cart.token) {
|
|
104
105
|
await setCartToken(cart.token);
|
|
105
106
|
}
|
|
106
107
|
revalidateTag("cart");
|
|
107
108
|
return cart;
|
|
108
109
|
}
|
|
109
|
-
async function addItem(variantId, quantity = 1) {
|
|
110
|
+
async function addItem(variantId, quantity = 1, metadata) {
|
|
110
111
|
const cart = await getOrCreateCart();
|
|
111
112
|
const orderToken = cart.token;
|
|
112
113
|
const token = await getAccessToken();
|
|
113
114
|
const order = await getClient().store.orders.lineItems.create(
|
|
114
115
|
cart.id,
|
|
115
|
-
{ variant_id: variantId, quantity },
|
|
116
|
+
{ variant_id: variantId, quantity, metadata },
|
|
116
117
|
{ orderToken, token }
|
|
117
118
|
);
|
|
118
119
|
revalidateTag("cart");
|
|
119
120
|
return order;
|
|
120
121
|
}
|
|
121
|
-
async function updateItem(lineItemId,
|
|
122
|
+
async function updateItem(lineItemId, params) {
|
|
122
123
|
const orderToken = await getCartToken();
|
|
123
124
|
const token = await getAccessToken();
|
|
124
125
|
if (!orderToken && !token) throw new Error("No cart found");
|
|
@@ -126,7 +127,7 @@ async function updateItem(lineItemId, quantity) {
|
|
|
126
127
|
const order = await getClient().store.orders.lineItems.update(
|
|
127
128
|
cart.id,
|
|
128
129
|
lineItemId,
|
|
129
|
-
|
|
130
|
+
params,
|
|
130
131
|
{ orderToken, token }
|
|
131
132
|
);
|
|
132
133
|
revalidateTag("cart");
|
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 { StoreOrder } 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 // Cart not found (e.g., order was completed) — clear stale token\n if (orderToken) {\n await clearCartToken();\n }\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 * Returns the updated order with recalculated totals.\n */\nexport async function addItem(\n variantId: string,\n quantity: number = 1\n): Promise<StoreOrder> {\n const cart = await getOrCreateCart();\n const orderToken = cart.token;\n const token = await getAccessToken();\n\n const order = await getClient().store.orders.lineItems.create(\n cart.id,\n { variant_id: variantId, quantity },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return order;\n}\n\n/**\n * Update a line item quantity in the cart.\n * Returns the updated order with recalculated totals.\n */\nexport async function updateItem(\n lineItemId: string,\n quantity: number\n): Promise<StoreOrder> {\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 order = await getClient().store.orders.lineItems.update(\n cart.id,\n lineItemId,\n { quantity },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return order;\n}\n\n/**\n * Remove a line item from the cart.\n * Returns the updated order with recalculated totals.\n */\nexport async function removeItem(lineItemId: string): Promise<StoreOrder> {\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 order = await getClient().store.orders.lineItems.delete(cart.id, lineItemId, {\n orderToken,\n token,\n });\n\n revalidateTag('cart');\n return order;\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":[]}
|
|
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 } 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 // Cart not found (e.g., order was completed) — clear stale token\n if (orderToken) {\n await clearCartToken();\n }\n return null;\n }\n}\n\n/**\n * Get existing cart or create a new one.\n * @param metadata - Optional metadata to set on the cart when creating a new one\n */\nexport async function getOrCreateCart(\n metadata?: Record<string, unknown>\n): Promise<StoreOrder & { token: string }> {\n const existing = await getCart();\n if (existing) return existing;\n\n const token = await getAccessToken();\n const cartParams = metadata ? { metadata } : undefined;\n const cart = await getClient().store.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 order with recalculated totals.\n */\nexport async function addItem(\n variantId: string,\n quantity: number = 1,\n metadata?: Record<string, unknown>\n): Promise<StoreOrder> {\n const cart = await getOrCreateCart();\n const orderToken = cart.token;\n const token = await getAccessToken();\n\n const order = await getClient().store.orders.lineItems.create(\n cart.id,\n { variant_id: variantId, quantity, metadata },\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return order;\n}\n\n/**\n * Update a line item in the cart (quantity and/or metadata).\n * Returns the updated order 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<StoreOrder> {\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 order = await getClient().store.orders.lineItems.update(\n cart.id,\n lineItemId,\n params,\n { orderToken, token }\n );\n\n revalidateTag('cart');\n return order;\n}\n\n/**\n * Remove a line item from the cart.\n * Returns the updated order with recalculated totals.\n */\nexport async function removeItem(lineItemId: string): Promise<StoreOrder> {\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 order = await getClient().store.orders.lineItems.delete(cart.id, lineItemId, {\n orderToken,\n token,\n });\n\n revalidateTag('cart');\n return order;\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":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set locale/country cookies for subsequent requests.
|
|
3
|
+
* Use this in country/language switchers instead of manipulating cookies directly.
|
|
4
|
+
*/
|
|
5
|
+
declare function setLocale(params: {
|
|
6
|
+
country?: string;
|
|
7
|
+
locale?: string;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
|
|
10
|
+
export { setLocale };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
// src/actions/locale.ts
|
|
4
|
+
import { cookies } from "next/headers";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
import { createSpreeClient } from "@spree/sdk";
|
|
8
|
+
var _client = null;
|
|
9
|
+
var _config = null;
|
|
10
|
+
function initSpreeNext(config) {
|
|
11
|
+
_config = config;
|
|
12
|
+
_client = createSpreeClient({
|
|
13
|
+
baseUrl: config.baseUrl,
|
|
14
|
+
publishableKey: config.publishableKey
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function getClient() {
|
|
18
|
+
if (!_client) {
|
|
19
|
+
const baseUrl = process.env.SPREE_API_URL;
|
|
20
|
+
const publishableKey = process.env.SPREE_PUBLISHABLE_KEY;
|
|
21
|
+
if (baseUrl && publishableKey) {
|
|
22
|
+
initSpreeNext({ baseUrl, publishableKey });
|
|
23
|
+
} else {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"@spree/next is not configured. Either call initSpreeNext() or set SPREE_API_URL and SPREE_PUBLISHABLE_KEY environment variables."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return _client;
|
|
30
|
+
}
|
|
31
|
+
function getConfig() {
|
|
32
|
+
if (!_config) {
|
|
33
|
+
getClient();
|
|
34
|
+
}
|
|
35
|
+
return _config;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/actions/locale.ts
|
|
39
|
+
async function setLocale(params) {
|
|
40
|
+
const config = getConfig();
|
|
41
|
+
const cookieStore = await cookies();
|
|
42
|
+
const maxAge = 60 * 60 * 24 * 365;
|
|
43
|
+
if (params.country) {
|
|
44
|
+
cookieStore.set(config.countryCookieName ?? "spree_country", params.country, {
|
|
45
|
+
path: "/",
|
|
46
|
+
maxAge
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (params.locale) {
|
|
50
|
+
cookieStore.set(config.localeCookieName ?? "spree_locale", params.locale, {
|
|
51
|
+
path: "/",
|
|
52
|
+
maxAge
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
setLocale
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=locale.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/actions/locale.ts","../../src/config.ts"],"sourcesContent":["'use server';\n\nimport { cookies } from 'next/headers';\nimport { getConfig } from '../config';\n\n/**\n * Set locale/country cookies for subsequent requests.\n * Use this in country/language switchers instead of manipulating cookies directly.\n */\nexport async function setLocale(params: {\n country?: string;\n locale?: string;\n}): Promise<void> {\n const config = getConfig();\n const cookieStore = await cookies();\n const maxAge = 60 * 60 * 24 * 365; // 1 year\n\n if (params.country) {\n cookieStore.set(config.countryCookieName ?? 'spree_country', params.country, {\n path: '/',\n maxAge,\n });\n }\n if (params.locale) {\n cookieStore.set(config.localeCookieName ?? 'spree_locale', params.locale, {\n path: '/',\n maxAge,\n });\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"],"mappings":";;;AAEA,SAAS,eAAe;;;ACFxB,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,gBAAgB,OAAO;AAAA,EACzB,CAAC;AACH;AAMO,SAAS,YAAyB;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,iBAAiB,QAAQ,IAAI;AACnC,QAAI,WAAW,gBAAgB;AAC7B,oBAAc,EAAE,SAAS,eAAe,CAAC;AAAA,IAC3C,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;;;ADtCA,eAAsB,UAAU,QAGd;AAChB,QAAM,SAAS,UAAU;AACzB,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,OAAO,SAAS;AAClB,gBAAY,IAAI,OAAO,qBAAqB,iBAAiB,OAAO,SAAS;AAAA,MAC3E,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAAA,EACH;AACA,MAAI,OAAO,QAAQ;AACjB,gBAAY,IAAI,OAAO,oBAAoB,gBAAgB,OAAO,QAAQ;AAAA,MACxE,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
|
package/dist/actions/orders.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { StoreOrder, PaginatedResponse } from '@spree/sdk';
|
|
1
|
+
import { StoreOrder, OrderListParams, PaginatedResponse } from '@spree/sdk';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* List the authenticated customer's orders.
|
|
5
5
|
*/
|
|
6
|
-
declare function listOrders(params?:
|
|
6
|
+
declare function listOrders(params?: OrderListParams): Promise<PaginatedResponse<StoreOrder>>;
|
|
7
7
|
/**
|
|
8
8
|
* Get a single order by ID or number.
|
|
9
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth-helpers.ts","../../src/config.ts","../../src/cookies.ts","../../src/actions/orders.ts"],"sourcesContent":["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","'use server';\n\nimport type { StoreOrder, PaginatedResponse } from '@spree/sdk';\nimport { withAuthRefresh } from '../auth-helpers';\nimport { getClient } from '../config';\n\n/**\n * List the authenticated customer's orders.\n */\nexport async function listOrders(\n params?:
|
|
1
|
+
{"version":3,"sources":["../../src/auth-helpers.ts","../../src/config.ts","../../src/cookies.ts","../../src/actions/orders.ts"],"sourcesContent":["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","'use server';\n\nimport type { StoreOrder, PaginatedResponse, OrderListParams } from '@spree/sdk';\nimport { withAuthRefresh } from '../auth-helpers';\nimport { getClient } from '../config';\n\n/**\n * List the authenticated customer's orders.\n */\nexport async function listOrders(\n params?: OrderListParams\n): Promise<PaginatedResponse<StoreOrder>> {\n return withAuthRefresh(async (options) => {\n return getClient().store.customer.orders.list(params, options);\n });\n}\n\n/**\n * Get a single order by ID or number.\n */\nexport async function getOrder(\n idOrNumber: string,\n params?: Record<string, unknown>\n): Promise<StoreOrder> {\n return withAuthRefresh(async (options) => {\n return getClient().store.orders.get(idOrNumber, params, options);\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/data/countries.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { SpreeNextOptions } from '../types.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* List countries available in the store.
|
|
6
6
|
* Each country includes currency and default_locale derived from its market.
|
|
7
|
+
* Locale/country are auto-read from cookies when not provided.
|
|
7
8
|
*/
|
|
8
9
|
declare function listCountries(options?: SpreeNextOptions): Promise<{
|
|
9
10
|
data: StoreCountry[];
|
|
@@ -12,6 +13,7 @@ declare function listCountries(options?: SpreeNextOptions): Promise<{
|
|
|
12
13
|
* Get a country by ISO code.
|
|
13
14
|
* @param iso - ISO 3166-1 alpha-2 code (e.g., "US", "DE")
|
|
14
15
|
* @param params - Optional params (e.g., { include: 'states' } for address forms)
|
|
16
|
+
* Locale/country are auto-read from cookies when not provided.
|
|
15
17
|
*/
|
|
16
18
|
declare function getCountry(iso: string, params?: {
|
|
17
19
|
include?: string;
|
package/dist/data/countries.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createSpreeClient } from '@spree/sdk';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
2
3
|
|
|
3
4
|
// src/config.ts
|
|
4
5
|
var _client = null;
|
|
6
|
+
var _config = null;
|
|
5
7
|
function initSpreeNext(config) {
|
|
8
|
+
_config = config;
|
|
6
9
|
_client = createSpreeClient({
|
|
7
10
|
baseUrl: config.baseUrl,
|
|
8
11
|
publishableKey: config.publishableKey
|
|
@@ -22,19 +25,34 @@ function getClient() {
|
|
|
22
25
|
}
|
|
23
26
|
return _client;
|
|
24
27
|
}
|
|
28
|
+
function getConfig() {
|
|
29
|
+
if (!_config) {
|
|
30
|
+
getClient();
|
|
31
|
+
}
|
|
32
|
+
return _config;
|
|
33
|
+
}
|
|
34
|
+
var DEFAULT_COUNTRY_COOKIE = "spree_country";
|
|
35
|
+
var DEFAULT_LOCALE_COOKIE = "spree_locale";
|
|
36
|
+
async function getLocaleOptions() {
|
|
37
|
+
const config = getConfig();
|
|
38
|
+
const cookieStore = await cookies();
|
|
39
|
+
const country = cookieStore.get(config.countryCookieName ?? DEFAULT_COUNTRY_COOKIE)?.value;
|
|
40
|
+
const locale = cookieStore.get(config.localeCookieName ?? DEFAULT_LOCALE_COOKIE)?.value;
|
|
41
|
+
return {
|
|
42
|
+
locale: locale || config.defaultLocale,
|
|
43
|
+
country: country || config.defaultCountry
|
|
44
|
+
// No currency — backend resolves from country via X-Spree-Country header
|
|
45
|
+
};
|
|
46
|
+
}
|
|
25
47
|
|
|
26
48
|
// src/data/countries.ts
|
|
27
49
|
async function listCountries(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
currency: options?.currency
|
|
31
|
-
});
|
|
50
|
+
const resolved = options ?? await getLocaleOptions();
|
|
51
|
+
return getClient().store.countries.list(resolved);
|
|
32
52
|
}
|
|
33
53
|
async function getCountry(iso, params, options) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
currency: options?.currency
|
|
37
|
-
});
|
|
54
|
+
const resolved = options ?? await getLocaleOptions();
|
|
55
|
+
return getClient().store.countries.get(iso, params, resolved);
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
export { getCountry, listCountries };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/config.ts","../../src/data/countries.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/config.ts","../../src/locale.ts","../../src/data/countries.ts"],"names":[],"mappings":";;;;AAGA,IAAI,OAAA,GAA8B,IAAA;AAClC,IAAI,OAAA,GAAkC,IAAA;AAO/B,SAAS,cAAc,MAAA,EAA+B;AAC3D,EAAA,OAAA,GAAU,MAAA;AACV,EAAA,OAAA,GAAU,iBAAA,CAAkB;AAAA,IAC1B,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,gBAAgB,MAAA,CAAO;AAAA,GACxB,CAAA;AACH;AAMO,SAAS,SAAA,GAAyB;AACvC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI,aAAA;AAC5B,IAAA,MAAM,cAAA,GAAiB,QAAQ,GAAA,CAAI,qBAAA;AACnC,IAAA,IAAI,WAAW,cAAA,EAAgB;AAC7B,MAAA,aAAA,CAAc,EAAE,OAAA,EAAS,cAAA,EAAgB,CAAA;AAAA,IAC3C,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAMO,SAAS,SAAA,GAA6B;AAC3C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,SAAA,EAAU;AAAA,EACZ;AACA,EAAA,OAAO,OAAA;AACT;AC5CA,IAAM,sBAAA,GAAyB,eAAA;AAC/B,IAAM,qBAAA,GAAwB,cAAA;AAO9B,eAAsB,gBAAA,GAInB;AACD,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,WAAA,GAAc,MAAM,OAAA,EAAQ;AAElC,EAAA,MAAM,UAAU,WAAA,CAAY,GAAA,CAAI,MAAA,CAAO,iBAAA,IAAqB,sBAAsB,CAAA,EAAG,KAAA;AACrF,EAAA,MAAM,SAAS,WAAA,CAAY,GAAA,CAAI,MAAA,CAAO,gBAAA,IAAoB,qBAAqB,CAAA,EAAG,KAAA;AAElF,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,UAAU,MAAA,CAAO,aAAA;AAAA,IACzB,OAAA,EAAS,WAAW,MAAA,CAAO;AAAA;AAAA,GAE7B;AACF;;;ACjBA,eAAsB,cACpB,OAAA,EACmC;AACnC,EAAA,MAAM,QAAA,GAAW,OAAA,IAAW,MAAM,gBAAA,EAAiB;AACnD,EAAA,OAAO,SAAA,EAAU,CAAE,KAAA,CAAM,SAAA,CAAU,KAAK,QAAQ,CAAA;AAClD;AAQA,eAAsB,UAAA,CACpB,GAAA,EACA,MAAA,EACA,OAAA,EACuB;AACvB,EAAA,MAAM,QAAA,GAAW,OAAA,IAAW,MAAM,gBAAA,EAAiB;AACnD,EAAA,OAAO,WAAU,CAAE,KAAA,CAAM,UAAU,GAAA,CAAI,GAAA,EAAK,QAAQ,QAAQ,CAAA;AAC9D","file":"countries.js","sourcesContent":["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_COUNTRY_COOKIE = 'spree_country';\nconst DEFAULT_LOCALE_COOKIE = 'spree_locale';\n\n/**\n * Read locale/currency/country from cookies (set by middleware).\n * Falls back to config defaults.\n * @internal\n */\nexport async function getLocaleOptions(): Promise<{\n locale?: string;\n currency?: string;\n country?: string;\n}> {\n const config = getConfig();\n const cookieStore = await cookies();\n\n const country = cookieStore.get(config.countryCookieName ?? DEFAULT_COUNTRY_COOKIE)?.value;\n const locale = cookieStore.get(config.localeCookieName ?? DEFAULT_LOCALE_COOKIE)?.value;\n\n return {\n locale: locale || config.defaultLocale,\n country: country || config.defaultCountry,\n // No currency — backend resolves from country via X-Spree-Country header\n };\n}\n","import type { StoreCountry } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { getLocaleOptions } from '../locale';\nimport type { SpreeNextOptions } from '../types';\n\n/**\n * List countries available in the store.\n * Each country includes currency and default_locale derived from its market.\n * Locale/country are auto-read from cookies when not provided.\n */\nexport async function listCountries(\n options?: SpreeNextOptions\n): Promise<{ data: StoreCountry[] }> {\n const resolved = options ?? await getLocaleOptions();\n return getClient().store.countries.list(resolved);\n}\n\n/**\n * Get a country by ISO code.\n * @param iso - ISO 3166-1 alpha-2 code (e.g., \"US\", \"DE\")\n * @param params - Optional params (e.g., { include: 'states' } for address forms)\n * Locale/country are auto-read from cookies when not provided.\n */\nexport async function getCountry(\n iso: string,\n params?: { include?: string },\n options?: SpreeNextOptions\n): Promise<StoreCountry> {\n const resolved = options ?? await getLocaleOptions();\n return getClient().store.countries.get(iso, params, resolved);\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { SpreeNextOptions } from '../types.js';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* List currencies supported by the store (derived from markets).
|
|
6
|
+
* Locale/country are auto-read from cookies when not provided.
|
|
6
7
|
*/
|
|
7
8
|
declare function listCurrencies(options?: SpreeNextOptions): Promise<{
|
|
8
9
|
data: StoreCurrency[];
|
package/dist/data/currencies.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createSpreeClient } from '@spree/sdk';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
2
3
|
|
|
3
4
|
// src/config.ts
|
|
4
5
|
var _client = null;
|
|
6
|
+
var _config = null;
|
|
5
7
|
function initSpreeNext(config) {
|
|
8
|
+
_config = config;
|
|
6
9
|
_client = createSpreeClient({
|
|
7
10
|
baseUrl: config.baseUrl,
|
|
8
11
|
publishableKey: config.publishableKey
|
|
@@ -22,13 +25,30 @@ function getClient() {
|
|
|
22
25
|
}
|
|
23
26
|
return _client;
|
|
24
27
|
}
|
|
28
|
+
function getConfig() {
|
|
29
|
+
if (!_config) {
|
|
30
|
+
getClient();
|
|
31
|
+
}
|
|
32
|
+
return _config;
|
|
33
|
+
}
|
|
34
|
+
var DEFAULT_COUNTRY_COOKIE = "spree_country";
|
|
35
|
+
var DEFAULT_LOCALE_COOKIE = "spree_locale";
|
|
36
|
+
async function getLocaleOptions() {
|
|
37
|
+
const config = getConfig();
|
|
38
|
+
const cookieStore = await cookies();
|
|
39
|
+
const country = cookieStore.get(config.countryCookieName ?? DEFAULT_COUNTRY_COOKIE)?.value;
|
|
40
|
+
const locale = cookieStore.get(config.localeCookieName ?? DEFAULT_LOCALE_COOKIE)?.value;
|
|
41
|
+
return {
|
|
42
|
+
locale: locale || config.defaultLocale,
|
|
43
|
+
country: country || config.defaultCountry
|
|
44
|
+
// No currency — backend resolves from country via X-Spree-Country header
|
|
45
|
+
};
|
|
46
|
+
}
|
|
25
47
|
|
|
26
48
|
// src/data/currencies.ts
|
|
27
49
|
async function listCurrencies(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
currency: options?.currency
|
|
31
|
-
});
|
|
50
|
+
const resolved = options ?? await getLocaleOptions();
|
|
51
|
+
return getClient().store.currencies.list(resolved);
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
export { listCurrencies };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/config.ts","../../src/data/currencies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/config.ts","../../src/locale.ts","../../src/data/currencies.ts"],"names":[],"mappings":";;;;AAGA,IAAI,OAAA,GAA8B,IAAA;AAClC,IAAI,OAAA,GAAkC,IAAA;AAO/B,SAAS,cAAc,MAAA,EAA+B;AAC3D,EAAA,OAAA,GAAU,MAAA;AACV,EAAA,OAAA,GAAU,iBAAA,CAAkB;AAAA,IAC1B,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,gBAAgB,MAAA,CAAO;AAAA,GACxB,CAAA;AACH;AAMO,SAAS,SAAA,GAAyB;AACvC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI,aAAA;AAC5B,IAAA,MAAM,cAAA,GAAiB,QAAQ,GAAA,CAAI,qBAAA;AACnC,IAAA,IAAI,WAAW,cAAA,EAAgB;AAC7B,MAAA,aAAA,CAAc,EAAE,OAAA,EAAS,cAAA,EAAgB,CAAA;AAAA,IAC3C,CAAA,MAAO;AACL,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAMO,SAAS,SAAA,GAA6B;AAC3C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,SAAA,EAAU;AAAA,EACZ;AACA,EAAA,OAAO,OAAA;AACT;AC5CA,IAAM,sBAAA,GAAyB,eAAA;AAC/B,IAAM,qBAAA,GAAwB,cAAA;AAO9B,eAAsB,gBAAA,GAInB;AACD,EAAA,MAAM,SAAS,SAAA,EAAU;AACzB,EAAA,MAAM,WAAA,GAAc,MAAM,OAAA,EAAQ;AAElC,EAAA,MAAM,UAAU,WAAA,CAAY,GAAA,CAAI,MAAA,CAAO,iBAAA,IAAqB,sBAAsB,CAAA,EAAG,KAAA;AACrF,EAAA,MAAM,SAAS,WAAA,CAAY,GAAA,CAAI,MAAA,CAAO,gBAAA,IAAoB,qBAAqB,CAAA,EAAG,KAAA;AAElF,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,UAAU,MAAA,CAAO,aAAA;AAAA,IACzB,OAAA,EAAS,WAAW,MAAA,CAAO;AAAA;AAAA,GAE7B;AACF;;;AClBA,eAAsB,eACpB,OAAA,EACoC;AACpC,EAAA,MAAM,QAAA,GAAW,OAAA,IAAW,MAAM,gBAAA,EAAiB;AACnD,EAAA,OAAO,SAAA,EAAU,CAAE,KAAA,CAAM,UAAA,CAAW,KAAK,QAAQ,CAAA;AACnD","file":"currencies.js","sourcesContent":["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_COUNTRY_COOKIE = 'spree_country';\nconst DEFAULT_LOCALE_COOKIE = 'spree_locale';\n\n/**\n * Read locale/currency/country from cookies (set by middleware).\n * Falls back to config defaults.\n * @internal\n */\nexport async function getLocaleOptions(): Promise<{\n locale?: string;\n currency?: string;\n country?: string;\n}> {\n const config = getConfig();\n const cookieStore = await cookies();\n\n const country = cookieStore.get(config.countryCookieName ?? DEFAULT_COUNTRY_COOKIE)?.value;\n const locale = cookieStore.get(config.localeCookieName ?? DEFAULT_LOCALE_COOKIE)?.value;\n\n return {\n locale: locale || config.defaultLocale,\n country: country || config.defaultCountry,\n // No currency — backend resolves from country via X-Spree-Country header\n };\n}\n","import type { StoreCurrency } from '@spree/sdk';\nimport { getClient } from '../config';\nimport { getLocaleOptions } from '../locale';\nimport type { SpreeNextOptions } from '../types';\n\n/**\n * List currencies supported by the store (derived from markets).\n * Locale/country are auto-read from cookies when not provided.\n */\nexport async function listCurrencies(\n options?: SpreeNextOptions\n): Promise<{ data: StoreCurrency[] }> {\n const resolved = options ?? await getLocaleOptions();\n return getClient().store.currencies.list(resolved);\n}\n"]}
|
package/dist/data/locales.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { SpreeNextOptions } from '../types.js';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* List locales supported by the store (derived from markets).
|
|
6
|
+
* Locale/country are auto-read from cookies when not provided.
|
|
6
7
|
*/
|
|
7
8
|
declare function listLocales(options?: SpreeNextOptions): Promise<{
|
|
8
9
|
data: StoreLocale[];
|
package/dist/data/locales.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createSpreeClient } from '@spree/sdk';
|
|
2
|
+
import { cookies } from 'next/headers';
|
|
2
3
|
|
|
3
4
|
// src/config.ts
|
|
4
5
|
var _client = null;
|
|
6
|
+
var _config = null;
|
|
5
7
|
function initSpreeNext(config) {
|
|
8
|
+
_config = config;
|
|
6
9
|
_client = createSpreeClient({
|
|
7
10
|
baseUrl: config.baseUrl,
|
|
8
11
|
publishableKey: config.publishableKey
|
|
@@ -22,13 +25,30 @@ function getClient() {
|
|
|
22
25
|
}
|
|
23
26
|
return _client;
|
|
24
27
|
}
|
|
28
|
+
function getConfig() {
|
|
29
|
+
if (!_config) {
|
|
30
|
+
getClient();
|
|
31
|
+
}
|
|
32
|
+
return _config;
|
|
33
|
+
}
|
|
34
|
+
var DEFAULT_COUNTRY_COOKIE = "spree_country";
|
|
35
|
+
var DEFAULT_LOCALE_COOKIE = "spree_locale";
|
|
36
|
+
async function getLocaleOptions() {
|
|
37
|
+
const config = getConfig();
|
|
38
|
+
const cookieStore = await cookies();
|
|
39
|
+
const country = cookieStore.get(config.countryCookieName ?? DEFAULT_COUNTRY_COOKIE)?.value;
|
|
40
|
+
const locale = cookieStore.get(config.localeCookieName ?? DEFAULT_LOCALE_COOKIE)?.value;
|
|
41
|
+
return {
|
|
42
|
+
locale: locale || config.defaultLocale,
|
|
43
|
+
country: country || config.defaultCountry
|
|
44
|
+
// No currency — backend resolves from country via X-Spree-Country header
|
|
45
|
+
};
|
|
46
|
+
}
|
|
25
47
|
|
|
26
48
|
// src/data/locales.ts
|
|
27
49
|
async function listLocales(options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
currency: options?.currency
|
|
31
|
-
});
|
|
50
|
+
const resolved = options ?? await getLocaleOptions();
|
|
51
|
+
return getClient().store.locales.list(resolved);
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
export { listLocales };
|