@commercengine/storefront-sdk 0.1.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.
@@ -0,0 +1,146 @@
1
+ import createClient from "openapi-fetch";
2
+ /**
3
+ * Available API environments
4
+ */
5
+ export declare enum Environment {
6
+ /**
7
+ * Staging environment
8
+ */
9
+ Staging = "staging",
10
+ /**
11
+ * Production environment
12
+ */
13
+ Production = "production"
14
+ }
15
+ import type { paths } from "../types/storefront";
16
+ /**
17
+ * Configuration options for the StorefrontAPI client
18
+ */
19
+ export interface StorefrontAPIConfig {
20
+ /**
21
+ * The store ID to use for API requests
22
+ */
23
+ storeId: string;
24
+ /**
25
+ * The environment to use (staging or production)
26
+ */
27
+ environment?: Environment;
28
+ /**
29
+ * Custom base URL (overrides environment-based URL if provided)
30
+ */
31
+ baseUrl?: string;
32
+ /**
33
+ * Optional authentication token
34
+ */
35
+ token?: string;
36
+ /**
37
+ * X-Api-Key for anonymous authentication endpoints
38
+ * Required for initial authentication
39
+ */
40
+ apiKey?: string;
41
+ /**
42
+ * Optional timeout in milliseconds
43
+ */
44
+ timeout?: number;
45
+ }
46
+ /**
47
+ * Error response from the API
48
+ */
49
+ export type ApiErrorResponse = {
50
+ message?: string;
51
+ success: boolean;
52
+ code?: string;
53
+ error?: Record<string, any>;
54
+ };
55
+ /**
56
+ * Base API client for Storefront API
57
+ */
58
+ export declare class StorefrontAPIClient {
59
+ protected client: ReturnType<typeof createClient<paths>>;
60
+ protected config: StorefrontAPIConfig;
61
+ private headers;
62
+ private readonly baseUrl;
63
+ private isRefreshing;
64
+ /**
65
+ * Create a new StorefrontAPIClient
66
+ *
67
+ * @param config - Configuration for the API client
68
+ *
69
+ * @remarks
70
+ * This client implements a token refresh mechanism that will:
71
+ * 1. Automatically retry requests that fail with a 401 error
72
+ * 2. Refresh the token before retrying the request
73
+ * 3. If the token refresh fails, the original error will be thrown
74
+ *
75
+ * This behavior is inherited by all client classes that extend StorefrontAPIClient.
76
+ *
77
+ * When using the AuthClient, tokens can be stored persistently in:
78
+ * - Memory (default) - Tokens are lost when the page refreshes or app restarts
79
+ * - Browser localStorage - Tokens persist across page refreshes
80
+ * - Cookies - Tokens persist across page refreshes and can be read server-side
81
+ * - Next.js cookies API - For server-side components
82
+ */
83
+ constructor(config: StorefrontAPIConfig);
84
+ /**
85
+ * Constructs the base URL from the configuration
86
+ *
87
+ * @param config - The client configuration
88
+ * @returns The constructed base URL
89
+ */
90
+ private getBaseUrlFromConfig;
91
+ /**
92
+ * Get the base URL of the API
93
+ *
94
+ * @returns The base URL of the API
95
+ */
96
+ getBaseUrl(): string;
97
+ /**
98
+ * Get the authorization header value
99
+ *
100
+ * @returns The Authorization header value or empty string if no token is set
101
+ */
102
+ getAuthorizationHeader(): string;
103
+ /**
104
+ * Set the authentication token
105
+ *
106
+ * @param token - The authentication token
107
+ */
108
+ setToken(token: string): void;
109
+ /**
110
+ * Clear the authentication token
111
+ */
112
+ clearToken(): void;
113
+ /**
114
+ * Set the X-Api-Key header
115
+ *
116
+ * @param apiKey - The API key to set
117
+ */
118
+ setApiKey(apiKey: string): void;
119
+ /**
120
+ * Clear the X-Api-Key header
121
+ */
122
+ clearApiKey(): void;
123
+ /**
124
+ * Handle API errors
125
+ *
126
+ * @param error - Error object from OpenAPI-Fetch
127
+ * @throws Rethrows the error with additional context
128
+ */
129
+ protected handleError(error: any): Promise<never>;
130
+ /**
131
+ * Attempt to refresh the token
132
+ * This is a placeholder method that will be overridden by AuthClient
133
+ *
134
+ * @returns Promise that resolves to true if token was refreshed, false otherwise
135
+ */
136
+ protected attemptTokenRefresh(): Promise<boolean>;
137
+ /**
138
+ * Execute a request with automatic token refresh handling
139
+ * For AuthClient, this will attempt to refresh the token and retry once on 401 errors
140
+ * Base implementation just executes the request
141
+ *
142
+ * @param requestFn - Function that executes the API request
143
+ * @returns Promise with the API response
144
+ */
145
+ protected executeRequest<T>(requestFn: () => Promise<T>): Promise<T>;
146
+ }
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.StorefrontAPIClient = exports.Environment = void 0;
7
+ const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
8
+ /**
9
+ * Available API environments
10
+ */
11
+ var Environment;
12
+ (function (Environment) {
13
+ /**
14
+ * Staging environment
15
+ */
16
+ Environment["Staging"] = "staging";
17
+ /**
18
+ * Production environment
19
+ */
20
+ Environment["Production"] = "production";
21
+ })(Environment || (exports.Environment = Environment = {}));
22
+ /**
23
+ * Base API client for Storefront API
24
+ */
25
+ class StorefrontAPIClient {
26
+ /**
27
+ * Create a new StorefrontAPIClient
28
+ *
29
+ * @param config - Configuration for the API client
30
+ *
31
+ * @remarks
32
+ * This client implements a token refresh mechanism that will:
33
+ * 1. Automatically retry requests that fail with a 401 error
34
+ * 2. Refresh the token before retrying the request
35
+ * 3. If the token refresh fails, the original error will be thrown
36
+ *
37
+ * This behavior is inherited by all client classes that extend StorefrontAPIClient.
38
+ *
39
+ * When using the AuthClient, tokens can be stored persistently in:
40
+ * - Memory (default) - Tokens are lost when the page refreshes or app restarts
41
+ * - Browser localStorage - Tokens persist across page refreshes
42
+ * - Cookies - Tokens persist across page refreshes and can be read server-side
43
+ * - Next.js cookies API - For server-side components
44
+ */
45
+ constructor(config) {
46
+ this.isRefreshing = false;
47
+ this.config = config;
48
+ this.headers = {
49
+ "Content-Type": "application/json",
50
+ };
51
+ if (config.token) {
52
+ this.headers["Authorization"] = `Bearer ${config.token}`;
53
+ }
54
+ if (config.apiKey) {
55
+ this.headers["X-Api-Key"] = config.apiKey;
56
+ }
57
+ // Determine base URL from environment or use custom URL if provided
58
+ this.baseUrl = this.getBaseUrlFromConfig(config);
59
+ this.client = (0, openapi_fetch_1.default)({
60
+ baseUrl: this.baseUrl,
61
+ fetch: (input, init) => {
62
+ // Add timeout if configured
63
+ const timeoutSignal = config.timeout
64
+ ? AbortSignal.timeout(config.timeout)
65
+ : undefined;
66
+ // Merge headers
67
+ const headers = {
68
+ ...this.headers,
69
+ ...(init?.headers || {}),
70
+ };
71
+ return fetch(input, {
72
+ ...init,
73
+ headers,
74
+ signal: timeoutSignal || init?.signal,
75
+ });
76
+ },
77
+ });
78
+ }
79
+ /**
80
+ * Constructs the base URL from the configuration
81
+ *
82
+ * @param config - The client configuration
83
+ * @returns The constructed base URL
84
+ */
85
+ getBaseUrlFromConfig(config) {
86
+ // If explicit baseUrl is provided, use it
87
+ if (config.baseUrl) {
88
+ return config.baseUrl;
89
+ }
90
+ // Otherwise construct URL based on environment and storeId
91
+ const env = config.environment || Environment.Production;
92
+ switch (env) {
93
+ case Environment.Staging:
94
+ return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
95
+ case Environment.Production:
96
+ default:
97
+ return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
98
+ }
99
+ }
100
+ /**
101
+ * Get the base URL of the API
102
+ *
103
+ * @returns The base URL of the API
104
+ */
105
+ getBaseUrl() {
106
+ return this.baseUrl;
107
+ }
108
+ /**
109
+ * Get the authorization header value
110
+ *
111
+ * @returns The Authorization header value or empty string if no token is set
112
+ */
113
+ getAuthorizationHeader() {
114
+ return this.config.token ? `Bearer ${this.config.token}` : "";
115
+ }
116
+ /**
117
+ * Set the authentication token
118
+ *
119
+ * @param token - The authentication token
120
+ */
121
+ setToken(token) {
122
+ this.config.token = token;
123
+ this.headers["Authorization"] = `Bearer ${token}`;
124
+ }
125
+ /**
126
+ * Clear the authentication token
127
+ */
128
+ clearToken() {
129
+ this.config.token = undefined;
130
+ delete this.headers["Authorization"];
131
+ }
132
+ /**
133
+ * Set the X-Api-Key header
134
+ *
135
+ * @param apiKey - The API key to set
136
+ */
137
+ setApiKey(apiKey) {
138
+ this.config.apiKey = apiKey;
139
+ this.headers["X-Api-Key"] = apiKey;
140
+ }
141
+ /**
142
+ * Clear the X-Api-Key header
143
+ */
144
+ clearApiKey() {
145
+ this.config.apiKey = undefined;
146
+ delete this.headers["X-Api-Key"];
147
+ }
148
+ /**
149
+ * Handle API errors
150
+ *
151
+ * @param error - Error object from OpenAPI-Fetch
152
+ * @throws Rethrows the error with additional context
153
+ */
154
+ async handleError(error) {
155
+ // Extract status code and error data from OpenAPI-Fetch error response
156
+ const statusCode = error.status || (error.response?.status ? error.response.status : 500);
157
+ const errorData = error.data ||
158
+ error.response?.data || { message: error.message || "Unknown error" };
159
+ // If we have a 401 error and we're not already in a refresh operation,
160
+ // attempt to refresh the token
161
+ if (statusCode === 401 && !this.isRefreshing) {
162
+ try {
163
+ this.isRefreshing = true;
164
+ const refreshed = await this.attemptTokenRefresh();
165
+ if (refreshed) {
166
+ // Token refreshed successfully - no need to throw error
167
+ // The calling method should retry the request
168
+ this.isRefreshing = false;
169
+ throw new Error("Token refreshed, please retry the request");
170
+ }
171
+ }
172
+ catch (refreshError) {
173
+ // If refresh fails, continue to throw the original error
174
+ }
175
+ finally {
176
+ this.isRefreshing = false;
177
+ }
178
+ }
179
+ if (statusCode === 401) {
180
+ throw new Error("Unauthorized: Please check your authentication token");
181
+ }
182
+ else if (statusCode === 404) {
183
+ throw new Error("Resource not found");
184
+ }
185
+ else if (errorData?.message) {
186
+ throw new Error(`API Error (${statusCode}): ${errorData.message}`);
187
+ }
188
+ else {
189
+ throw new Error(`API Error (${statusCode})`);
190
+ }
191
+ }
192
+ /**
193
+ * Attempt to refresh the token
194
+ * This is a placeholder method that will be overridden by AuthClient
195
+ *
196
+ * @returns Promise that resolves to true if token was refreshed, false otherwise
197
+ */
198
+ async attemptTokenRefresh() {
199
+ // Base implementation does nothing
200
+ // Will be overridden by AuthClient
201
+ return false;
202
+ }
203
+ /**
204
+ * Execute a request with automatic token refresh handling
205
+ * For AuthClient, this will attempt to refresh the token and retry once on 401 errors
206
+ * Base implementation just executes the request
207
+ *
208
+ * @param requestFn - Function that executes the API request
209
+ * @returns Promise with the API response
210
+ */
211
+ async executeRequest(requestFn) {
212
+ try {
213
+ return await requestFn();
214
+ }
215
+ catch (error) {
216
+ // Handle 401 errors by attempting to refresh token
217
+ if (error &&
218
+ typeof error === "object" &&
219
+ "status" in error &&
220
+ error.status === 401) {
221
+ // Attempt to refresh the token
222
+ const refreshed = await this.attemptTokenRefresh();
223
+ if (refreshed) {
224
+ // If token was refreshed, retry the request once
225
+ return await requestFn();
226
+ }
227
+ }
228
+ // Check if the error is from handleError and is the special refresh token message
229
+ if (error instanceof Error &&
230
+ error.message === "Token refreshed, please retry the request") {
231
+ // If token was refreshed, retry the request once
232
+ return await requestFn();
233
+ }
234
+ // Otherwise, throw the error to be handled by the caller
235
+ throw error;
236
+ }
237
+ }
238
+ }
239
+ exports.StorefrontAPIClient = StorefrontAPIClient;