perspectapi-ts-sdk 1.2.0 → 1.3.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 +127 -0
- package/dist/index.d.mts +26 -12
- package/dist/index.d.ts +26 -12
- package/dist/index.js +49 -24
- package/dist/index.mjs +49 -24
- package/package.json +1 -1
- package/src/client/base-client.ts +11 -8
- package/src/client/contact-client.ts +14 -2
- package/src/client/newsletter-client.ts +27 -6
- package/src/types/index.ts +1 -0
- package/src/utils/http-client.ts +13 -8
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A comprehensive TypeScript SDK for PerspectAPI, designed to work seamlessly with
|
|
|
12
12
|
- 🔑 **Multiple Auth Methods** - Support for JWT tokens and API keys
|
|
13
13
|
- 📊 **Comprehensive Coverage** - All PerspectAPI endpoints supported
|
|
14
14
|
- 🧩 **High-Level Loaders** - Drop-in helpers for products, content, and checkout flows with fallbacks
|
|
15
|
+
- 📧 **Newsletter Management** - Complete newsletter subscription system with double opt-in, preferences, and lists
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -376,6 +377,132 @@ const submissions = await client.contact.getContactSubmissions(siteName, {
|
|
|
376
377
|
await client.contact.updateContactStatus(siteName, submission.data.id, 'read');
|
|
377
378
|
```
|
|
378
379
|
|
|
380
|
+
### Newsletter Subscriptions
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
const siteName = 'your-site-name';
|
|
384
|
+
|
|
385
|
+
// Subscribe to newsletter (with double opt-in)
|
|
386
|
+
const subscription = await client.newsletter.subscribe(siteName, {
|
|
387
|
+
email: 'subscriber@example.com',
|
|
388
|
+
name: 'Jane Doe',
|
|
389
|
+
list_ids: ['list_default'], // Optional: subscribe to specific lists
|
|
390
|
+
frequency: 'weekly', // instant, daily, weekly, monthly
|
|
391
|
+
topics: ['news', 'updates'], // Optional: topic preferences
|
|
392
|
+
double_opt_in: true, // Default: true for GDPR compliance
|
|
393
|
+
turnstile_token: 'token' // Optional: Turnstile verification
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Confirm subscription via email token
|
|
397
|
+
const confirmed = await client.newsletter.confirmSubscription(
|
|
398
|
+
siteName,
|
|
399
|
+
'confirmation-token-from-email'
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
// Unsubscribe
|
|
403
|
+
const unsubscribed = await client.newsletter.unsubscribe(siteName, {
|
|
404
|
+
email: 'subscriber@example.com',
|
|
405
|
+
reason: 'Too many emails' // Optional feedback
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// One-click unsubscribe (from email link)
|
|
409
|
+
await client.newsletter.unsubscribeByToken(siteName, 'unsubscribe-token');
|
|
410
|
+
|
|
411
|
+
// Update preferences
|
|
412
|
+
await client.newsletter.updatePreferences(siteName, 'subscriber@example.com', {
|
|
413
|
+
frequency: 'monthly',
|
|
414
|
+
topics: ['product-updates'],
|
|
415
|
+
email_format: 'html', // html, text, or both
|
|
416
|
+
timezone: 'America/New_York',
|
|
417
|
+
track_opens: false,
|
|
418
|
+
track_clicks: false
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Check subscription status
|
|
422
|
+
const status = await client.newsletter.getStatus(siteName, 'subscriber@example.com');
|
|
423
|
+
console.log('Subscribed:', status.data.subscribed);
|
|
424
|
+
console.log('Status:', status.data.status); // pending, confirmed, unsubscribed
|
|
425
|
+
|
|
426
|
+
// Get available newsletter lists
|
|
427
|
+
const lists = await client.newsletter.getLists(siteName);
|
|
428
|
+
console.log('Available lists:', lists.data.lists);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### Newsletter Admin Functions
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Get all subscriptions (admin only)
|
|
435
|
+
const subscriptions = await client.newsletter.getSubscriptions(siteName, {
|
|
436
|
+
page: 1,
|
|
437
|
+
limit: 100,
|
|
438
|
+
status: 'confirmed',
|
|
439
|
+
list_id: 'list_weekly',
|
|
440
|
+
search: 'john',
|
|
441
|
+
startDate: '2024-01-01',
|
|
442
|
+
endDate: '2024-12-31'
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
// Update subscription status
|
|
446
|
+
await client.newsletter.updateSubscriptionStatus(
|
|
447
|
+
siteName,
|
|
448
|
+
'sub_123',
|
|
449
|
+
'unsubscribed',
|
|
450
|
+
'Admin action: User requested via support'
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
// Bulk operations
|
|
454
|
+
await client.newsletter.bulkUpdateSubscriptions(siteName, {
|
|
455
|
+
ids: ['sub_123', 'sub_456'],
|
|
456
|
+
action: 'add_to_list',
|
|
457
|
+
list_id: 'list_special_offers'
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// List management
|
|
461
|
+
const newList = await client.newsletter.createList(siteName, {
|
|
462
|
+
list_name: 'VIP Customers',
|
|
463
|
+
slug: 'vip-customers',
|
|
464
|
+
description: 'Exclusive updates for VIP customers',
|
|
465
|
+
is_public: false,
|
|
466
|
+
is_default: false,
|
|
467
|
+
double_opt_in: true,
|
|
468
|
+
welcome_email_enabled: true
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
await client.newsletter.updateList(siteName, 'list_123', {
|
|
472
|
+
description: 'Updated description',
|
|
473
|
+
is_public: true
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Get statistics
|
|
477
|
+
const stats = await client.newsletter.getStatistics(siteName, {
|
|
478
|
+
startDate: '2024-01-01',
|
|
479
|
+
endDate: '2024-12-31',
|
|
480
|
+
list_id: 'list_weekly'
|
|
481
|
+
});
|
|
482
|
+
console.log('Total subscribers:', stats.data.totalSubscribers);
|
|
483
|
+
console.log('Open rate:', stats.data.engagementMetrics.averageOpenRate);
|
|
484
|
+
|
|
485
|
+
// Export subscriptions
|
|
486
|
+
const exportData = await client.newsletter.exportSubscriptions(siteName, {
|
|
487
|
+
format: 'csv', // csv, json, or xlsx
|
|
488
|
+
status: 'confirmed',
|
|
489
|
+
list_id: 'list_weekly'
|
|
490
|
+
});
|
|
491
|
+
console.log('Download URL:', exportData.data.downloadUrl);
|
|
492
|
+
|
|
493
|
+
// Import subscriptions
|
|
494
|
+
const importResult = await client.newsletter.importSubscriptions(siteName, {
|
|
495
|
+
subscriptions: [
|
|
496
|
+
{ email: 'user1@example.com', name: 'User One', lists: ['list_weekly'] },
|
|
497
|
+
{ email: 'user2@example.com', name: 'User Two', lists: ['list_daily'] }
|
|
498
|
+
],
|
|
499
|
+
skip_confirmation: false, // Skip double opt-in for imported users
|
|
500
|
+
update_existing: true // Update if email already exists
|
|
501
|
+
});
|
|
502
|
+
console.log('Imported:', importResult.data.imported);
|
|
503
|
+
console.log('Failed:', importResult.data.failed);
|
|
504
|
+
```
|
|
505
|
+
|
|
379
506
|
## Configuration Options
|
|
380
507
|
|
|
381
508
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -397,6 +397,7 @@ interface RequestOptions {
|
|
|
397
397
|
body?: any;
|
|
398
398
|
params?: Record<string, string | number | boolean>;
|
|
399
399
|
timeout?: number;
|
|
400
|
+
csrfToken?: string;
|
|
400
401
|
}
|
|
401
402
|
|
|
402
403
|
/**
|
|
@@ -433,19 +434,19 @@ declare class HttpClient {
|
|
|
433
434
|
/**
|
|
434
435
|
* POST request
|
|
435
436
|
*/
|
|
436
|
-
post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
|
|
437
|
+
post<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
437
438
|
/**
|
|
438
439
|
* PUT request
|
|
439
440
|
*/
|
|
440
|
-
put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
|
|
441
|
+
put<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
441
442
|
/**
|
|
442
443
|
* DELETE request
|
|
443
444
|
*/
|
|
444
|
-
delete<T = any>(endpoint: string): Promise<ApiResponse<T>>;
|
|
445
|
+
delete<T = any>(endpoint: string, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
445
446
|
/**
|
|
446
447
|
* PATCH request
|
|
447
448
|
*/
|
|
448
|
-
patch<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
|
|
449
|
+
patch<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
449
450
|
/**
|
|
450
451
|
* Build full URL with query parameters
|
|
451
452
|
*/
|
|
@@ -501,19 +502,19 @@ declare abstract class BaseClient {
|
|
|
501
502
|
/**
|
|
502
503
|
* Handle create operations
|
|
503
504
|
*/
|
|
504
|
-
protected create<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
|
|
505
|
+
protected create<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
|
|
505
506
|
/**
|
|
506
507
|
* Handle update operations
|
|
507
508
|
*/
|
|
508
|
-
protected update<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
|
|
509
|
+
protected update<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
|
|
509
510
|
/**
|
|
510
511
|
* Handle partial update operations
|
|
511
512
|
*/
|
|
512
|
-
protected patch<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
|
|
513
|
+
protected patch<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
|
|
513
514
|
/**
|
|
514
515
|
* Handle delete operations
|
|
515
516
|
*/
|
|
516
|
-
protected delete<T = any>(endpoint: string): Promise<ApiResponse<T>>;
|
|
517
|
+
protected delete<T = any>(endpoint: string, csrfToken?: string): Promise<ApiResponse<T>>;
|
|
517
518
|
}
|
|
518
519
|
|
|
519
520
|
/**
|
|
@@ -1457,8 +1458,11 @@ declare class ContactClient extends BaseClient {
|
|
|
1457
1458
|
private contactEndpoint;
|
|
1458
1459
|
/**
|
|
1459
1460
|
* Submit contact form
|
|
1461
|
+
* @param siteName - The site to submit contact form to
|
|
1462
|
+
* @param data - Contact form data
|
|
1463
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1460
1464
|
*/
|
|
1461
|
-
submitContact(siteName: string, data: CreateContactRequest): Promise<ApiResponse<{
|
|
1465
|
+
submitContact(siteName: string, data: CreateContactRequest, csrfToken?: string): Promise<ApiResponse<{
|
|
1462
1466
|
id: string;
|
|
1463
1467
|
message: string;
|
|
1464
1468
|
status: string;
|
|
@@ -1612,16 +1616,22 @@ declare class NewsletterClient extends BaseClient {
|
|
|
1612
1616
|
private newsletterEndpoint;
|
|
1613
1617
|
/**
|
|
1614
1618
|
* Subscribe to newsletter
|
|
1619
|
+
* @param siteName - The site to subscribe to
|
|
1620
|
+
* @param data - Subscription data
|
|
1621
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1615
1622
|
*/
|
|
1616
|
-
subscribe(siteName: string, data: CreateNewsletterSubscriptionRequest): Promise<ApiResponse<NewsletterSubscribeResponse>>;
|
|
1623
|
+
subscribe(siteName: string, data: CreateNewsletterSubscriptionRequest, csrfToken?: string): Promise<ApiResponse<NewsletterSubscribeResponse>>;
|
|
1617
1624
|
/**
|
|
1618
1625
|
* Confirm newsletter subscription via token
|
|
1619
1626
|
*/
|
|
1620
1627
|
confirmSubscription(siteName: string, token: string): Promise<ApiResponse<NewsletterConfirmResponse>>;
|
|
1621
1628
|
/**
|
|
1622
1629
|
* Unsubscribe from newsletter
|
|
1630
|
+
* @param siteName - The site to unsubscribe from
|
|
1631
|
+
* @param data - Unsubscribe data
|
|
1632
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1623
1633
|
*/
|
|
1624
|
-
unsubscribe(siteName: string, data: NewsletterUnsubscribeRequest): Promise<ApiResponse<{
|
|
1634
|
+
unsubscribe(siteName: string, data: NewsletterUnsubscribeRequest, csrfToken?: string): Promise<ApiResponse<{
|
|
1625
1635
|
message: string;
|
|
1626
1636
|
}>>;
|
|
1627
1637
|
/**
|
|
@@ -1630,8 +1640,12 @@ declare class NewsletterClient extends BaseClient {
|
|
|
1630
1640
|
unsubscribeByToken(siteName: string, token: string): Promise<ApiResponse<string>>;
|
|
1631
1641
|
/**
|
|
1632
1642
|
* Update subscription preferences
|
|
1643
|
+
* @param siteName - The site name
|
|
1644
|
+
* @param email - Subscriber email
|
|
1645
|
+
* @param preferences - New preferences
|
|
1646
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1633
1647
|
*/
|
|
1634
|
-
updatePreferences(siteName: string, email: string, preferences: NewsletterPreferences): Promise<ApiResponse<{
|
|
1648
|
+
updatePreferences(siteName: string, email: string, preferences: NewsletterPreferences, csrfToken?: string): Promise<ApiResponse<{
|
|
1635
1649
|
message: string;
|
|
1636
1650
|
preferences: NewsletterPreferences;
|
|
1637
1651
|
}>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -397,6 +397,7 @@ interface RequestOptions {
|
|
|
397
397
|
body?: any;
|
|
398
398
|
params?: Record<string, string | number | boolean>;
|
|
399
399
|
timeout?: number;
|
|
400
|
+
csrfToken?: string;
|
|
400
401
|
}
|
|
401
402
|
|
|
402
403
|
/**
|
|
@@ -433,19 +434,19 @@ declare class HttpClient {
|
|
|
433
434
|
/**
|
|
434
435
|
* POST request
|
|
435
436
|
*/
|
|
436
|
-
post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
|
|
437
|
+
post<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
437
438
|
/**
|
|
438
439
|
* PUT request
|
|
439
440
|
*/
|
|
440
|
-
put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
|
|
441
|
+
put<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
441
442
|
/**
|
|
442
443
|
* DELETE request
|
|
443
444
|
*/
|
|
444
|
-
delete<T = any>(endpoint: string): Promise<ApiResponse<T>>;
|
|
445
|
+
delete<T = any>(endpoint: string, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
445
446
|
/**
|
|
446
447
|
* PATCH request
|
|
447
448
|
*/
|
|
448
|
-
patch<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
|
|
449
|
+
patch<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
|
|
449
450
|
/**
|
|
450
451
|
* Build full URL with query parameters
|
|
451
452
|
*/
|
|
@@ -501,19 +502,19 @@ declare abstract class BaseClient {
|
|
|
501
502
|
/**
|
|
502
503
|
* Handle create operations
|
|
503
504
|
*/
|
|
504
|
-
protected create<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
|
|
505
|
+
protected create<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
|
|
505
506
|
/**
|
|
506
507
|
* Handle update operations
|
|
507
508
|
*/
|
|
508
|
-
protected update<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
|
|
509
|
+
protected update<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
|
|
509
510
|
/**
|
|
510
511
|
* Handle partial update operations
|
|
511
512
|
*/
|
|
512
|
-
protected patch<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
|
|
513
|
+
protected patch<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
|
|
513
514
|
/**
|
|
514
515
|
* Handle delete operations
|
|
515
516
|
*/
|
|
516
|
-
protected delete<T = any>(endpoint: string): Promise<ApiResponse<T>>;
|
|
517
|
+
protected delete<T = any>(endpoint: string, csrfToken?: string): Promise<ApiResponse<T>>;
|
|
517
518
|
}
|
|
518
519
|
|
|
519
520
|
/**
|
|
@@ -1457,8 +1458,11 @@ declare class ContactClient extends BaseClient {
|
|
|
1457
1458
|
private contactEndpoint;
|
|
1458
1459
|
/**
|
|
1459
1460
|
* Submit contact form
|
|
1461
|
+
* @param siteName - The site to submit contact form to
|
|
1462
|
+
* @param data - Contact form data
|
|
1463
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1460
1464
|
*/
|
|
1461
|
-
submitContact(siteName: string, data: CreateContactRequest): Promise<ApiResponse<{
|
|
1465
|
+
submitContact(siteName: string, data: CreateContactRequest, csrfToken?: string): Promise<ApiResponse<{
|
|
1462
1466
|
id: string;
|
|
1463
1467
|
message: string;
|
|
1464
1468
|
status: string;
|
|
@@ -1612,16 +1616,22 @@ declare class NewsletterClient extends BaseClient {
|
|
|
1612
1616
|
private newsletterEndpoint;
|
|
1613
1617
|
/**
|
|
1614
1618
|
* Subscribe to newsletter
|
|
1619
|
+
* @param siteName - The site to subscribe to
|
|
1620
|
+
* @param data - Subscription data
|
|
1621
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1615
1622
|
*/
|
|
1616
|
-
subscribe(siteName: string, data: CreateNewsletterSubscriptionRequest): Promise<ApiResponse<NewsletterSubscribeResponse>>;
|
|
1623
|
+
subscribe(siteName: string, data: CreateNewsletterSubscriptionRequest, csrfToken?: string): Promise<ApiResponse<NewsletterSubscribeResponse>>;
|
|
1617
1624
|
/**
|
|
1618
1625
|
* Confirm newsletter subscription via token
|
|
1619
1626
|
*/
|
|
1620
1627
|
confirmSubscription(siteName: string, token: string): Promise<ApiResponse<NewsletterConfirmResponse>>;
|
|
1621
1628
|
/**
|
|
1622
1629
|
* Unsubscribe from newsletter
|
|
1630
|
+
* @param siteName - The site to unsubscribe from
|
|
1631
|
+
* @param data - Unsubscribe data
|
|
1632
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1623
1633
|
*/
|
|
1624
|
-
unsubscribe(siteName: string, data: NewsletterUnsubscribeRequest): Promise<ApiResponse<{
|
|
1634
|
+
unsubscribe(siteName: string, data: NewsletterUnsubscribeRequest, csrfToken?: string): Promise<ApiResponse<{
|
|
1625
1635
|
message: string;
|
|
1626
1636
|
}>>;
|
|
1627
1637
|
/**
|
|
@@ -1630,8 +1640,12 @@ declare class NewsletterClient extends BaseClient {
|
|
|
1630
1640
|
unsubscribeByToken(siteName: string, token: string): Promise<ApiResponse<string>>;
|
|
1631
1641
|
/**
|
|
1632
1642
|
* Update subscription preferences
|
|
1643
|
+
* @param siteName - The site name
|
|
1644
|
+
* @param email - Subscriber email
|
|
1645
|
+
* @param preferences - New preferences
|
|
1646
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1633
1647
|
*/
|
|
1634
|
-
updatePreferences(siteName: string, email: string, preferences: NewsletterPreferences): Promise<ApiResponse<{
|
|
1648
|
+
updatePreferences(siteName: string, email: string, preferences: NewsletterPreferences, csrfToken?: string): Promise<ApiResponse<{
|
|
1635
1649
|
message: string;
|
|
1636
1650
|
preferences: NewsletterPreferences;
|
|
1637
1651
|
}>>;
|
package/dist/index.js
CHANGED
|
@@ -130,26 +130,26 @@ var HttpClient = class {
|
|
|
130
130
|
/**
|
|
131
131
|
* POST request
|
|
132
132
|
*/
|
|
133
|
-
async post(endpoint, body) {
|
|
134
|
-
return this.request(endpoint, { method: "POST", body });
|
|
133
|
+
async post(endpoint, body, options) {
|
|
134
|
+
return this.request(endpoint, { method: "POST", body, ...options });
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* PUT request
|
|
138
138
|
*/
|
|
139
|
-
async put(endpoint, body) {
|
|
140
|
-
return this.request(endpoint, { method: "PUT", body });
|
|
139
|
+
async put(endpoint, body, options) {
|
|
140
|
+
return this.request(endpoint, { method: "PUT", body, ...options });
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
143
|
* DELETE request
|
|
144
144
|
*/
|
|
145
|
-
async delete(endpoint) {
|
|
146
|
-
return this.request(endpoint, { method: "DELETE" });
|
|
145
|
+
async delete(endpoint, options) {
|
|
146
|
+
return this.request(endpoint, { method: "DELETE", ...options });
|
|
147
147
|
}
|
|
148
148
|
/**
|
|
149
149
|
* PATCH request
|
|
150
150
|
*/
|
|
151
|
-
async patch(endpoint, body) {
|
|
152
|
-
return this.request(endpoint, { method: "PATCH", body });
|
|
151
|
+
async patch(endpoint, body, options) {
|
|
152
|
+
return this.request(endpoint, { method: "PATCH", body, ...options });
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
155
|
* Build full URL with query parameters
|
|
@@ -175,6 +175,9 @@ var HttpClient = class {
|
|
|
175
175
|
...this.defaultHeaders,
|
|
176
176
|
...options.headers
|
|
177
177
|
};
|
|
178
|
+
if (options.csrfToken) {
|
|
179
|
+
headers["X-CSRF-Token"] = options.csrfToken;
|
|
180
|
+
}
|
|
178
181
|
const requestOptions = {
|
|
179
182
|
method: options.method || "GET",
|
|
180
183
|
headers
|
|
@@ -298,26 +301,26 @@ var BaseClient = class {
|
|
|
298
301
|
/**
|
|
299
302
|
* Handle create operations
|
|
300
303
|
*/
|
|
301
|
-
async create(endpoint, data) {
|
|
302
|
-
return this.http.post(this.buildPath(endpoint), data);
|
|
304
|
+
async create(endpoint, data, csrfToken) {
|
|
305
|
+
return this.http.post(this.buildPath(endpoint), data, { csrfToken });
|
|
303
306
|
}
|
|
304
307
|
/**
|
|
305
308
|
* Handle update operations
|
|
306
309
|
*/
|
|
307
|
-
async update(endpoint, data) {
|
|
308
|
-
return this.http.put(this.buildPath(endpoint), data);
|
|
310
|
+
async update(endpoint, data, csrfToken) {
|
|
311
|
+
return this.http.put(this.buildPath(endpoint), data, { csrfToken });
|
|
309
312
|
}
|
|
310
313
|
/**
|
|
311
314
|
* Handle partial update operations
|
|
312
315
|
*/
|
|
313
|
-
async patch(endpoint, data) {
|
|
314
|
-
return this.http.patch(this.buildPath(endpoint), data);
|
|
316
|
+
async patch(endpoint, data, csrfToken) {
|
|
317
|
+
return this.http.patch(this.buildPath(endpoint), data, { csrfToken });
|
|
315
318
|
}
|
|
316
319
|
/**
|
|
317
320
|
* Handle delete operations
|
|
318
321
|
*/
|
|
319
|
-
async delete(endpoint) {
|
|
320
|
-
return this.http.delete(this.buildPath(endpoint));
|
|
322
|
+
async delete(endpoint, csrfToken) {
|
|
323
|
+
return this.http.delete(this.buildPath(endpoint), { csrfToken });
|
|
321
324
|
}
|
|
322
325
|
};
|
|
323
326
|
|
|
@@ -1184,9 +1187,15 @@ var ContactClient = class extends BaseClient {
|
|
|
1184
1187
|
}
|
|
1185
1188
|
/**
|
|
1186
1189
|
* Submit contact form
|
|
1190
|
+
* @param siteName - The site to submit contact form to
|
|
1191
|
+
* @param data - Contact form data
|
|
1192
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1187
1193
|
*/
|
|
1188
|
-
async submitContact(siteName, data) {
|
|
1189
|
-
|
|
1194
|
+
async submitContact(siteName, data, csrfToken) {
|
|
1195
|
+
if (typeof window !== "undefined" && !csrfToken && !data.turnstileToken) {
|
|
1196
|
+
console.warn("CSRF token recommended for browser-based contact form submissions");
|
|
1197
|
+
}
|
|
1198
|
+
return this.create(this.contactEndpoint(siteName, "/contact/submit"), data, csrfToken);
|
|
1190
1199
|
}
|
|
1191
1200
|
/**
|
|
1192
1201
|
* Get contact submission status
|
|
@@ -1281,11 +1290,18 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1281
1290
|
}
|
|
1282
1291
|
/**
|
|
1283
1292
|
* Subscribe to newsletter
|
|
1293
|
+
* @param siteName - The site to subscribe to
|
|
1294
|
+
* @param data - Subscription data
|
|
1295
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1284
1296
|
*/
|
|
1285
|
-
async subscribe(siteName, data) {
|
|
1297
|
+
async subscribe(siteName, data, csrfToken) {
|
|
1298
|
+
if (typeof window !== "undefined" && !csrfToken && !data.turnstile_token) {
|
|
1299
|
+
console.warn("CSRF token recommended for browser-based newsletter subscriptions");
|
|
1300
|
+
}
|
|
1286
1301
|
return this.create(
|
|
1287
1302
|
this.newsletterEndpoint(siteName, "/newsletter/subscribe"),
|
|
1288
|
-
data
|
|
1303
|
+
data,
|
|
1304
|
+
csrfToken
|
|
1289
1305
|
);
|
|
1290
1306
|
}
|
|
1291
1307
|
/**
|
|
@@ -1298,11 +1314,15 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1298
1314
|
}
|
|
1299
1315
|
/**
|
|
1300
1316
|
* Unsubscribe from newsletter
|
|
1317
|
+
* @param siteName - The site to unsubscribe from
|
|
1318
|
+
* @param data - Unsubscribe data
|
|
1319
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1301
1320
|
*/
|
|
1302
|
-
async unsubscribe(siteName, data) {
|
|
1321
|
+
async unsubscribe(siteName, data, csrfToken) {
|
|
1303
1322
|
return this.create(
|
|
1304
1323
|
this.newsletterEndpoint(siteName, "/newsletter/unsubscribe"),
|
|
1305
|
-
data
|
|
1324
|
+
data,
|
|
1325
|
+
csrfToken
|
|
1306
1326
|
);
|
|
1307
1327
|
}
|
|
1308
1328
|
/**
|
|
@@ -1315,11 +1335,16 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1315
1335
|
}
|
|
1316
1336
|
/**
|
|
1317
1337
|
* Update subscription preferences
|
|
1338
|
+
* @param siteName - The site name
|
|
1339
|
+
* @param email - Subscriber email
|
|
1340
|
+
* @param preferences - New preferences
|
|
1341
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1318
1342
|
*/
|
|
1319
|
-
async updatePreferences(siteName, email, preferences) {
|
|
1343
|
+
async updatePreferences(siteName, email, preferences, csrfToken) {
|
|
1320
1344
|
return this.patch(
|
|
1321
1345
|
this.newsletterEndpoint(siteName, "/newsletter/preferences"),
|
|
1322
|
-
{ email, ...preferences }
|
|
1346
|
+
{ email, ...preferences },
|
|
1347
|
+
csrfToken
|
|
1323
1348
|
);
|
|
1324
1349
|
}
|
|
1325
1350
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -79,26 +79,26 @@ var HttpClient = class {
|
|
|
79
79
|
/**
|
|
80
80
|
* POST request
|
|
81
81
|
*/
|
|
82
|
-
async post(endpoint, body) {
|
|
83
|
-
return this.request(endpoint, { method: "POST", body });
|
|
82
|
+
async post(endpoint, body, options) {
|
|
83
|
+
return this.request(endpoint, { method: "POST", body, ...options });
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
86
|
* PUT request
|
|
87
87
|
*/
|
|
88
|
-
async put(endpoint, body) {
|
|
89
|
-
return this.request(endpoint, { method: "PUT", body });
|
|
88
|
+
async put(endpoint, body, options) {
|
|
89
|
+
return this.request(endpoint, { method: "PUT", body, ...options });
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
92
|
* DELETE request
|
|
93
93
|
*/
|
|
94
|
-
async delete(endpoint) {
|
|
95
|
-
return this.request(endpoint, { method: "DELETE" });
|
|
94
|
+
async delete(endpoint, options) {
|
|
95
|
+
return this.request(endpoint, { method: "DELETE", ...options });
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
98
|
* PATCH request
|
|
99
99
|
*/
|
|
100
|
-
async patch(endpoint, body) {
|
|
101
|
-
return this.request(endpoint, { method: "PATCH", body });
|
|
100
|
+
async patch(endpoint, body, options) {
|
|
101
|
+
return this.request(endpoint, { method: "PATCH", body, ...options });
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Build full URL with query parameters
|
|
@@ -124,6 +124,9 @@ var HttpClient = class {
|
|
|
124
124
|
...this.defaultHeaders,
|
|
125
125
|
...options.headers
|
|
126
126
|
};
|
|
127
|
+
if (options.csrfToken) {
|
|
128
|
+
headers["X-CSRF-Token"] = options.csrfToken;
|
|
129
|
+
}
|
|
127
130
|
const requestOptions = {
|
|
128
131
|
method: options.method || "GET",
|
|
129
132
|
headers
|
|
@@ -247,26 +250,26 @@ var BaseClient = class {
|
|
|
247
250
|
/**
|
|
248
251
|
* Handle create operations
|
|
249
252
|
*/
|
|
250
|
-
async create(endpoint, data) {
|
|
251
|
-
return this.http.post(this.buildPath(endpoint), data);
|
|
253
|
+
async create(endpoint, data, csrfToken) {
|
|
254
|
+
return this.http.post(this.buildPath(endpoint), data, { csrfToken });
|
|
252
255
|
}
|
|
253
256
|
/**
|
|
254
257
|
* Handle update operations
|
|
255
258
|
*/
|
|
256
|
-
async update(endpoint, data) {
|
|
257
|
-
return this.http.put(this.buildPath(endpoint), data);
|
|
259
|
+
async update(endpoint, data, csrfToken) {
|
|
260
|
+
return this.http.put(this.buildPath(endpoint), data, { csrfToken });
|
|
258
261
|
}
|
|
259
262
|
/**
|
|
260
263
|
* Handle partial update operations
|
|
261
264
|
*/
|
|
262
|
-
async patch(endpoint, data) {
|
|
263
|
-
return this.http.patch(this.buildPath(endpoint), data);
|
|
265
|
+
async patch(endpoint, data, csrfToken) {
|
|
266
|
+
return this.http.patch(this.buildPath(endpoint), data, { csrfToken });
|
|
264
267
|
}
|
|
265
268
|
/**
|
|
266
269
|
* Handle delete operations
|
|
267
270
|
*/
|
|
268
|
-
async delete(endpoint) {
|
|
269
|
-
return this.http.delete(this.buildPath(endpoint));
|
|
271
|
+
async delete(endpoint, csrfToken) {
|
|
272
|
+
return this.http.delete(this.buildPath(endpoint), { csrfToken });
|
|
270
273
|
}
|
|
271
274
|
};
|
|
272
275
|
|
|
@@ -1133,9 +1136,15 @@ var ContactClient = class extends BaseClient {
|
|
|
1133
1136
|
}
|
|
1134
1137
|
/**
|
|
1135
1138
|
* Submit contact form
|
|
1139
|
+
* @param siteName - The site to submit contact form to
|
|
1140
|
+
* @param data - Contact form data
|
|
1141
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1136
1142
|
*/
|
|
1137
|
-
async submitContact(siteName, data) {
|
|
1138
|
-
|
|
1143
|
+
async submitContact(siteName, data, csrfToken) {
|
|
1144
|
+
if (typeof window !== "undefined" && !csrfToken && !data.turnstileToken) {
|
|
1145
|
+
console.warn("CSRF token recommended for browser-based contact form submissions");
|
|
1146
|
+
}
|
|
1147
|
+
return this.create(this.contactEndpoint(siteName, "/contact/submit"), data, csrfToken);
|
|
1139
1148
|
}
|
|
1140
1149
|
/**
|
|
1141
1150
|
* Get contact submission status
|
|
@@ -1230,11 +1239,18 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1230
1239
|
}
|
|
1231
1240
|
/**
|
|
1232
1241
|
* Subscribe to newsletter
|
|
1242
|
+
* @param siteName - The site to subscribe to
|
|
1243
|
+
* @param data - Subscription data
|
|
1244
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1233
1245
|
*/
|
|
1234
|
-
async subscribe(siteName, data) {
|
|
1246
|
+
async subscribe(siteName, data, csrfToken) {
|
|
1247
|
+
if (typeof window !== "undefined" && !csrfToken && !data.turnstile_token) {
|
|
1248
|
+
console.warn("CSRF token recommended for browser-based newsletter subscriptions");
|
|
1249
|
+
}
|
|
1235
1250
|
return this.create(
|
|
1236
1251
|
this.newsletterEndpoint(siteName, "/newsletter/subscribe"),
|
|
1237
|
-
data
|
|
1252
|
+
data,
|
|
1253
|
+
csrfToken
|
|
1238
1254
|
);
|
|
1239
1255
|
}
|
|
1240
1256
|
/**
|
|
@@ -1247,11 +1263,15 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1247
1263
|
}
|
|
1248
1264
|
/**
|
|
1249
1265
|
* Unsubscribe from newsletter
|
|
1266
|
+
* @param siteName - The site to unsubscribe from
|
|
1267
|
+
* @param data - Unsubscribe data
|
|
1268
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1250
1269
|
*/
|
|
1251
|
-
async unsubscribe(siteName, data) {
|
|
1270
|
+
async unsubscribe(siteName, data, csrfToken) {
|
|
1252
1271
|
return this.create(
|
|
1253
1272
|
this.newsletterEndpoint(siteName, "/newsletter/unsubscribe"),
|
|
1254
|
-
data
|
|
1273
|
+
data,
|
|
1274
|
+
csrfToken
|
|
1255
1275
|
);
|
|
1256
1276
|
}
|
|
1257
1277
|
/**
|
|
@@ -1264,11 +1284,16 @@ var NewsletterClient = class extends BaseClient {
|
|
|
1264
1284
|
}
|
|
1265
1285
|
/**
|
|
1266
1286
|
* Update subscription preferences
|
|
1287
|
+
* @param siteName - The site name
|
|
1288
|
+
* @param email - Subscriber email
|
|
1289
|
+
* @param preferences - New preferences
|
|
1290
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
1267
1291
|
*/
|
|
1268
|
-
async updatePreferences(siteName, email, preferences) {
|
|
1292
|
+
async updatePreferences(siteName, email, preferences, csrfToken) {
|
|
1269
1293
|
return this.patch(
|
|
1270
1294
|
this.newsletterEndpoint(siteName, "/newsletter/preferences"),
|
|
1271
|
-
{ email, ...preferences }
|
|
1295
|
+
{ email, ...preferences },
|
|
1296
|
+
csrfToken
|
|
1272
1297
|
);
|
|
1273
1298
|
}
|
|
1274
1299
|
/**
|
package/package.json
CHANGED
|
@@ -66,9 +66,10 @@ export abstract class BaseClient {
|
|
|
66
66
|
*/
|
|
67
67
|
protected async create<T, R = T>(
|
|
68
68
|
endpoint: string,
|
|
69
|
-
data: T
|
|
69
|
+
data: T,
|
|
70
|
+
csrfToken?: string
|
|
70
71
|
): Promise<ApiResponse<R>> {
|
|
71
|
-
return this.http.post<R>(this.buildPath(endpoint), data);
|
|
72
|
+
return this.http.post<R>(this.buildPath(endpoint), data, { csrfToken });
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
/**
|
|
@@ -76,9 +77,10 @@ export abstract class BaseClient {
|
|
|
76
77
|
*/
|
|
77
78
|
protected async update<T, R = T>(
|
|
78
79
|
endpoint: string,
|
|
79
|
-
data: T
|
|
80
|
+
data: T,
|
|
81
|
+
csrfToken?: string
|
|
80
82
|
): Promise<ApiResponse<R>> {
|
|
81
|
-
return this.http.put<R>(this.buildPath(endpoint), data);
|
|
83
|
+
return this.http.put<R>(this.buildPath(endpoint), data, { csrfToken });
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
/**
|
|
@@ -86,15 +88,16 @@ export abstract class BaseClient {
|
|
|
86
88
|
*/
|
|
87
89
|
protected async patch<T, R = T>(
|
|
88
90
|
endpoint: string,
|
|
89
|
-
data: T
|
|
91
|
+
data: T,
|
|
92
|
+
csrfToken?: string
|
|
90
93
|
): Promise<ApiResponse<R>> {
|
|
91
|
-
return this.http.patch<R>(this.buildPath(endpoint), data);
|
|
94
|
+
return this.http.patch<R>(this.buildPath(endpoint), data, { csrfToken });
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
/**
|
|
95
98
|
* Handle delete operations
|
|
96
99
|
*/
|
|
97
|
-
protected async delete<T = any>(endpoint: string): Promise<ApiResponse<T>> {
|
|
98
|
-
return this.http.delete<T>(this.buildPath(endpoint));
|
|
100
|
+
protected async delete<T = any>(endpoint: string, csrfToken?: string): Promise<ApiResponse<T>> {
|
|
101
|
+
return this.http.delete<T>(this.buildPath(endpoint), { csrfToken });
|
|
99
102
|
}
|
|
100
103
|
}
|
|
@@ -24,17 +24,29 @@ export class ContactClient extends BaseClient {
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Submit contact form
|
|
27
|
+
* @param siteName - The site to submit contact form to
|
|
28
|
+
* @param data - Contact form data
|
|
29
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
27
30
|
*/
|
|
28
|
-
async submitContact(
|
|
31
|
+
async submitContact(
|
|
32
|
+
siteName: string,
|
|
33
|
+
data: CreateContactRequest,
|
|
34
|
+
csrfToken?: string
|
|
35
|
+
): Promise<ApiResponse<{
|
|
29
36
|
id: string;
|
|
30
37
|
message: string;
|
|
31
38
|
status: string;
|
|
32
39
|
}>> {
|
|
40
|
+
// CSRF token is required for browser submissions
|
|
41
|
+
if (typeof window !== 'undefined' && !csrfToken && !data.turnstileToken) {
|
|
42
|
+
console.warn('CSRF token recommended for browser-based contact form submissions');
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
return this.create<CreateContactRequest, {
|
|
34
46
|
id: string;
|
|
35
47
|
message: string;
|
|
36
48
|
status: string;
|
|
37
|
-
}>(this.contactEndpoint(siteName, '/contact/submit'), data);
|
|
49
|
+
}>(this.contactEndpoint(siteName, '/contact/submit'), data, csrfToken);
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
/**
|
|
@@ -30,14 +30,24 @@ export class NewsletterClient extends BaseClient {
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Subscribe to newsletter
|
|
33
|
+
* @param siteName - The site to subscribe to
|
|
34
|
+
* @param data - Subscription data
|
|
35
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
33
36
|
*/
|
|
34
37
|
async subscribe(
|
|
35
38
|
siteName: string,
|
|
36
|
-
data: CreateNewsletterSubscriptionRequest
|
|
39
|
+
data: CreateNewsletterSubscriptionRequest,
|
|
40
|
+
csrfToken?: string
|
|
37
41
|
): Promise<ApiResponse<NewsletterSubscribeResponse>> {
|
|
42
|
+
// CSRF token is required for browser submissions
|
|
43
|
+
if (typeof window !== 'undefined' && !csrfToken && !data.turnstile_token) {
|
|
44
|
+
console.warn('CSRF token recommended for browser-based newsletter subscriptions');
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
return this.create<CreateNewsletterSubscriptionRequest, NewsletterSubscribeResponse>(
|
|
39
48
|
this.newsletterEndpoint(siteName, '/newsletter/subscribe'),
|
|
40
|
-
data
|
|
49
|
+
data,
|
|
50
|
+
csrfToken
|
|
41
51
|
);
|
|
42
52
|
}
|
|
43
53
|
|
|
@@ -55,14 +65,19 @@ export class NewsletterClient extends BaseClient {
|
|
|
55
65
|
|
|
56
66
|
/**
|
|
57
67
|
* Unsubscribe from newsletter
|
|
68
|
+
* @param siteName - The site to unsubscribe from
|
|
69
|
+
* @param data - Unsubscribe data
|
|
70
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
58
71
|
*/
|
|
59
72
|
async unsubscribe(
|
|
60
73
|
siteName: string,
|
|
61
|
-
data: NewsletterUnsubscribeRequest
|
|
74
|
+
data: NewsletterUnsubscribeRequest,
|
|
75
|
+
csrfToken?: string
|
|
62
76
|
): Promise<ApiResponse<{ message: string }>> {
|
|
63
77
|
return this.create<NewsletterUnsubscribeRequest, { message: string }>(
|
|
64
78
|
this.newsletterEndpoint(siteName, '/newsletter/unsubscribe'),
|
|
65
|
-
data
|
|
79
|
+
data,
|
|
80
|
+
csrfToken
|
|
66
81
|
);
|
|
67
82
|
}
|
|
68
83
|
|
|
@@ -81,15 +96,21 @@ export class NewsletterClient extends BaseClient {
|
|
|
81
96
|
|
|
82
97
|
/**
|
|
83
98
|
* Update subscription preferences
|
|
99
|
+
* @param siteName - The site name
|
|
100
|
+
* @param email - Subscriber email
|
|
101
|
+
* @param preferences - New preferences
|
|
102
|
+
* @param csrfToken - CSRF token (required for browser-based submissions)
|
|
84
103
|
*/
|
|
85
104
|
async updatePreferences(
|
|
86
105
|
siteName: string,
|
|
87
106
|
email: string,
|
|
88
|
-
preferences: NewsletterPreferences
|
|
107
|
+
preferences: NewsletterPreferences,
|
|
108
|
+
csrfToken?: string
|
|
89
109
|
): Promise<ApiResponse<{ message: string; preferences: NewsletterPreferences }>> {
|
|
90
110
|
return this.patch(
|
|
91
111
|
this.newsletterEndpoint(siteName, '/newsletter/preferences'),
|
|
92
|
-
{ email, ...preferences }
|
|
112
|
+
{ email, ...preferences },
|
|
113
|
+
csrfToken
|
|
93
114
|
);
|
|
94
115
|
}
|
|
95
116
|
|
package/src/types/index.ts
CHANGED
package/src/utils/http-client.ts
CHANGED
|
@@ -115,29 +115,29 @@ export class HttpClient {
|
|
|
115
115
|
/**
|
|
116
116
|
* POST request
|
|
117
117
|
*/
|
|
118
|
-
async post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
119
|
-
return this.request<T>(endpoint, { method: 'POST', body });
|
|
118
|
+
async post<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
119
|
+
return this.request<T>(endpoint, { method: 'POST', body, ...options });
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* PUT request
|
|
124
124
|
*/
|
|
125
|
-
async put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
126
|
-
return this.request<T>(endpoint, { method: 'PUT', body });
|
|
125
|
+
async put<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
126
|
+
return this.request<T>(endpoint, { method: 'PUT', body, ...options });
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
130
|
* DELETE request
|
|
131
131
|
*/
|
|
132
|
-
async delete<T = any>(endpoint: string): Promise<ApiResponse<T>> {
|
|
133
|
-
return this.request<T>(endpoint, { method: 'DELETE' });
|
|
132
|
+
async delete<T = any>(endpoint: string, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
133
|
+
return this.request<T>(endpoint, { method: 'DELETE', ...options });
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* PATCH request
|
|
138
138
|
*/
|
|
139
|
-
async patch<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>> {
|
|
140
|
-
return this.request<T>(endpoint, { method: 'PATCH', body });
|
|
139
|
+
async patch<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>> {
|
|
140
|
+
return this.request<T>(endpoint, { method: 'PATCH', body, ...options });
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
@@ -169,6 +169,11 @@ export class HttpClient {
|
|
|
169
169
|
...options.headers,
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
// Add CSRF token if provided
|
|
173
|
+
if (options.csrfToken) {
|
|
174
|
+
headers['X-CSRF-Token'] = options.csrfToken;
|
|
175
|
+
}
|
|
176
|
+
|
|
172
177
|
const requestOptions: RequestInit = {
|
|
173
178
|
method: options.method || 'GET',
|
|
174
179
|
headers,
|