@commercengine/storefront-sdk-nextjs 0.1.0-alpha.0 → 0.1.0-alpha.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/dist/index.js CHANGED
@@ -6,8 +6,11 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/sdk-manager.ts
9
+ import { cache } from "react";
9
10
  import {
10
- StorefrontSDK
11
+ StorefrontSDK,
12
+ MemoryTokenStorage,
13
+ Environment
11
14
  } from "@commercengine/storefront-sdk";
12
15
 
13
16
  // src/token-storage.ts
@@ -116,7 +119,7 @@ var ServerTokenStorage = class {
116
119
  secure: this.options.secure,
117
120
  sameSite: this.options.sameSite?.toLowerCase(),
118
121
  httpOnly: false
119
- // Must be false for client-side access
122
+ // Allow client-side access for SDK flexibility
120
123
  });
121
124
  } catch (error) {
122
125
  console.warn(`Could not set access token on server:`, error);
@@ -139,7 +142,7 @@ var ServerTokenStorage = class {
139
142
  secure: this.options.secure,
140
143
  sameSite: this.options.sameSite?.toLowerCase(),
141
144
  httpOnly: false
142
- // Must be false for client-side access
145
+ // Allow client-side access for SDK flexibility
143
146
  });
144
147
  } catch (error) {
145
148
  console.warn(`Could not set refresh token on server:`, error);
@@ -155,88 +158,171 @@ var ServerTokenStorage = class {
155
158
  }
156
159
  };
157
160
 
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
- }
161
+ // src/build-token-cache.ts
162
+ var store = /* @__PURE__ */ new Map();
163
+ function isExpired(token) {
164
+ if (!token) return true;
165
+ if (!token.expiresAt) return false;
166
+ return Date.now() > token.expiresAt - 3e4;
167
+ }
168
+ function getCachedToken(key) {
169
+ const token = store.get(key);
170
+ return isExpired(token) ? null : token;
171
+ }
172
+ function setCachedToken(key, token) {
173
+ const expiresAt = token.ttlSeconds != null ? Date.now() + token.ttlSeconds * 1e3 : void 0;
174
+ store.set(key, {
175
+ accessToken: token.accessToken,
176
+ refreshToken: token.refreshToken ?? null,
177
+ expiresAt
178
+ });
179
+ }
180
+ function clearCachedToken(key) {
181
+ store.delete(key);
182
+ }
183
+
184
+ // src/build-caching-memory-storage.ts
185
+ var DEFAULT_TTL_SECONDS = 5 * 60;
186
+ var BuildCachingMemoryTokenStorage = class {
187
+ constructor(cacheKey, ttlSeconds = DEFAULT_TTL_SECONDS) {
188
+ this.cacheKey = cacheKey;
189
+ this.ttlSeconds = ttlSeconds;
190
+ this.access = null;
191
+ this.refresh = null;
178
192
  }
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.");
193
+ async getAccessToken() {
194
+ if (this.access) {
195
+ console.log(`\u{1F535} [BuildCache] Using instance token for key: ${this.cacheKey}`);
196
+ return this.access;
185
197
  }
186
- if (!this.clientSDK) {
187
- this.clientSDK = this.createClientSDK();
198
+ const cached = getCachedToken(this.cacheKey);
199
+ if (cached?.accessToken) {
200
+ console.log(`\u{1F7E2} [BuildCache] Using cached token for key: ${this.cacheKey}`);
201
+ this.access = cached.accessToken;
202
+ this.refresh = cached.refreshToken ?? null;
203
+ return this.access;
188
204
  }
189
- return this.clientSDK;
205
+ console.log(`\u{1F7E1} [BuildCache] No cached token found for key: ${this.cacheKey}`);
206
+ return null;
190
207
  }
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
- )
208
+ async setAccessToken(token) {
209
+ console.log(`\u{1F7E0} [BuildCache] Caching new access token for key: ${this.cacheKey}`);
210
+ this.access = token;
211
+ setCachedToken(this.cacheKey, {
212
+ accessToken: token,
213
+ refreshToken: this.refresh,
214
+ ttlSeconds: this.ttlSeconds
204
215
  });
205
216
  }
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
- });
217
+ async getRefreshToken() {
218
+ return this.refresh;
214
219
  }
