@commercengine/storefront-sdk-nextjs 0.1.0-alpha.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 +239 -0
- package/dist/index.cjs +337 -0
- package/dist/index.d.cts +116 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.js +305 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @commercengine/storefront-sdk-nextjs
|
|
2
|
+
|
|
3
|
+
**Zero-config Next.js wrapper** for the Commerce Engine Storefront SDK. Handles server/client complexity automatically with smart token management.
|
|
4
|
+
|
|
5
|
+
**✨ What makes this special:**
|
|
6
|
+
- **🔥 Zero token management** - Tokens created automatically on first API call
|
|
7
|
+
- **🧠 Smart environment detection** - One API works everywhere
|
|
8
|
+
- **🍪 Cookie-based storage** - Seamless server/client token sync
|
|
9
|
+
- **⚡ No setup required** - Just import and use
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @commercengine/storefront-sdk-nextjs
|
|
15
|
+
# or
|
|
16
|
+
pnpm add @commercengine/storefront-sdk-nextjs
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Initialize in your root layout
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// app/layout.tsx
|
|
25
|
+
import { StorefrontSDKInitializer, Environment } from '@commercengine/storefront-sdk-nextjs';
|
|
26
|
+
|
|
27
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
28
|
+
return (
|
|
29
|
+
<html>
|
|
30
|
+
<body>
|
|
31
|
+
<StorefrontSDKInitializer
|
|
32
|
+
config={{
|
|
33
|
+
storeId: process.env.NEXT_PUBLIC_STORE_ID!,
|
|
34
|
+
environment: Environment.Production,
|
|
35
|
+
apiKey: process.env.NEXT_PUBLIC_API_KEY,
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
38
|
+
{children}
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Use anywhere - tokens created automatically!
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// ✅ Client Component - Just works
|
|
49
|
+
'use client';
|
|
50
|
+
import { getStorefrontSDK } from '@commercengine/storefront-sdk-nextjs';
|
|
51
|
+
|
|
52
|
+
export default function AddToCart({ productId }: { productId: string }) {
|
|
53
|
+
const handleAddToCart = async () => {
|
|
54
|
+
const sdk = getStorefrontSDK(); // No cookies needed on client
|
|
55
|
+
|
|
56
|
+
// First API call automatically creates anonymous tokens! 🎉
|
|
57
|
+
const { data } = await sdk.cart.addToCart({
|
|
58
|
+
product_id: productId,
|
|
59
|
+
quantity: 1,
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return <button onClick={handleAddToCart}>Add to Cart</button>;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// ✅ Server Action - Cookies required
|
|
69
|
+
'use server';
|
|
70
|
+
import { cookies } from 'next/headers';
|
|
71
|
+
import { getStorefrontSDK } from '@commercengine/storefront-sdk-nextjs';
|
|
72
|
+
|
|
73
|
+
export async function addToCartAction(productId: string) {
|
|
74
|
+
const sdk = getStorefrontSDK(await cookies()); // Cookies needed on server
|
|
75
|
+
|
|
76
|
+
// Tokens automatically managed across server/client boundary! 🎉
|
|
77
|
+
const { data } = await sdk.cart.addToCart({
|
|
78
|
+
product_id: productId,
|
|
79
|
+
quantity: 1,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return { success: true };
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// ✅ Server Component - Cookies required
|
|
88
|
+
import { cookies } from 'next/headers';
|
|
89
|
+
import { getStorefrontSDK } from '@commercengine/storefront-sdk-nextjs';
|
|
90
|
+
|
|
91
|
+
export default async function ProductPage({ params }: { params: { id: string } }) {
|
|
92
|
+
const sdk = getStorefrontSDK(await cookies());
|
|
93
|
+
|
|
94
|
+
// Anonymous tokens work perfectly for public data! 🎉
|
|
95
|
+
const { data: product } = await sdk.catalog.getProduct({
|
|
96
|
+
product_id_or_slug: params.id
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return <div>{product?.name}</div>;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## API Reference
|
|
105
|
+
|
|
106
|
+
### `getStorefrontSDK()`
|
|
107
|
+
|
|
108
|
+
**Smart function** that works everywhere:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// ✅ Client-side (browser)
|
|
112
|
+
const sdk = getStorefrontSDK();
|
|
113
|
+
|
|
114
|
+
// ✅ Server-side (requires cookies for token persistence)
|
|
115
|
+
const sdk = getStorefrontSDK(await cookies());
|
|
116
|
+
|
|
117
|
+
// ❌ Server-side without cookies (helpful error message)
|
|
118
|
+
const sdk = getStorefrontSDK(); // Throws clear error with instructions
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `StorefrontSDKInitializer`
|
|
122
|
+
|
|
123
|
+
**Lightweight initializer** (recommended for most apps):
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
<StorefrontSDKInitializer
|
|
127
|
+
config={{
|
|
128
|
+
storeId: "your-store-id",
|
|
129
|
+
environment: Environment.Production, // or Environment.Staging
|
|
130
|
+
apiKey: "your-api-key",
|
|
131
|
+
|
|
132
|
+
// Optional: Advanced configuration
|
|
133
|
+
tokenStorageOptions: {
|
|
134
|
+
prefix: "ce_", // Cookie prefix (default)
|
|
135
|
+
maxAge: 30 * 24 * 60 * 60, // 30 days
|
|
136
|
+
secure: true, // Auto-detected from environment
|
|
137
|
+
sameSite: "Lax",
|
|
138
|
+
},
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Automatic Token Management 🤖
|
|
144
|
+
|
|
145
|
+
The SDK now handles tokens **completely automatically**:
|
|
146
|
+
|
|
147
|
+
### ✅ What Happens Automatically:
|
|
148
|
+
|
|
149
|
+
1. **First API Call**: Creates anonymous tokens seamlessly
|
|
150
|
+
2. **Token Expiry**: Refreshes tokens transparently
|
|
151
|
+
3. **Token Corruption**: Cleans up orphaned tokens
|
|
152
|
+
4. **User Login**: Upgrades from anonymous to authenticated tokens
|
|
153
|
+
5. **User Logout**: Downgrades gracefully with continuity
|
|
154
|
+
6. **Page Refresh**: Tokens persist via cookies across server/client
|
|
155
|
+
|
|
156
|
+
### ✅ Zero Configuration Needed:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// This just works - no token setup required!
|
|
160
|
+
const sdk = getStorefrontSDK();
|
|
161
|
+
const { data } = await sdk.catalog.listProducts(); // ✨ Tokens created automatically
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Error Messages
|
|
165
|
+
|
|
166
|
+
### Server Environment Detection
|
|
167
|
+
```
|
|
168
|
+
🚨 Server Environment Detected!
|
|
169
|
+
|
|
170
|
+
You're calling getStorefrontSDK() on the server without cookies.
|
|
171
|
+
Please pass the Next.js cookie store:
|
|
172
|
+
|
|
173
|
+
✅ Correct usage:
|
|
174
|
+
import { cookies } from 'next/headers';
|
|
175
|
+
|
|
176
|
+
// Server Actions & Route Handlers
|
|
177
|
+
const sdk = getStorefrontSDK(await cookies());
|
|
178
|
+
|
|
179
|
+
// API Routes (Next.js 12)
|
|
180
|
+
const sdk = getStorefrontSDK(cookies());
|
|
181
|
+
|
|
182
|
+
❌ Your current usage:
|
|
183
|
+
const sdk = getStorefrontSDK(); // Missing cookies!
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Migration from Base SDK
|
|
187
|
+
|
|
188
|
+
**Before (Base SDK):**
|
|
189
|
+
```typescript
|
|
190
|
+
const sdk = new StorefrontSDK({ storeId: "...", tokenStorage: ... });
|
|
191
|
+
await sdk.auth.getAnonymousToken(); // Manual token creation
|
|
192
|
+
const { data } = await sdk.catalog.listProducts();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**After (NextJS SDK):**
|
|
196
|
+
```typescript
|
|
197
|
+
const sdk = getStorefrontSDK(); // Environment detected automatically
|
|
198
|
+
const { data } = await sdk.catalog.listProducts(); // Tokens created automatically!
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## TypeScript Support
|
|
202
|
+
|
|
203
|
+
Full TypeScript support with all types from the base SDK:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import type {
|
|
207
|
+
StorefrontSDK,
|
|
208
|
+
NextJSSDKConfig,
|
|
209
|
+
Environment
|
|
210
|
+
} from '@commercengine/storefront-sdk-nextjs';
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Best Practices
|
|
214
|
+
|
|
215
|
+
1. **✅ One-Line Setup**: `StorefrontSDKInitializer` in root layout is all you need
|
|
216
|
+
2. **✅ Server Cookies**: Always pass `await cookies()` on server-side
|
|
217
|
+
3. **✅ Error Handling**: Wrap API calls in try-catch
|
|
218
|
+
4. **✅ Environment Variables**: Use Next.js env vars for configuration
|
|
219
|
+
5. **⚡ Trust the Middleware**: No manual token management needed
|
|
220
|
+
|
|
221
|
+
## Why This Wrapper Exists
|
|
222
|
+
|
|
223
|
+
**The base SDK is powerful but complex for Next.js:**
|
|
224
|
+
- Manual token storage setup
|
|
225
|
+
- Server/client environment handling
|
|
226
|
+
- Cookie configuration for SSR
|
|
227
|
+
- Token lifecycle management
|
|
228
|
+
|
|
229
|
+
**This wrapper makes it effortless:**
|
|
230
|
+
- Zero configuration token management
|
|
231
|
+
- Automatic environment detection
|
|
232
|
+
- Cookie-based persistence works out of the box
|
|
233
|
+
- One API that works everywhere
|
|
234
|
+
|
|
235
|
+
**Result: Focus on your app, not SDK complexity!** 🚀
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
All rights reserved - Commerce Engine
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ClientTokenStorage: () => ClientTokenStorage,
|
|
34
|
+
Environment: () => import_storefront_sdk2.Environment,
|
|
35
|
+
ServerTokenStorage: () => ServerTokenStorage,
|
|
36
|
+
StorefrontSDKInitializer: () => StorefrontSDKInitializer,
|
|
37
|
+
getStorefrontSDK: () => getStorefrontSDK
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/sdk-manager.ts
|
|
42
|
+
var import_storefront_sdk = require("@commercengine/storefront-sdk");
|
|
43
|
+
|
|
44
|
+
// src/token-storage.ts
|
|
45
|
+
var ClientTokenStorage = class {
|
|
46
|
+
constructor(options = {}) {
|
|
47
|
+
const prefix = options.prefix || "ce_";
|
|
48
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
49
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
50
|
+
this.options = {
|
|
51
|
+
maxAge: options.maxAge || 30 * 24 * 60 * 60,
|
|
52
|
+
// 30 days default
|
|
53
|
+
path: options.path || "/",
|
|
54
|
+
domain: options.domain,
|
|
55
|
+
secure: options.secure ?? (typeof window !== "undefined" && window.location?.protocol === "https:"),
|
|
56
|
+
sameSite: options.sameSite || "Lax"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async getAccessToken() {
|
|
60
|
+
return this.getCookie(this.accessTokenKey);
|
|
61
|
+
}
|
|
62
|
+
async setAccessToken(token) {
|
|
63
|
+
this.setCookie(this.accessTokenKey, token);
|
|
64
|
+
}
|
|
65
|
+
async getRefreshToken() {
|
|
66
|
+
return this.getCookie(this.refreshTokenKey);
|
|
67
|
+
}
|
|
68
|
+
async setRefreshToken(token) {
|
|
69
|
+
this.setCookie(this.refreshTokenKey, token);
|
|
70
|
+
}
|
|
71
|
+
async clearTokens() {
|
|
72
|
+
this.deleteCookie(this.accessTokenKey);
|
|
73
|
+
this.deleteCookie(this.refreshTokenKey);
|
|
74
|
+
}
|
|
75
|
+
getCookie(name) {
|
|
76
|
+
if (typeof document === "undefined") return null;
|
|
77
|
+
const value = `; ${document.cookie}`;
|
|
78
|
+
const parts = value.split(`; ${name}=`);
|
|
79
|
+
if (parts.length === 2) {
|
|
80
|
+
const cookieValue = parts.pop()?.split(";").shift();
|
|
81
|
+
return cookieValue ? decodeURIComponent(cookieValue) : null;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
setCookie(name, value) {
|
|
86
|
+
if (typeof document === "undefined") return;
|
|
87
|
+
const encodedValue = encodeURIComponent(value);
|
|
88
|
+
let cookieString = `${name}=${encodedValue}`;
|
|
89
|
+
if (this.options.maxAge) {
|
|
90
|
+
cookieString += `; Max-Age=${this.options.maxAge}`;
|
|
91
|
+
}
|
|
92
|
+
if (this.options.path) {
|
|
93
|
+
cookieString += `; Path=${this.options.path}`;
|
|
94
|
+
}
|
|
95
|
+
if (this.options.domain) {
|
|
96
|
+
cookieString += `; Domain=${this.options.domain}`;
|
|
97
|
+
}
|
|
98
|
+
if (this.options.secure) {
|
|
99
|
+
cookieString += `; Secure`;
|
|
100
|
+
}
|
|
101
|
+
if (this.options.sameSite) {
|
|
102
|
+
cookieString += `; SameSite=${this.options.sameSite}`;
|
|
103
|
+
}
|
|
104
|
+
document.cookie = cookieString;
|
|
105
|
+
}
|
|
106
|
+
deleteCookie(name) {
|
|
107
|
+
if (typeof document === "undefined") return;
|
|
108
|
+
let cookieString = `${name}=; Max-Age=0`;
|
|
109
|
+
if (this.options.path) {
|
|
110
|
+
cookieString += `; Path=${this.options.path}`;
|
|
111
|
+
}
|
|
112
|
+
if (this.options.domain) {
|
|
113
|
+
cookieString += `; Domain=${this.options.domain}`;
|
|
114
|
+
}
|
|
115
|
+
document.cookie = cookieString;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var ServerTokenStorage = class {
|
|
119
|
+
constructor(cookieStore, options = {}) {
|
|
120
|
+
const prefix = options.prefix || "ce_";
|
|
121
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
122
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
123
|
+
this.cookieStore = cookieStore;
|
|
124
|
+
this.options = {
|
|
125
|
+
maxAge: options.maxAge || 30 * 24 * 60 * 60,
|
|
126
|
+
// 30 days default
|
|
127
|
+
path: options.path || "/",
|
|
128
|
+
domain: options.domain,
|
|
129
|
+
secure: options.secure ?? process.env.NODE_ENV === "production",
|
|
130
|
+
sameSite: options.sameSite || "Lax"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async getAccessToken() {
|
|
134
|
+
try {
|
|
135
|
+
return this.cookieStore.get(this.accessTokenKey)?.value || null;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.warn(`Could not get access token from server cookies:`, error);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async setAccessToken(token) {
|
|
142
|
+
try {
|
|
143
|
+
this.cookieStore.set(this.accessTokenKey, token, {
|
|
144
|
+
maxAge: this.options.maxAge,
|
|
145
|
+
path: this.options.path,
|
|
146
|
+
domain: this.options.domain,
|
|
147
|
+
secure: this.options.secure,
|
|
148
|
+
sameSite: this.options.sameSite?.toLowerCase(),
|
|
149
|
+
httpOnly: false
|
|
150
|
+
// Must be false for client-side access
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.warn(`Could not set access token on server:`, error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async getRefreshToken() {
|
|
157
|
+
try {
|
|
158
|
+
return this.cookieStore.get(this.refreshTokenKey)?.value || null;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.warn(`Could not get refresh token from server cookies:`, error);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async setRefreshToken(token) {
|
|
165
|
+
try {
|
|
166
|
+
this.cookieStore.set(this.refreshTokenKey, token, {
|
|
167
|
+
maxAge: this.options.maxAge,
|
|
168
|
+
path: this.options.path,
|
|
169
|
+
domain: this.options.domain,
|
|
170
|
+
secure: this.options.secure,
|
|
171
|
+
sameSite: this.options.sameSite?.toLowerCase(),
|
|
172
|
+
httpOnly: false
|
|
173
|
+
// Must be false for client-side access
|
|
174
|
+
});
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.warn(`Could not set refresh token on server:`, error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async clearTokens() {
|
|
180
|
+
try {
|
|
181
|
+
this.cookieStore.delete(this.accessTokenKey);
|
|
182
|
+
this.cookieStore.delete(this.refreshTokenKey);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.warn(`Could not clear tokens on server:`, error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/sdk-manager.ts
|
|
190
|
+
var NextJSSDKManager = class _NextJSSDKManager {
|
|
191
|
+
constructor() {
|
|
192
|
+
this.clientSDK = null;
|
|
193
|
+
this.config = null;
|
|
194
|
+
}
|
|
195
|
+
static getInstance() {
|
|
196
|
+
if (!_NextJSSDKManager.instance) {
|
|
197
|
+
_NextJSSDKManager.instance = new _NextJSSDKManager();
|
|
198
|
+
}
|
|
199
|
+
return _NextJSSDKManager.instance;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Initialize the SDK with configuration (should be called once)
|
|
203
|
+
*/
|
|
204
|
+
initialize(config) {
|
|
205
|
+
this.config = config;
|
|
206
|
+
if (typeof window !== "undefined" && !this.clientSDK) {
|
|
207
|
+
this.clientSDK = this.createClientSDK();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get SDK instance for client-side usage
|
|
212
|
+
*/
|
|
213
|
+
getClientSDK() {
|
|
214
|
+
if (!this.config) {
|
|
215
|
+
throw new Error("SDK not initialized. Call initialize() first.");
|
|
216
|
+
}
|
|
217
|
+
if (!this.clientSDK) {
|
|
218
|
+
this.clientSDK = this.createClientSDK();
|
|
219
|
+
}
|
|
220
|
+
return this.clientSDK;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get SDK instance for server-side usage
|
|
224
|
+
*/
|
|
225
|
+
getServerSDK(cookieStore) {
|
|
226
|
+
if (!this.config) {
|
|
227
|
+
throw new Error("SDK not initialized. Call initialize() first.");
|
|
228
|
+
}
|
|
229
|
+
return new import_storefront_sdk.StorefrontSDK({
|
|
230
|
+
...this.config,
|
|
231
|
+
tokenStorage: new ServerTokenStorage(
|
|
232
|
+
cookieStore,
|
|
233
|
+
this.config.tokenStorageOptions
|
|
234
|
+
)
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
createClientSDK() {
|
|
238
|
+
if (!this.config) {
|
|
239
|
+
throw new Error("SDK not initialized. Call initialize() first.");
|
|
240
|
+
}
|
|
241
|
+
return new import_storefront_sdk.StorefrontSDK({
|
|
242
|
+
...this.config,
|
|
243
|
+
tokenStorage: new ClientTokenStorage(this.config.tokenStorageOptions)
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Check if SDK is initialized
|
|
248
|
+
*/
|
|
249
|
+
isInitialized() {
|
|
250
|
+
return this.config !== null;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Reset the SDK (useful for testing)
|
|
254
|
+
*/
|
|
255
|
+
reset() {
|
|
256
|
+
this.clientSDK = null;
|
|
257
|
+
this.config = null;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function getStorefrontSDK(cookieStore) {
|
|
261
|
+
const manager = NextJSSDKManager.getInstance();
|
|
262
|
+
if (typeof window !== "undefined") {
|
|
263
|
+
if (cookieStore) {
|
|
264
|
+
console.warn(
|
|
265
|
+
"Cookie store passed in client environment - this will be ignored"
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return manager.getClientSDK();
|
|
269
|
+
}
|
|
270
|
+
if (!cookieStore) {
|
|
271
|
+
let autoDetectMessage = "";
|
|
272
|
+
try {
|
|
273
|
+
require.resolve("next/headers");
|
|
274
|
+
autoDetectMessage = `
|
|
275
|
+
|
|
276
|
+
\u{1F50D} Auto-detection attempted but failed. You may be in:
|
|
277
|
+
- Server Action (use: const sdk = getStorefrontSDK(await cookies()))
|
|
278
|
+
- API Route (use: const sdk = getStorefrontSDK(cookies()))
|
|
279
|
+
- Middleware (token access limited)
|
|
280
|
+
`;
|
|
281
|
+
} catch {
|
|
282
|
+
autoDetectMessage = `
|
|
283
|
+
|
|
284
|
+
\u{1F4A1} Make sure you have Next.js installed and are in a server context.
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
throw new Error(
|
|
288
|
+
`
|
|
289
|
+
\u{1F6A8} Server Environment Detected!
|
|
290
|
+
|
|
291
|
+
You're calling getStorefrontSDK() on the server without cookies.
|
|
292
|
+
Please pass the Next.js cookie store:
|
|
293
|
+
|
|
294
|
+
\u2705 Correct usage:
|
|
295
|
+
import { cookies } from 'next/headers';
|
|
296
|
+
|
|
297
|
+
// Server Actions & Route Handlers
|
|
298
|
+
const sdk = getStorefrontSDK(await cookies());
|
|
299
|
+
|
|
300
|
+
// API Routes (Next.js 12)
|
|
301
|
+
const sdk = getStorefrontSDK(cookies());
|
|
302
|
+
|
|
303
|
+
\u274C Your current usage:
|
|
304
|
+
const sdk = getStorefrontSDK(); // Missing cookies!
|
|
305
|
+
${autoDetectMessage}
|
|
306
|
+
This is required for server-side token access.
|
|
307
|
+
`.trim()
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
return manager.getServerSDK(cookieStore);
|
|
311
|
+
}
|
|
312
|
+
function initializeStorefrontSDK(config) {
|
|
313
|
+
const manager = NextJSSDKManager.getInstance();
|
|
314
|
+
manager.initialize(config);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/init-client.ts
|
|
318
|
+
var import_react = require("react");
|
|
319
|
+
function StorefrontSDKInitializer({
|
|
320
|
+
config
|
|
321
|
+
}) {
|
|
322
|
+
(0, import_react.useEffect)(() => {
|
|
323
|
+
initializeStorefrontSDK(config);
|
|
324
|
+
}, [config]);
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/index.ts
|
|
329
|
+
var import_storefront_sdk2 = require("@commercengine/storefront-sdk");
|
|
330
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
331
|
+
0 && (module.exports = {
|
|
332
|
+
ClientTokenStorage,
|
|
333
|
+
Environment,
|
|
334
|
+
ServerTokenStorage,
|
|
335
|
+
StorefrontSDKInitializer,
|
|
336
|
+
getStorefrontSDK
|
|
337
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { TokenStorage, StorefrontSDK, StorefrontSDKOptions } from '@commercengine/storefront-sdk';
|
|
2
|
+
export { Environment, StorefrontSDK, StorefrontSDKOptions } from '@commercengine/storefront-sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for NextJSTokenStorage
|
|
6
|
+
*/
|
|
7
|
+
interface NextJSTokenStorageOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Prefix for cookie names (default: "ce_")
|
|
10
|
+
*/
|
|
11
|
+
prefix?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Maximum age of cookies in seconds (default: 30 days)
|
|
14
|
+
*/
|
|
15
|
+
maxAge?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Cookie path (default: "/")
|
|
18
|
+
*/
|
|
19
|
+
path?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Cookie domain (default: current domain)
|
|
22
|
+
*/
|
|
23
|
+
domain?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether cookies should be secure (default: auto-detect based on environment)
|
|
26
|
+
*/
|
|
27
|
+
secure?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* SameSite cookie attribute (default: "Lax")
|
|
30
|
+
*/
|
|
31
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Client-side token storage that uses document.cookie
|
|
35
|
+
*/
|
|
36
|
+
declare class ClientTokenStorage implements TokenStorage {
|
|
37
|
+
private accessTokenKey;
|
|
38
|
+
private refreshTokenKey;
|
|
39
|
+
private options;
|
|
40
|
+
constructor(options?: NextJSTokenStorageOptions);
|
|
41
|
+
getAccessToken(): Promise<string | null>;
|
|
42
|
+
setAccessToken(token: string): Promise<void>;
|
|
43
|
+
getRefreshToken(): Promise<string | null>;
|
|
44
|
+
setRefreshToken(token: string): Promise<void>;
|
|
45
|
+
clearTokens(): Promise<void>;
|
|
46
|
+
private getCookie;
|
|
47
|
+
private setCookie;
|
|
48
|
+
private deleteCookie;
|
|
49
|
+
}
|
|
50
|
+
type NextCookieStore$1 = {
|
|
51
|
+
get: (name: string) => {
|
|
52
|
+
value: string;
|
|
53
|
+
} | undefined;
|
|
54
|
+
set: (name: string, value: string, options?: any) => void;
|
|
55
|
+
delete: (name: string) => void;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Server-side token storage that uses Next.js cookies API
|
|
59
|
+
*/
|
|
60
|
+
declare class ServerTokenStorage implements TokenStorage {
|
|
61
|
+
private accessTokenKey;
|
|
62
|
+
private refreshTokenKey;
|
|
63
|
+
private options;
|
|
64
|
+
private cookieStore;
|
|
65
|
+
constructor(cookieStore: NextCookieStore$1, options?: NextJSTokenStorageOptions);
|
|
66
|
+
getAccessToken(): Promise<string | null>;
|
|
67
|
+
setAccessToken(token: string): Promise<void>;
|
|
68
|
+
getRefreshToken(): Promise<string | null>;
|
|
69
|
+
setRefreshToken(token: string): Promise<void>;
|
|
70
|
+
clearTokens(): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Configuration for the NextJS SDK wrapper
|
|
75
|
+
*/
|
|
76
|
+
interface NextJSSDKConfig extends Omit<StorefrontSDKOptions, "tokenStorage"> {
|
|
77
|
+
/**
|
|
78
|
+
* Token storage configuration options
|
|
79
|
+
*/
|
|
80
|
+
tokenStorageOptions?: NextJSTokenStorageOptions;
|
|
81
|
+
}
|
|
82
|
+
type NextCookieStore = {
|
|
83
|
+
get: (name: string) => {
|
|
84
|
+
value: string;
|
|
85
|
+
} | undefined;
|
|
86
|
+
set: (name: string, value: string, options?: any) => void;
|
|
87
|
+
delete: (name: string) => void;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Smart SDK getter that automatically detects environment
|
|
91
|
+
*
|
|
92
|
+
* Usage:
|
|
93
|
+
* - Client-side: getStorefrontSDK()
|
|
94
|
+
* - Server-side: getStorefrontSDK(await cookies())
|
|
95
|
+
*/
|
|
96
|
+
declare function getStorefrontSDK(): StorefrontSDK;
|
|
97
|
+
declare function getStorefrontSDK(cookieStore: NextCookieStore): StorefrontSDK;
|
|
98
|
+
|
|
99
|
+
interface StorefrontSDKInitializerProps {
|
|
100
|
+
config: NextJSSDKConfig;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Client-side initialization component
|
|
104
|
+
* Use this in your root layout to initialize the SDK once
|
|
105
|
+
*
|
|
106
|
+
* The core SDK middleware now automatically handles everything:
|
|
107
|
+
* - Creates anonymous tokens automatically on first API request (if no tokens exist)
|
|
108
|
+
* - Token state assessment and cleanup on first request per page load
|
|
109
|
+
* - Expired token refresh with automatic anonymous fallback
|
|
110
|
+
* - Graceful handling of partial token states
|
|
111
|
+
*
|
|
112
|
+
* No manual token creation needed - just make API calls and everything works!
|
|
113
|
+
*/
|
|
114
|
+
declare function StorefrontSDKInitializer({ config, }: StorefrontSDKInitializerProps): null;
|
|
115
|
+
|
|
116
|
+
export { ClientTokenStorage, type NextJSSDKConfig, type NextJSTokenStorageOptions, ServerTokenStorage, StorefrontSDKInitializer, getStorefrontSDK };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { TokenStorage, StorefrontSDK, StorefrontSDKOptions } from '@commercengine/storefront-sdk';
|
|
2
|
+
export { Environment, StorefrontSDK, StorefrontSDKOptions } from '@commercengine/storefront-sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for NextJSTokenStorage
|
|
6
|
+
*/
|
|
7
|
+
interface NextJSTokenStorageOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Prefix for cookie names (default: "ce_")
|
|
10
|
+
*/
|
|
11
|
+
prefix?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Maximum age of cookies in seconds (default: 30 days)
|
|
14
|
+
*/
|
|
15
|
+
maxAge?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Cookie path (default: "/")
|
|
18
|
+
*/
|
|
19
|
+
path?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Cookie domain (default: current domain)
|
|
22
|
+
*/
|
|
23
|
+
domain?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether cookies should be secure (default: auto-detect based on environment)
|
|
26
|
+
*/
|
|
27
|
+
secure?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* SameSite cookie attribute (default: "Lax")
|
|
30
|
+
*/
|
|
31
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Client-side token storage that uses document.cookie
|
|
35
|
+
*/
|
|
36
|
+
declare class ClientTokenStorage implements TokenStorage {
|
|
37
|
+
private accessTokenKey;
|
|
38
|
+
private refreshTokenKey;
|
|
39
|
+
private options;
|
|
40
|
+
constructor(options?: NextJSTokenStorageOptions);
|
|
41
|
+
getAccessToken(): Promise<string | null>;
|
|
42
|
+
setAccessToken(token: string): Promise<void>;
|
|
43
|
+
getRefreshToken(): Promise<string | null>;
|
|
44
|
+
setRefreshToken(token: string): Promise<void>;
|
|
45
|
+
clearTokens(): Promise<void>;
|
|
46
|
+
private getCookie;
|
|
47
|
+
private setCookie;
|
|
48
|
+
private deleteCookie;
|
|
49
|
+
}
|
|
50
|
+
type NextCookieStore$1 = {
|
|
51
|
+
get: (name: string) => {
|
|
52
|
+
value: string;
|
|
53
|
+
} | undefined;
|
|
54
|
+
set: (name: string, value: string, options?: any) => void;
|
|
55
|
+
delete: (name: string) => void;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Server-side token storage that uses Next.js cookies API
|
|
59
|
+
*/
|
|
60
|
+
declare class ServerTokenStorage implements TokenStorage {
|
|
61
|
+
private accessTokenKey;
|
|
62
|
+
private refreshTokenKey;
|
|
63
|
+
private options;
|
|
64
|
+
private cookieStore;
|
|
65
|
+
constructor(cookieStore: NextCookieStore$1, options?: NextJSTokenStorageOptions);
|
|
66
|
+
getAccessToken(): Promise<string | null>;
|
|
67
|
+
setAccessToken(token: string): Promise<void>;
|
|
68
|
+
getRefreshToken(): Promise<string | null>;
|
|
69
|
+
setRefreshToken(token: string): Promise<void>;
|
|
70
|
+
clearTokens(): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Configuration for the NextJS SDK wrapper
|
|
75
|
+
*/
|
|
76
|
+
interface NextJSSDKConfig extends Omit<StorefrontSDKOptions, "tokenStorage"> {
|
|
77
|
+
/**
|
|
78
|
+
* Token storage configuration options
|
|
79
|
+
*/
|
|
80
|
+
tokenStorageOptions?: NextJSTokenStorageOptions;
|
|
81
|
+
}
|
|
82
|
+
type NextCookieStore = {
|
|
83
|
+
get: (name: string) => {
|
|
84
|
+
value: string;
|
|
85
|
+
} | undefined;
|
|
86
|
+
set: (name: string, value: string, options?: any) => void;
|
|
87
|
+
delete: (name: string) => void;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Smart SDK getter that automatically detects environment
|
|
91
|
+
*
|
|
92
|
+
* Usage:
|
|
93
|
+
* - Client-side: getStorefrontSDK()
|
|
94
|
+
* - Server-side: getStorefrontSDK(await cookies())
|
|
95
|
+
*/
|
|
96
|
+
declare function getStorefrontSDK(): StorefrontSDK;
|
|
97
|
+
declare function getStorefrontSDK(cookieStore: NextCookieStore): StorefrontSDK;
|
|
98
|
+
|
|
99
|
+
interface StorefrontSDKInitializerProps {
|
|
100
|
+
config: NextJSSDKConfig;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Client-side initialization component
|
|
104
|
+
* Use this in your root layout to initialize the SDK once
|
|
105
|
+
*
|
|
106
|
+
* The core SDK middleware now automatically handles everything:
|
|
107
|
+
* - Creates anonymous tokens automatically on first API request (if no tokens exist)
|
|
108
|
+
* - Token state assessment and cleanup on first request per page load
|
|
109
|
+
* - Expired token refresh with automatic anonymous fallback
|
|
110
|
+
* - Graceful handling of partial token states
|
|
111
|
+
*
|
|
112
|
+
* No manual token creation needed - just make API calls and everything works!
|
|
113
|
+
*/
|
|
114
|
+
declare function StorefrontSDKInitializer({ config, }: StorefrontSDKInitializerProps): null;
|
|
115
|
+
|
|
116
|
+
export { ClientTokenStorage, type NextJSSDKConfig, type NextJSTokenStorageOptions, ServerTokenStorage, StorefrontSDKInitializer, getStorefrontSDK };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/sdk-manager.ts
|
|
9
|
+
import {
|
|
10
|
+
StorefrontSDK
|
|
11
|
+
} from "@commercengine/storefront-sdk";
|
|
12
|
+
|
|
13
|
+
// src/token-storage.ts
|
|
14
|
+
var ClientTokenStorage = class {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
const prefix = options.prefix || "ce_";
|
|
17
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
18
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
19
|
+
this.options = {
|
|
20
|
+
maxAge: options.maxAge || 30 * 24 * 60 * 60,
|
|
21
|
+
// 30 days default
|
|
22
|
+
path: options.path || "/",
|
|
23
|
+
domain: options.domain,
|
|
24
|
+
secure: options.secure ?? (typeof window !== "undefined" && window.location?.protocol === "https:"),
|
|
25
|
+
sameSite: options.sameSite || "Lax"
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async getAccessToken() {
|
|
29
|
+
return this.getCookie(this.accessTokenKey);
|
|
30
|
+
}
|
|
31
|
+
async setAccessToken(token) {
|
|
32
|
+
this.setCookie(this.accessTokenKey, token);
|
|
33
|
+
}
|
|
34
|
+
async getRefreshToken() {
|
|
35
|
+
return this.getCookie(this.refreshTokenKey);
|
|
36
|
+
}
|
|
37
|
+
async setRefreshToken(token) {
|
|
38
|
+
this.setCookie(this.refreshTokenKey, token);
|
|
39
|
+
}
|
|
40
|
+
async clearTokens() {
|
|
41
|
+
this.deleteCookie(this.accessTokenKey);
|
|
42
|
+
this.deleteCookie(this.refreshTokenKey);
|
|
43
|
+
}
|
|
44
|
+
getCookie(name) {
|
|
45
|
+
if (typeof document === "undefined") return null;
|
|
46
|
+
const value = `; ${document.cookie}`;
|
|
47
|
+
const parts = value.split(`; ${name}=`);
|
|
48
|
+
if (parts.length === 2) {
|
|
49
|
+
const cookieValue = parts.pop()?.split(";").shift();
|
|
50
|
+
return cookieValue ? decodeURIComponent(cookieValue) : null;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
setCookie(name, value) {
|
|
55
|
+
if (typeof document === "undefined") return;
|
|
56
|
+
const encodedValue = encodeURIComponent(value);
|
|
57
|
+
let cookieString = `${name}=${encodedValue}`;
|
|
58
|
+
if (this.options.maxAge) {
|
|
59
|
+
cookieString += `; Max-Age=${this.options.maxAge}`;
|
|
60
|
+
}
|
|
61
|
+
if (this.options.path) {
|
|
62
|
+
cookieString += `; Path=${this.options.path}`;
|
|
63
|
+
}
|
|
64
|
+
if (this.options.domain) {
|
|
65
|
+
cookieString += `; Domain=${this.options.domain}`;
|
|
66
|
+
}
|
|
67
|
+
if (this.options.secure) {
|
|
68
|
+
cookieString += `; Secure`;
|
|
69
|
+
}
|
|
70
|
+
if (this.options.sameSite) {
|
|
71
|
+
cookieString += `; SameSite=${this.options.sameSite}`;
|
|
72
|
+
}
|
|
73
|
+
document.cookie = cookieString;
|
|
74
|
+
}
|
|
75
|
+
deleteCookie(name) {
|
|
76
|
+
if (typeof document === "undefined") return;
|
|
77
|
+
let cookieString = `${name}=; Max-Age=0`;
|
|
78
|
+
if (this.options.path) {
|
|
79
|
+
cookieString += `; Path=${this.options.path}`;
|
|
80
|
+
}
|
|
81
|
+
if (this.options.domain) {
|
|
82
|
+
cookieString += `; Domain=${this.options.domain}`;
|
|
83
|
+
}
|
|
84
|
+
document.cookie = cookieString;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var ServerTokenStorage = class {
|
|
88
|
+
constructor(cookieStore, options = {}) {
|
|
89
|
+
const prefix = options.prefix || "ce_";
|
|
90
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
91
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
92
|
+
this.cookieStore = cookieStore;
|
|
93
|
+
this.options = {
|
|
94
|
+
maxAge: options.maxAge || 30 * 24 * 60 * 60,
|
|
95
|
+
// 30 days default
|
|
96
|
+
path: options.path || "/",
|
|
97
|
+
domain: options.domain,
|
|
98
|
+
secure: options.secure ?? process.env.NODE_ENV === "production",
|
|
99
|
+
sameSite: options.sameSite || "Lax"
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async getAccessToken() {
|
|
103
|
+
try {
|
|
104
|
+
return this.cookieStore.get(this.accessTokenKey)?.value || null;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.warn(`Could not get access token from server cookies:`, error);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async setAccessToken(token) {
|
|
111
|
+
try {
|
|
112
|
+
this.cookieStore.set(this.accessTokenKey, token, {
|
|
113
|
+
maxAge: this.options.maxAge,
|
|
114
|
+
path: this.options.path,
|
|
115
|
+
domain: this.options.domain,
|
|
116
|
+
secure: this.options.secure,
|
|
117
|
+
sameSite: this.options.sameSite?.toLowerCase(),
|
|
118
|
+
httpOnly: false
|
|
119
|
+
// Must be false for client-side access
|
|
120
|
+
});
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.warn(`Could not set access token on server:`, error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async getRefreshToken() {
|
|
126
|
+
try {
|
|
127
|
+
return this.cookieStore.get(this.refreshTokenKey)?.value || null;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.warn(`Could not get refresh token from server cookies:`, error);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async setRefreshToken(token) {
|
|
134
|
+
try {
|
|
135
|
+
this.cookieStore.set(this.refreshTokenKey, token, {
|
|
136
|
+
maxAge: this.options.maxAge,
|
|
137
|
+
path: this.options.path,
|
|
138
|
+
domain: this.options.domain,
|
|
139
|
+
secure: this.options.secure,
|
|
140
|
+
sameSite: this.options.sameSite?.toLowerCase(),
|
|
141
|
+
httpOnly: false
|
|
142
|
+
// Must be false for client-side access
|
|
143
|
+
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.warn(`Could not set refresh token on server:`, error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async clearTokens() {
|
|
149
|
+
try {
|
|
150
|
+
this.cookieStore.delete(this.accessTokenKey);
|
|
151
|
+
this.cookieStore.delete(this.refreshTokenKey);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.warn(`Could not clear tokens on server:`, error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/sdk-manager.ts
|
|
159
|
+
var NextJSSDKManager = class _NextJSSDKManager {
|
|
160
|
+
constructor() {
|
|
161
|
+
this.clientSDK = null;
|
|
162
|
+
this.config = null;
|
|
163
|
+
}
|
|
164
|
+
static getInstance() {
|
|
165
|
+
if (!_NextJSSDKManager.instance) {
|
|
166
|
+
_NextJSSDKManager.instance = new _NextJSSDKManager();
|
|
167
|
+
}
|
|
168
|
+
return _NextJSSDKManager.instance;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Initialize the SDK with configuration (should be called once)
|
|
172
|
+
*/
|
|
173
|
+
initialize(config) {
|
|
174
|
+
this.config = config;
|
|
175
|
+
if (typeof window !== "undefined" && !this.clientSDK) {
|
|
176
|
+
this.clientSDK = this.createClientSDK();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get SDK instance for client-side usage
|
|
181
|
+
*/
|
|
182
|
+
getClientSDK() {
|
|
183
|
+
if (!this.config) {
|
|
184
|
+
throw new Error("SDK not initialized. Call initialize() first.");
|
|
185
|
+
}
|
|
186
|
+
if (!this.clientSDK) {
|
|
187
|
+
this.clientSDK = this.createClientSDK();
|
|
188
|
+
}
|
|
189
|
+
return this.clientSDK;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get SDK instance for server-side usage
|
|
193
|
+
*/
|
|
194
|
+
getServerSDK(cookieStore) {
|
|
195
|
+
if (!this.config) {
|
|
196
|
+
throw new Error("SDK not initialized. Call initialize() first.");
|
|
197
|
+
}
|
|
198
|
+
return new StorefrontSDK({
|
|
199
|
+
...this.config,
|
|
200
|
+
tokenStorage: new ServerTokenStorage(
|
|
201
|
+
cookieStore,
|
|
202
|
+
this.config.tokenStorageOptions
|
|
203
|
+
)
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
createClientSDK() {
|
|
207
|
+
if (!this.config) {
|
|
208
|
+
throw new Error("SDK not initialized. Call initialize() first.");
|
|
209
|
+
}
|
|
210
|
+
return new StorefrontSDK({
|
|
211
|
+
...this.config,
|
|
212
|
+
tokenStorage: new ClientTokenStorage(this.config.tokenStorageOptions)
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Check if SDK is initialized
|
|
217
|
+
*/
|
|
218
|
+
isInitialized() {
|
|
219
|
+
return this.config !== null;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Reset the SDK (useful for testing)
|
|
223
|
+
*/
|
|
224
|
+
reset() {
|
|
225
|
+
this.clientSDK = null;
|
|
226
|
+
this.config = null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function getStorefrontSDK(cookieStore) {
|
|
230
|
+
const manager = NextJSSDKManager.getInstance();
|
|
231
|
+
if (typeof window !== "undefined") {
|
|
232
|
+
if (cookieStore) {
|
|
233
|
+
console.warn(
|
|
234
|
+
"Cookie store passed in client environment - this will be ignored"
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return manager.getClientSDK();
|
|
238
|
+
}
|
|
239
|
+
if (!cookieStore) {
|
|
240
|
+
let autoDetectMessage = "";
|
|
241
|
+
try {
|
|
242
|
+
__require.resolve("next/headers");
|
|
243
|
+
autoDetectMessage = `
|
|
244
|
+
|
|
245
|
+
\u{1F50D} Auto-detection attempted but failed. You may be in:
|
|
246
|
+
- Server Action (use: const sdk = getStorefrontSDK(await cookies()))
|
|
247
|
+
- API Route (use: const sdk = getStorefrontSDK(cookies()))
|
|
248
|
+
- Middleware (token access limited)
|
|
249
|
+
`;
|
|
250
|
+
} catch {
|
|
251
|
+
autoDetectMessage = `
|
|
252
|
+
|
|
253
|
+
\u{1F4A1} Make sure you have Next.js installed and are in a server context.
|
|
254
|
+
`;
|
|
255
|
+
}
|
|
256
|
+
throw new Error(
|
|
257
|
+
`
|
|
258
|
+
\u{1F6A8} Server Environment Detected!
|
|
259
|
+
|
|
260
|
+
You're calling getStorefrontSDK() on the server without cookies.
|
|
261
|
+
Please pass the Next.js cookie store:
|
|
262
|
+
|
|
263
|
+
\u2705 Correct usage:
|
|
264
|
+
import { cookies } from 'next/headers';
|
|
265
|
+
|
|
266
|
+
// Server Actions & Route Handlers
|
|
267
|
+
const sdk = getStorefrontSDK(await cookies());
|
|
268
|
+
|
|
269
|
+
// API Routes (Next.js 12)
|
|
270
|
+
const sdk = getStorefrontSDK(cookies());
|
|
271
|
+
|
|
272
|
+
\u274C Your current usage:
|
|
273
|
+
const sdk = getStorefrontSDK(); // Missing cookies!
|
|
274
|
+
${autoDetectMessage}
|
|
275
|
+
This is required for server-side token access.
|
|
276
|
+
`.trim()
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
return manager.getServerSDK(cookieStore);
|
|
280
|
+
}
|
|
281
|
+
function initializeStorefrontSDK(config) {
|
|
282
|
+
const manager = NextJSSDKManager.getInstance();
|
|
283
|
+
manager.initialize(config);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/init-client.ts
|
|
287
|
+
import { useEffect } from "react";
|
|
288
|
+
function StorefrontSDKInitializer({
|
|
289
|
+
config
|
|
290
|
+
}) {
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
initializeStorefrontSDK(config);
|
|
293
|
+
}, [config]);
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/index.ts
|
|
298
|
+
import { Environment } from "@commercengine/storefront-sdk";
|
|
299
|
+
export {
|
|
300
|
+
ClientTokenStorage,
|
|
301
|
+
Environment,
|
|
302
|
+
ServerTokenStorage,
|
|
303
|
+
StorefrontSDKInitializer,
|
|
304
|
+
getStorefrontSDK
|
|
305
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@commercengine/storefront-sdk-nextjs",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Next.js wrapper for the Commerce Engine Storefront SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"commerce engine",
|
|
21
|
+
"storefront",
|
|
22
|
+
"api",
|
|
23
|
+
"sdk",
|
|
24
|
+
"nextjs",
|
|
25
|
+
"react"
|
|
26
|
+
],
|
|
27
|
+
"author": "Commerce Engine",
|
|
28
|
+
"license": "All rights reserved",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"next": ">=13.0.0",
|
|
31
|
+
"react": ">=18.0.0",
|
|
32
|
+
"react-dom": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@commercengine/storefront-sdk": "0.8.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^24.3.0",
|
|
39
|
+
"@types/react": "^18.0.0",
|
|
40
|
+
"next": ">=13.0.0",
|
|
41
|
+
"react": ">=18.0.0",
|
|
42
|
+
"react-dom": ">=18.0.0",
|
|
43
|
+
"rimraf": "^6.0.1",
|
|
44
|
+
"tsup": "^8.5.0",
|
|
45
|
+
"typescript": "^5.9.2"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"clean": "rimraf dist"
|
|
53
|
+
}
|
|
54
|
+
}
|