perspectapi-ts-sdk 1.1.1 → 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/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
- return this.create(this.contactEndpoint(siteName, "/contact/submit"), data);
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
@@ -1217,6 +1226,201 @@ var ContactClient = class extends BaseClient {
1217
1226
  }
1218
1227
  };
1219
1228
 
1229
+ // src/client/newsletter-client.ts
1230
+ var NewsletterClient = class extends BaseClient {
1231
+ constructor(http) {
1232
+ super(http, "/api/v1");
1233
+ }
1234
+ /**
1235
+ * Build a newsletter endpoint scoped to a site (without /sites prefix)
1236
+ */
1237
+ newsletterEndpoint(siteName, endpoint) {
1238
+ return this.siteScopedEndpoint(siteName, endpoint, { includeSitesSegment: false });
1239
+ }
1240
+ /**
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)
1245
+ */
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
+ }
1250
+ return this.create(
1251
+ this.newsletterEndpoint(siteName, "/newsletter/subscribe"),
1252
+ data,
1253
+ csrfToken
1254
+ );
1255
+ }
1256
+ /**
1257
+ * Confirm newsletter subscription via token
1258
+ */
1259
+ async confirmSubscription(siteName, token) {
1260
+ return this.getSingle(
1261
+ this.newsletterEndpoint(siteName, `/newsletter/confirm/${encodeURIComponent(token)}`)
1262
+ );
1263
+ }
1264
+ /**
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)
1269
+ */
1270
+ async unsubscribe(siteName, data, csrfToken) {
1271
+ return this.create(
1272
+ this.newsletterEndpoint(siteName, "/newsletter/unsubscribe"),
1273
+ data,
1274
+ csrfToken
1275
+ );
1276
+ }
1277
+ /**
1278
+ * One-click unsubscribe via token (GET request)
1279
+ */
1280
+ async unsubscribeByToken(siteName, token) {
1281
+ return this.http.get(
1282
+ this.buildPath(this.newsletterEndpoint(siteName, `/newsletter/unsubscribe/${encodeURIComponent(token)}`))
1283
+ );
1284
+ }
1285
+ /**
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)
1291
+ */
1292
+ async updatePreferences(siteName, email, preferences, csrfToken) {
1293
+ return this.patch(
1294
+ this.newsletterEndpoint(siteName, "/newsletter/preferences"),
1295
+ { email, ...preferences },
1296
+ csrfToken
1297
+ );
1298
+ }
1299
+ /**
1300
+ * Get available newsletter lists
1301
+ */
1302
+ async getLists(siteName) {
1303
+ return this.getSingle(
1304
+ this.newsletterEndpoint(siteName, "/newsletter/lists")
1305
+ );
1306
+ }
1307
+ /**
1308
+ * Check subscription status by email
1309
+ */
1310
+ async getStatus(siteName, email) {
1311
+ return this.http.get(
1312
+ this.buildPath(this.newsletterEndpoint(siteName, "/newsletter/status")),
1313
+ { email }
1314
+ );
1315
+ }
1316
+ // Admin methods (require authentication)
1317
+ /**
1318
+ * Get all newsletter subscriptions (admin only)
1319
+ */
1320
+ async getSubscriptions(siteName, params) {
1321
+ return this.getPaginated(
1322
+ this.newsletterEndpoint(siteName, "/newsletter/subscriptions"),
1323
+ params
1324
+ );
1325
+ }
1326
+ /**
1327
+ * Get subscription by ID (admin only)
1328
+ */
1329
+ async getSubscriptionById(siteName, id) {
1330
+ return this.getSingle(
1331
+ this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`)
1332
+ );
1333
+ }
1334
+ /**
1335
+ * Update subscription status (admin only)
1336
+ */
1337
+ async updateSubscriptionStatus(siteName, id, status, notes) {
1338
+ return this.patch(
1339
+ this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`),
1340
+ { status, notes }
1341
+ );
1342
+ }
1343
+ /**
1344
+ * Delete subscription (admin only)
1345
+ */
1346
+ async deleteSubscription(siteName, id) {
1347
+ return this.delete(
1348
+ this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`)
1349
+ );
1350
+ }
1351
+ /**
1352
+ * Bulk update subscriptions (admin only)
1353
+ */
1354
+ async bulkUpdateSubscriptions(siteName, data) {
1355
+ return this.create(
1356
+ this.newsletterEndpoint(siteName, "/newsletter/subscriptions/bulk-update"),
1357
+ data
1358
+ );
1359
+ }
1360
+ /**
1361
+ * Create newsletter list (admin only)
1362
+ */
1363
+ async createList(siteName, data) {
1364
+ return this.create(
1365
+ this.newsletterEndpoint(siteName, "/newsletter/lists"),
1366
+ data
1367
+ );
1368
+ }
1369
+ /**
1370
+ * Update newsletter list (admin only)
1371
+ */
1372
+ async updateList(siteName, listId, data) {
1373
+ return this.update(
1374
+ this.newsletterEndpoint(siteName, `/newsletter/lists/${encodeURIComponent(listId)}`),
1375
+ data
1376
+ );
1377
+ }
1378
+ /**
1379
+ * Delete newsletter list (admin only)
1380
+ */
1381
+ async deleteList(siteName, listId) {
1382
+ return this.delete(
1383
+ this.newsletterEndpoint(siteName, `/newsletter/lists/${encodeURIComponent(listId)}`)
1384
+ );
1385
+ }
1386
+ /**
1387
+ * Get newsletter statistics (admin only)
1388
+ */
1389
+ async getStatistics(siteName, params) {
1390
+ return this.http.get(
1391
+ this.buildPath(this.newsletterEndpoint(siteName, "/newsletter/statistics")),
1392
+ params
1393
+ );
1394
+ }
1395
+ /**
1396
+ * Export newsletter subscriptions (admin only)
1397
+ */
1398
+ async exportSubscriptions(siteName, params) {
1399
+ return this.create(
1400
+ this.newsletterEndpoint(siteName, "/newsletter/export"),
1401
+ params || {}
1402
+ );
1403
+ }
1404
+ /**
1405
+ * Import newsletter subscriptions (admin only)
1406
+ */
1407
+ async importSubscriptions(siteName, data) {
1408
+ return this.create(
1409
+ this.newsletterEndpoint(siteName, "/newsletter/import"),
1410
+ data
1411
+ );
1412
+ }
1413
+ /**
1414
+ * Send test newsletter (admin only)
1415
+ */
1416
+ async sendTestNewsletter(siteName, data) {
1417
+ return this.create(
1418
+ this.newsletterEndpoint(siteName, "/newsletter/test"),
1419
+ data
1420
+ );
1421
+ }
1422
+ };
1423
+
1220
1424
  // src/perspect-api-client.ts
1221
1425
  var PerspectApiClient = class {
1222
1426
  http;
@@ -1231,6 +1435,7 @@ var PerspectApiClient = class {
1231
1435
  webhooks;
1232
1436
  checkout;
1233
1437
  contact;
1438
+ newsletter;
1234
1439
  constructor(config) {
1235
1440
  if (!config.baseUrl) {
1236
1441
  throw new Error("baseUrl is required in PerspectApiConfig");
@@ -1246,6 +1451,7 @@ var PerspectApiClient = class {
1246
1451
  this.webhooks = new WebhooksClient(this.http);
1247
1452
  this.checkout = new CheckoutClient(this.http);
1248
1453
  this.contact = new ContactClient(this.http);
1454
+ this.newsletter = new NewsletterClient(this.http);
1249
1455
  }
1250
1456
  /**
1251
1457
  * Update authentication token
@@ -1707,6 +1913,7 @@ export {
1707
1913
  ContactClient,
1708
1914
  ContentClient,
1709
1915
  HttpClient,
1916
+ NewsletterClient,
1710
1917
  OrganizationsClient,
1711
1918
  PerspectApiClient,
1712
1919
  ProductsClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perspectapi-ts-sdk",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -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(siteName: string, data: CreateContactRequest): Promise<ApiResponse<{
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
  /**