perspectapi-ts-sdk 1.1.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 +515 -0
- package/dist/index.d.mts +1770 -0
- package/dist/index.d.ts +1770 -0
- package/dist/index.js +1777 -0
- package/dist/index.mjs +1727 -0
- package/package.json +69 -0
- package/src/client/api-keys-client.ts +103 -0
- package/src/client/auth-client.ts +87 -0
- package/src/client/base-client.ts +100 -0
- package/src/client/categories-client.ts +151 -0
- package/src/client/checkout-client.ts +249 -0
- package/src/client/contact-client.ts +203 -0
- package/src/client/content-client.ts +112 -0
- package/src/client/organizations-client.ts +106 -0
- package/src/client/products-client.ts +247 -0
- package/src/client/sites-client.ts +148 -0
- package/src/client/webhooks-client.ts +199 -0
- package/src/index.ts +74 -0
- package/src/loaders.ts +644 -0
- package/src/perspect-api-client.ts +179 -0
- package/src/types/index.ts +399 -0
- package/src/utils/http-client.ts +268 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Client for PerspectAPI SDK
|
|
3
|
+
* Cloudflare Workers compatible - uses native fetch API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ApiResponse,
|
|
8
|
+
ApiError,
|
|
9
|
+
RequestOptions,
|
|
10
|
+
PerspectApiConfig
|
|
11
|
+
} from '../types';
|
|
12
|
+
|
|
13
|
+
export class HttpClient {
|
|
14
|
+
private baseUrl: string;
|
|
15
|
+
private defaultHeaders: Record<string, string>;
|
|
16
|
+
private timeout: number;
|
|
17
|
+
private retries: number;
|
|
18
|
+
|
|
19
|
+
constructor(config: PerspectApiConfig) {
|
|
20
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
21
|
+
this.timeout = config.timeout || 30000;
|
|
22
|
+
this.retries = config.retries || 3;
|
|
23
|
+
|
|
24
|
+
this.defaultHeaders = {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'User-Agent': 'perspectapi-ts-sdk/1.0.0',
|
|
27
|
+
...config.headers,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Add authentication headers
|
|
31
|
+
if (config.apiKey) {
|
|
32
|
+
this.defaultHeaders['X-API-Key'] = config.apiKey;
|
|
33
|
+
}
|
|
34
|
+
if (config.jwt) {
|
|
35
|
+
this.defaultHeaders['Authorization'] = `Bearer ${config.jwt}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Update authentication token
|
|
41
|
+
*/
|
|
42
|
+
setAuth(jwt: string) {
|
|
43
|
+
this.defaultHeaders['Authorization'] = `Bearer ${jwt}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update API key
|
|
48
|
+
*/
|
|
49
|
+
setApiKey(apiKey: string) {
|
|
50
|
+
this.defaultHeaders['X-API-Key'] = apiKey;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Remove authentication
|
|
55
|
+
*/
|
|
56
|
+
clearAuth() {
|
|
57
|
+
delete this.defaultHeaders['Authorization'];
|
|
58
|
+
delete this.defaultHeaders['X-API-Key'];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Make HTTP request with retry logic
|
|
63
|
+
*/
|
|
64
|
+
async request<T = any>(
|
|
65
|
+
endpoint: string,
|
|
66
|
+
options: RequestOptions = {}
|
|
67
|
+
): Promise<ApiResponse<T>> {
|
|
68
|
+
const url = this.buildUrl(endpoint, options.params);
|
|
69
|
+
const requestOptions = this.buildRequestOptions(options);
|
|
70
|
+
|
|
71
|
+
// Log the full request details
|
|
72
|
+
console.log(`[HTTP Client] Making ${options.method || 'GET'} request to: ${url}`);
|
|
73
|
+
console.log(`[HTTP Client] Base URL: ${this.baseUrl}`);
|
|
74
|
+
console.log(`[HTTP Client] Endpoint: ${endpoint}`);
|
|
75
|
+
console.log(`[HTTP Client] Full URL: ${url}`);
|
|
76
|
+
if (options.params) {
|
|
77
|
+
console.log(`[HTTP Client] Query params:`, options.params);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let lastError: Error;
|
|
81
|
+
|
|
82
|
+
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
|
83
|
+
try {
|
|
84
|
+
const response = await this.fetchWithTimeout(url, requestOptions);
|
|
85
|
+
return await this.handleResponse<T>(response);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
lastError = error as Error;
|
|
88
|
+
|
|
89
|
+
// Don't retry on client errors (4xx)
|
|
90
|
+
if (error && typeof error === 'object' && 'status' in error &&
|
|
91
|
+
typeof (error as any).status === 'number' && (error as any).status < 500) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Don't retry on last attempt
|
|
96
|
+
if (attempt === this.retries) {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Exponential backoff
|
|
101
|
+
await this.delay(Math.pow(2, attempt) * 1000);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
throw lastError!;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* GET request
|
|
110
|
+
*/
|
|
111
|
+
async get<T = any>(endpoint: string, params?: Record<string, any>): Promise<ApiResponse<T>> {
|
|
112
|
+
return this.request<T>(endpoint, { method: 'GET', params });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* POST request
|
|
117
|
+
*/
|
|
118
|
+
async post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
119
|
+
return this.request<T>(endpoint, { method: 'POST', body });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* PUT request
|
|
124
|
+
*/
|
|
125
|
+
async put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
126
|
+
return this.request<T>(endpoint, { method: 'PUT', body });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* DELETE request
|
|
131
|
+
*/
|
|
132
|
+
async delete<T = any>(endpoint: string): Promise<ApiResponse<T>> {
|
|
133
|
+
return this.request<T>(endpoint, { method: 'DELETE' });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* PATCH request
|
|
138
|
+
*/
|
|
139
|
+
async patch<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
140
|
+
return this.request<T>(endpoint, { method: 'PATCH', body });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build full URL with query parameters
|
|
145
|
+
*/
|
|
146
|
+
private buildUrl(endpoint: string, params?: Record<string, any>): string {
|
|
147
|
+
const url = `${this.baseUrl}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
|
|
148
|
+
|
|
149
|
+
if (!params || Object.keys(params).length === 0) {
|
|
150
|
+
return url;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const searchParams = new URLSearchParams();
|
|
154
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
155
|
+
if (value !== undefined && value !== null) {
|
|
156
|
+
searchParams.append(key, String(value));
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return `${url}?${searchParams.toString()}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Build request options
|
|
165
|
+
*/
|
|
166
|
+
private buildRequestOptions(options: RequestOptions): RequestInit {
|
|
167
|
+
const headers = {
|
|
168
|
+
...this.defaultHeaders,
|
|
169
|
+
...options.headers,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const requestOptions: RequestInit = {
|
|
173
|
+
method: options.method || 'GET',
|
|
174
|
+
headers,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (options.body && options.method !== 'GET') {
|
|
178
|
+
if (typeof options.body === 'string') {
|
|
179
|
+
requestOptions.body = options.body;
|
|
180
|
+
} else {
|
|
181
|
+
requestOptions.body = JSON.stringify(options.body);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return requestOptions;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fetch with timeout support
|
|
190
|
+
*/
|
|
191
|
+
private async fetchWithTimeout(url: string, options: RequestInit): Promise<Response> {
|
|
192
|
+
const controller = new AbortController();
|
|
193
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
const response = await fetch(url, {
|
|
197
|
+
...options,
|
|
198
|
+
signal: controller.signal,
|
|
199
|
+
});
|
|
200
|
+
return response;
|
|
201
|
+
} finally {
|
|
202
|
+
clearTimeout(timeoutId);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Handle response and errors
|
|
208
|
+
*/
|
|
209
|
+
private async handleResponse<T>(response: Response): Promise<ApiResponse<T>> {
|
|
210
|
+
const contentType = response.headers.get('content-type');
|
|
211
|
+
const isJson = contentType?.includes('application/json');
|
|
212
|
+
|
|
213
|
+
let data: any;
|
|
214
|
+
try {
|
|
215
|
+
data = isJson ? await response.json() : await response.text();
|
|
216
|
+
} catch (error) {
|
|
217
|
+
data = null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
const error: ApiError = {
|
|
222
|
+
message: data?.error || data?.message || `HTTP ${response.status}: ${response.statusText}`,
|
|
223
|
+
status: response.status,
|
|
224
|
+
code: data?.code,
|
|
225
|
+
details: data,
|
|
226
|
+
};
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Handle different response formats
|
|
231
|
+
if (isJson && typeof data === 'object') {
|
|
232
|
+
// If response has data property, return as-is
|
|
233
|
+
if ('data' in data || 'message' in data || 'error' in data) {
|
|
234
|
+
return data as ApiResponse<T>;
|
|
235
|
+
}
|
|
236
|
+
// Otherwise wrap in data property
|
|
237
|
+
return { data: data as T, success: true };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { data: data as T, success: true };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Delay utility for retries
|
|
245
|
+
*/
|
|
246
|
+
private delay(ms: number): Promise<void> {
|
|
247
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Create API Error from unknown error
|
|
253
|
+
*/
|
|
254
|
+
export function createApiError(error: unknown): ApiError {
|
|
255
|
+
if (error instanceof Error) {
|
|
256
|
+
return error as ApiError;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check if it's already an ApiError-like object
|
|
260
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
261
|
+
return error as ApiError;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
message: 'Unknown error occurred',
|
|
266
|
+
details: error,
|
|
267
|
+
};
|
|
268
|
+
}
|