215
- /**
216
- * Check if SDK is initialized
217
- */
218
- isInitialized() {
219
- return this.config !== null;
220
+ async setRefreshToken(token) {
221
+ this.refresh = token;
222
+ setCachedToken(this.cacheKey, {
223
+ accessToken: this.access ?? "",
224
+ refreshToken: token,
225
+ ttlSeconds: this.ttlSeconds
226
+ });
220
227
  }
221
- /**
222
- * Reset the SDK (useful for testing)
223
- */
224
- reset() {
225
- this.clientSDK = null;
226
- this.config = null;
228
+ async clearTokens() {
229
+ this.access = null;
230
+ this.refresh = null;
231
+ clearCachedToken(this.cacheKey);
227
232
  }
228
233
  };
234
+
235
+ // src/sdk-manager.ts
236
+ var globalConfig = null;
237
+ function getEnvConfig() {
238
+ return {
239
+ storeId: process.env.NEXT_PUBLIC_STORE_ID || "",
240
+ environment: process.env.NEXT_PUBLIC_ENVIRONMENT === "production" ? Environment.Production : Environment.Staging,
241
+ apiKey: process.env.NEXT_PUBLIC_API_KEY
242
+ };
243
+ }
244
+ function getConfig() {
245
+ if (globalConfig) {
246
+ return globalConfig;
247
+ }
248
+ return getEnvConfig();
249
+ }
250
+ var clientSDK = null;
251
+ function hasRequestContext() {
252
+ try {
253
+ const { cookies } = __require("next/headers");
254
+ cookies();
255
+ return true;
256
+ } catch {
257
+ return false;
258
+ }
259
+ }
260
+ function createTokenStorage(cookieStore, options, config) {
261
+ if (typeof window !== "undefined") {
262
+ return new ClientTokenStorage(options);
263
+ }
264
+ if (cookieStore) {
265
+ return new ServerTokenStorage(cookieStore, options);
266
+ }
267
+ const shouldCache = process.env.NEXT_BUILD_CACHE_TOKENS === "true";
268
+ if (shouldCache && config) {
269
+ const cacheKey = `${config.storeId}:${config.environment || "production"}`;
270
+ console.log(`\u{1F680} [BuildCache] Using BuildCachingMemoryTokenStorage with key: ${cacheKey}`);
271
+ return new BuildCachingMemoryTokenStorage(cacheKey);
272
+ }
273
+ console.log(`\u{1F504} [Build] Using standard MemoryTokenStorage (caching disabled)`);
274
+ return new MemoryTokenStorage();
275
+ }
276
+ var getServerSDKCached = cache((cookieStore) => {
277
+ const config = getEnvConfig();
278
+ return new StorefrontSDK({
279
+ ...config,
280
+ tokenStorage: createTokenStorage(
281
+ cookieStore,
282
+ config.tokenStorageOptions,
283
+ config
284
+ )
285
+ });
286
+ });
287
+ var buildTimeSDK = null;
288
+ function getBuildTimeSDK() {
289
+ const config = getEnvConfig();
290
+ if (!buildTimeSDK) {
291
+ buildTimeSDK = new StorefrontSDK({
292
+ ...config,
293
+ tokenStorage: createTokenStorage(
294
+ void 0,
295
+ config.tokenStorageOptions,
296
+ config
297
+ )
298
+ });
299
+ }
300
+ return buildTimeSDK;
301
+ }
229
302
  function getStorefrontSDK(cookieStore) {
230
- const manager = NextJSSDKManager.getInstance();
231
303
  if (typeof window !== "undefined") {
232
304
  if (cookieStore) {
233
305
  console.warn(
234
306
  "Cookie store passed in client environment - this will be ignored"
235
307
  );
236
308
  }
237
- return manager.getClientSDK();
309
+ const config = getConfig();
310
+ if (!clientSDK) {
311
+ clientSDK = new StorefrontSDK({
312
+ ...config,
313
+ tokenStorage: createTokenStorage(
314
+ void 0,
315
+ config.tokenStorageOptions,
316
+ config
317
+ )
318
+ });
319
+ }
320
+ return clientSDK;
238
321
  }
239
- if (!cookieStore) {
322
+ if (cookieStore) {
323
+ return getServerSDKCached(cookieStore);
324
+ }
325
+ if (hasRequestContext()) {
240
326
  let autoDetectMessage = "";
241
327
  try {
242
328
  __require.resolve("next/headers");
@@ -245,7 +331,7 @@ function getStorefrontSDK(cookieStore) {
245
331
  \u{1F50D} Auto-detection attempted but failed. You may be in:
246
332
  - Server Action (use: const sdk = getStorefrontSDK(await cookies()))
247
333
  - API Route (use: const sdk = getStorefrontSDK(cookies()))
248
- - Middleware (token access limited)
334
+ - Server Component in App Router (use: const sdk = getStorefrontSDK(cookies()))
249
335
  `;
250
336
  } catch {
251
337
  autoDetectMessage = `
@@ -266,7 +352,7 @@ import { cookies } from 'next/headers';
266
352
  // Server Actions & Route Handlers
267
353
  const sdk = getStorefrontSDK(await cookies());
268
354
 
269
- // API Routes (Next.js 12)
355
+ // API Routes & Server Components (App Router)
270
356
  const sdk = getStorefrontSDK(cookies());
271
357
 
272
358
  \u274C Your current usage:
@@ -276,30 +362,19 @@ This is required for server-side token access.
276
362
  `.trim()
277
363
  );
278
364
  }
279
- return manager.getServerSDK(cookieStore);
280
- }
281
- function initializeStorefrontSDK(config) {
282
- const manager = NextJSSDKManager.getInstance();
283
- manager.initialize(config);
365
+ return getBuildTimeSDK();
284
366
  }
285
367
 
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;
368
+ // src/storefront.ts
369
+ function storefront(cookieStore) {
370
+ return getStorefrontSDK(cookieStore);
295
371
  }
296
372
 
297
373
  // src/index.ts
298
- import { Environment } from "@commercengine/storefront-sdk";
374
+ export * from "@commercengine/storefront-sdk";
299
375
  export {
300
376
  ClientTokenStorage,
301
- Environment,
302
377
  ServerTokenStorage,
303
- StorefrontSDKInitializer,
304
- getStorefrontSDK
378
+ getStorefrontSDK,
379
+ storefront
305
380
  };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/middleware.ts
21
+ var middleware_exports = {};
22
+ __export(middleware_exports, {
23
+ ensureStorefrontTokens: () => ensureStorefrontTokens
24
+ });
25
+ module.exports = __toCommonJS(middleware_exports);
26
+
27
+ // src/middleware-helper.ts
28
+ async function ensureStorefrontTokens(reqCookies, resCookies, opts) {
29
+ const prefix = opts.cookiePrefix ?? "ce_";
30
+ const ACCESS = `${prefix}access_token`;
31
+ const existingAccess = reqCookies.get(ACCESS)?.value;
32
+ if (existingAccess) {
33
+ return false;
34
+ }
35
+ try {
36
+ const resp = await fetch(`${opts.baseUrl}/auth/anonymous`, {
37
+ method: "POST",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ ...opts.apiKey ? { "X-Api-Key": opts.apiKey } : {}
41
+ }
42
+ });
43
+ if (!resp.ok) return false;
44
+ const data = await resp.json();
45
+ const content = data?.content;
46
+ if (!content?.access_token || !content?.refresh_token) return false;
47
+ const cookieDefaults = {
48
+ path: "/",
49
+ httpOnly: false,
50
+ sameSite: "lax",
51
+ secure: opts.cookieOptions?.secure ?? false,
52
+ maxAge: 30 * 24 * 60 * 60
53
+ // 30 days
54
+ };
55
+ const finalOpts = { ...cookieDefaults, ...opts.cookieOptions || {} };
56
+ resCookies.set(ACCESS, content.access_token, finalOpts);
57
+ resCookies.set(`${prefix}refresh_token`, content.refresh_token, finalOpts);
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+ // Annotate the CommonJS export names for ESM import in node:
64
+ 0 && (module.exports = {
65
+ ensureStorefrontTokens
66
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Minimal cookie interface compatible with Next.js middleware cookies API.
3
+ */
4
+ interface NextCookiesLike {
5
+ get: (name: string) => {
6
+ value: string;
7
+ } | undefined;
8
+ set: (name: string, value: string, options?: {
9
+ maxAge?: number;
10
+ path?: string;
11
+ domain?: string;
12
+ secure?: boolean;
13
+ sameSite?: "strict" | "lax" | "none" | (string & {});
14
+ httpOnly?: boolean;
15
+ }) => any;
16
+ }
17
+ interface EnsureTokensOptions {
18
+ baseUrl: string;
19
+ apiKey?: string;
20
+ cookiePrefix?: string;
21
+ cookieOptions?: {
22
+ maxAge?: number;
23
+ path?: string;
24
+ domain?: string;
25
+ secure?: boolean;
26
+ sameSite?: "strict" | "lax" | "none";
27
+ httpOnly?: boolean;
28
+ };
29
+ }
30
+ /**
31
+ * Pre-seed anonymous tokens in Next.js middleware when missing.
32
+ * Call this early to ensure SSR and client share the same session.
33
+ *
34
+ * Returns true if tokens were set on the response, false otherwise.
35
+ */
36
+ declare function ensureStorefrontTokens(reqCookies: NextCookiesLike, resCookies: NextCookiesLike, opts: EnsureTokensOptions): Promise<boolean>;
37
+
38
+ export { type EnsureTokensOptions, type NextCookiesLike, ensureStorefrontTokens };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Minimal cookie interface compatible with Next.js middleware cookies API.
3
+ */
4
+ interface NextCookiesLike {
5
+ get: (name: string) => {
6
+ value: string;
7
+ } | undefined;
8
+ set: (name: string, value: string, options?: {
9
+ maxAge?: number;
10
+ path?: string;
11
+ domain?: string;
12
+ secure?: boolean;
13
+ sameSite?: "strict" | "lax" | "none" | (string & {});
14
+ httpOnly?: boolean;
15
+ }) => any;
16
+ }
17
+ interface EnsureTokensOptions {
18
+ baseUrl: string;
19
+ apiKey?: string;
20
+ cookiePrefix?: string;
21
+ cookieOptions?: {
22
+ maxAge?: number;
23
+ path?: string;
24
+ domain?: string;
25
+ secure?: boolean;
26
+ sameSite?: "strict" | "lax" | "none";
27
+ httpOnly?: boolean;
28
+ };
29
+ }
30
+ /**
31
+ * Pre-seed anonymous tokens in Next.js middleware when missing.
32
+ * Call this early to ensure SSR and client share the same session.
33
+ *
34
+ * Returns true if tokens were set on the response, false otherwise.
35
+ */
36
+ declare function ensureStorefrontTokens(reqCookies: NextCookiesLike, resCookies: NextCookiesLike, opts: EnsureTokensOptions): Promise<boolean>;
37
+
38
+ export { type EnsureTokensOptions, type NextCookiesLike, ensureStorefrontTokens };
@@ -0,0 +1,39 @@
1
+ // src/middleware-helper.ts
2
+ async function ensureStorefrontTokens(reqCookies, resCookies, opts) {
3
+ const prefix = opts.cookiePrefix ?? "ce_";
4
+ const ACCESS = `${prefix}access_token`;
5
+ const existingAccess = reqCookies.get(ACCESS)?.value;
6
+ if (existingAccess) {
7
+ return false;
8
+ }
9
+ try {
10
+ const resp = await fetch(`${opts.baseUrl}/auth/anonymous`, {
11
+ method: "POST",
12
+ headers: {
13
+ "Content-Type": "application/json",
14
+ ...opts.apiKey ? { "X-Api-Key": opts.apiKey } : {}
15
+ }
16
+ });
17
+ if (!resp.ok) return false;
18
+ const data = await resp.json();
19
+ const content = data?.content;
20
+ if (!content?.access_token || !content?.refresh_token) return false;
21
+ const cookieDefaults = {
22
+ path: "/",
23
+ httpOnly: false,
24
+ sameSite: "lax",
25
+ secure: opts.cookieOptions?.secure ?? false,
26
+ maxAge: 30 * 24 * 60 * 60
27
+ // 30 days
28
+ };
29
+ const finalOpts = { ...cookieDefaults, ...opts.cookieOptions || {} };
30
+ resCookies.set(ACCESS, content.access_token, finalOpts);
31
+ resCookies.set(`${prefix}refresh_token`, content.refresh_token, finalOpts);
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+ export {
38
+ ensureStorefrontTokens
39
+ };
@@ -0,0 +1,105 @@
1
+ import { TokenStorage, StorefrontSDK, StorefrontSDKOptions } from '@commercengine/storefront-sdk';
2
+ import './storefront.cjs';
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 with request context: getStorefrontSDK(await cookies())
95
+ * - SSG/ISR (no request context): getStorefrontSDK() (uses memory storage)
96
+ */
97
+ declare function getStorefrontSDK(): StorefrontSDK;
98
+ declare function getStorefrontSDK(cookieStore: NextCookieStore): StorefrontSDK;
99
+ /**
100
+ * Initialize the SDK with configuration (internal use)
101
+ * This should be called once in your app via StorefrontSDKInitializer
102
+ */
103
+ declare function initializeStorefrontSDK(config: NextJSSDKConfig): void;
104
+
105
+ export { ClientTokenStorage as C, type NextJSSDKConfig as N, ServerTokenStorage as S, type NextJSTokenStorageOptions as a, getStorefrontSDK as g, initializeStorefrontSDK as i };