perspectapi-ts-sdk 7.2.6 → 7.4.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.js CHANGED
@@ -182,6 +182,8 @@ var HttpClient = class {
182
182
  }
183
183
  this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
184
184
  let lastError;
185
+ const method = (options.method || "GET").toUpperCase();
186
+ const isIdempotent = method === "GET" || method === "HEAD";
185
187
  for (let attempt = 0; attempt <= this.retries; attempt++) {
186
188
  try {
187
189
  const response = await this.fetchWithTimeout(url, requestOptions);
@@ -191,6 +193,9 @@ var HttpClient = class {
191
193
  if (error && typeof error === "object" && "status" in error && typeof error.status === "number" && error.status < 500) {
192
194
  throw error;
193
195
  }
196
+ if (!isIdempotent) {
197
+ throw error;
198
+ }
194
199
  if (attempt === this.retries) {
195
200
  break;
196
201
  }
@@ -1347,6 +1352,97 @@ var NewsletterV2Client = class extends BaseV2Client {
1347
1352
  data
1348
1353
  );
1349
1354
  }
1355
+ // --- Lists (get by id) ---
1356
+ async getListById(siteName, id, cachePolicy) {
1357
+ return this.getOne(
1358
+ this.sitePath(siteName, "newsletter", `lists/${encodeURIComponent(id)}`),
1359
+ void 0,
1360
+ cachePolicy
1361
+ );
1362
+ }
1363
+ // --- Subscriptions (admin: lookup / status / delete / bulk) ---
1364
+ async getSubscriptionByEmail(siteName, email, cachePolicy) {
1365
+ return this.getOne(
1366
+ this.sitePath(siteName, "newsletter", "subscriptions/by-email"),
1367
+ { email },
1368
+ cachePolicy
1369
+ );
1370
+ }
1371
+ async updateSubscriptionStatus(siteName, id, data) {
1372
+ return this.post(
1373
+ this.sitePath(siteName, "newsletter", `subscriptions/${encodeURIComponent(id)}/status`),
1374
+ data
1375
+ );
1376
+ }
1377
+ async deleteSubscription(siteName, id) {
1378
+ return this.deleteOne(
1379
+ this.sitePath(siteName, "newsletter", `subscriptions/${encodeURIComponent(id)}`)
1380
+ );
1381
+ }
1382
+ async bulkUpdateSubscriptions(siteName, data) {
1383
+ return this.post(
1384
+ this.sitePath(siteName, "newsletter", "subscriptions/bulk"),
1385
+ data
1386
+ );
1387
+ }
1388
+ // --- Statistics ---
1389
+ async getStatistics(siteName, params, cachePolicy) {
1390
+ return this.getOne(
1391
+ this.sitePath(siteName, "newsletter", "statistics"),
1392
+ params,
1393
+ cachePolicy
1394
+ );
1395
+ }
1396
+ // --- Campaigns (create / update / delete) ---
1397
+ async createCampaign(siteName, data) {
1398
+ return this.post(
1399
+ this.sitePath(siteName, "newsletter", "campaigns"),
1400
+ data
1401
+ );
1402
+ }
1403
+ async updateCampaign(siteName, id, data) {
1404
+ return this.patchOne(
1405
+ this.sitePath(siteName, "newsletter", `campaigns/${encodeURIComponent(id)}`),
1406
+ data
1407
+ );
1408
+ }
1409
+ async deleteCampaign(siteName, id) {
1410
+ return this.deleteOne(
1411
+ this.sitePath(siteName, "newsletter", `campaigns/${encodeURIComponent(id)}`)
1412
+ );
1413
+ }
1414
+ // --- Series ---
1415
+ async listSeries(siteName, params, cachePolicy) {
1416
+ return this.getList(
1417
+ this.sitePath(siteName, "newsletter", "series"),
1418
+ params,
1419
+ cachePolicy
1420
+ );
1421
+ }
1422
+ async getSeries(siteName, id, cachePolicy) {
1423
+ return this.getOne(
1424
+ this.sitePath(siteName, "newsletter", `series/${encodeURIComponent(id)}`),
1425
+ void 0,
1426
+ cachePolicy
1427
+ );
1428
+ }
1429
+ async createSeries(siteName, data) {
1430
+ return this.post(
1431
+ this.sitePath(siteName, "newsletter", "series"),
1432
+ data
1433
+ );
1434
+ }
1435
+ async updateSeries(siteName, id, data) {
1436
+ return this.patchOne(
1437
+ this.sitePath(siteName, "newsletter", `series/${encodeURIComponent(id)}`),
1438
+ data
1439
+ );
1440
+ }
1441
+ async deleteSeries(siteName, id) {
1442
+ return this.deleteOne(
1443
+ this.sitePath(siteName, "newsletter", `series/${encodeURIComponent(id)}`)
1444
+ );
1445
+ }
1350
1446
  };
1351
1447
 
1352
1448
  // src/v2/client/contacts-client.ts
@@ -1535,11 +1631,18 @@ var CreditsV2Client = class extends BaseV2Client {
1535
1631
  // src/v2/client/email-client.ts
1536
1632
  var EmailV2Client = class extends BaseV2Client {
1537
1633
  /**
1538
- * Send a transactional email using the site's configured email provider.
1634
+ * Send a transactional email through the site's email provider.
1635
+ *
1636
+ * Requires a server-side API key — never call this from a browser. Sites
1637
+ * without a configured provider fall back to the platform default with
1638
+ * policy restrictions (owner-only recipients, daily quota).
1539
1639
  *
1540
- * Requires a server-side API key never call this from a browser.
1541
- * The site must have an email provider configured in its settings.
1542
- * At least one of `html` or `text` must be provided.
1640
+ * Provide content one of two ways (mutually exclusive):
1641
+ * - Raw: `subject` plus at least one of `html` / `text`.
1642
+ * - Template: `template_type` (published template for that type) or
1643
+ * `template_id` (published template by ID) plus optional `variables`; the
1644
+ * template is rendered server-side. `subject` is optional here and
1645
+ * overrides the rendered one.
1543
1646
  */
1544
1647
  async send(siteName, params) {
1545
1648
  return this.post(
@@ -1549,6 +1652,66 @@ var EmailV2Client = class extends BaseV2Client {
1549
1652
  }
1550
1653
  };
1551
1654
 
1655
+ // src/v2/client/email-templates-client.ts
1656
+ var EmailTemplatesV2Client = class extends BaseV2Client {
1657
+ path(siteName, suffix = "") {
1658
+ return this.sitePath(siteName, "email", suffix ? `templates/${suffix}` : "templates");
1659
+ }
1660
+ /** List every supported template type with its allowed variables. */
1661
+ async listTypes(siteName, cachePolicy) {
1662
+ return this.getList(
1663
+ this.path(siteName, "types"),
1664
+ void 0,
1665
+ cachePolicy
1666
+ );
1667
+ }
1668
+ /** Get the allowed variables for a single template type. */
1669
+ async getTypeVariables(siteName, templateType, cachePolicy) {
1670
+ return this.getOne(
1671
+ this.path(siteName, `types/${templateType}`),
1672
+ void 0,
1673
+ cachePolicy
1674
+ );
1675
+ }
1676
+ /** List template versions, optionally filtered by type and/or status. */
1677
+ async list(siteName, params, cachePolicy) {
1678
+ return this.getList(this.path(siteName), params, cachePolicy);
1679
+ }
1680
+ /** Get a specific template version by ID (`etpl_…`). */
1681
+ async get(siteName, templateId, cachePolicy) {
1682
+ return this.getOne(
1683
+ this.path(siteName, encodeURIComponent(templateId)),
1684
+ void 0,
1685
+ cachePolicy
1686
+ );
1687
+ }
1688
+ /** Get the currently published version for a template type. */
1689
+ async getPublished(siteName, templateType, cachePolicy) {
1690
+ return this.getOne(
1691
+ this.path(siteName, `published/${templateType}`),
1692
+ void 0,
1693
+ cachePolicy
1694
+ );
1695
+ }
1696
+ /** Create a new template version. */
1697
+ async create(siteName, params) {
1698
+ return this.post(this.path(siteName), params);
1699
+ }
1700
+ /** Clone a template version, optionally overriding fields. */
1701
+ async clone(siteName, templateId, params) {
1702
+ return this.post(
1703
+ this.path(siteName, `${encodeURIComponent(templateId)}/clone`),
1704
+ params ?? {}
1705
+ );
1706
+ }
1707
+ /** Publish a template version (archives the previously published one). */
1708
+ async publish(siteName, templateId) {
1709
+ return this.post(
1710
+ this.path(siteName, `${encodeURIComponent(templateId)}/publish`)
1711
+ );
1712
+ }
1713
+ };
1714
+
1552
1715
  // src/v2/index.ts
1553
1716
  var PerspectApiV2Client = class {
1554
1717
  http;
@@ -1568,6 +1731,7 @@ var PerspectApiV2Client = class {
1568
1731
  subscriptions;
1569
1732
  credits;
1570
1733
  email;
1734
+ emailTemplates;
1571
1735
  constructor(config) {
1572
1736
  const baseUrl = config.baseUrl.replace(/\/+$/, "");
1573
1737
  const v2BaseUrl = baseUrl.endsWith("/api/v2") ? baseUrl : `${baseUrl}/api/v2`;
@@ -1590,6 +1754,7 @@ var PerspectApiV2Client = class {
1590
1754
  this.subscriptions = new SubscriptionsV2Client(this.http, basePath, cache);
1591
1755
  this.credits = new CreditsV2Client(this.http, basePath, cache);
1592
1756
  this.email = new EmailV2Client(this.http, basePath, cache);
1757
+ this.emailTemplates = new EmailTemplatesV2Client(this.http, basePath, cache);
1593
1758
  }
1594
1759
  /** Update the JWT token for authenticated requests. */
1595
1760
  setAuth(jwt) {
@@ -4478,7 +4643,7 @@ function buildAbClient(apiKey, siteName, baseUrl, ctx, config, visitorId, isBot)
4478
4643
  return { variant: flag.defaultVariant, enrolled: false, config: null };
4479
4644
  }
4480
4645
  const variantCfg = flag.variants.find((v) => v.key === assigned)?.config ?? null;
4481
- emit(apiKey, baseUrl, ctx, {
4646
+ await emit(apiKey, baseUrl, ctx, {
4482
4647
  site: siteName,
4483
4648
  kind: "exposure",
4484
4649
  flag: flagKey,
@@ -4499,7 +4664,7 @@ function buildAbClient(apiKey, siteName, baseUrl, ctx, config, visitorId, isBot)
4499
4664
  const bucket = bucketFor(visitorId, flagKey, flag.currentVersion);
4500
4665
  const assigned = variantForBucket(bucket, flag.variants, flag.trafficAllocationBp);
4501
4666
  if (assigned === null) continue;
4502
- emit(apiKey, baseUrl, ctx, {
4667
+ await emit(apiKey, baseUrl, ctx, {
4503
4668
  site: siteName,
4504
4669
  kind: "conversion",
4505
4670
  flag: flagKey,
@@ -4543,7 +4708,11 @@ function emit(apiKey, baseUrl, ctx, body) {
4543
4708
  headers: { "content-type": "application/json", "x-api-key": apiKey },
4544
4709
  body: JSON.stringify(body)
4545
4710
  }).then(() => void 0).catch(() => void 0);
4546
- if (ctx) ctx.waitUntil(task);
4711
+ if (ctx) {
4712
+ ctx.waitUntil(task);
4713
+ return Promise.resolve();
4714
+ }
4715
+ return task;
4547
4716
  }
4548
4717
  function classifyBotFromCfRequest(request) {
4549
4718
  const cf = request.cf;
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  PerspectV2Error,
8
8
  createApiError,
9
9
  createPerspectApiV2Client
10
- } from "./chunk-WD62IBI5.mjs";
10
+ } from "./chunk-PWZSSX4V.mjs";
11
11
 
12
12
  // src/deprecation.ts
13
13
  var V1_SUNSET_DATE = "2026-06-01";
@@ -2889,7 +2889,7 @@ function buildAbClient(apiKey, siteName, baseUrl, ctx, config, visitorId, isBot)
2889
2889
  return { variant: flag.defaultVariant, enrolled: false, config: null };
2890
2890
  }
2891
2891
  const variantCfg = flag.variants.find((v) => v.key === assigned)?.config ?? null;
2892
- emit(apiKey, baseUrl, ctx, {
2892
+ await emit(apiKey, baseUrl, ctx, {
2893
2893
  site: siteName,
2894
2894
  kind: "exposure",
2895
2895
  flag: flagKey,
@@ -2910,7 +2910,7 @@ function buildAbClient(apiKey, siteName, baseUrl, ctx, config, visitorId, isBot)
2910
2910
  const bucket = bucketFor(visitorId, flagKey, flag.currentVersion);
2911
2911
  const assigned = variantForBucket(bucket, flag.variants, flag.trafficAllocationBp);
2912
2912
  if (assigned === null) continue;
2913
- emit(apiKey, baseUrl, ctx, {
2913
+ await emit(apiKey, baseUrl, ctx, {
2914
2914
  site: siteName,
2915
2915
  kind: "conversion",
2916
2916
  flag: flagKey,
@@ -2954,7 +2954,11 @@ function emit(apiKey, baseUrl, ctx, body) {
2954
2954
  headers: { "content-type": "application/json", "x-api-key": apiKey },
2955
2955
  body: JSON.stringify(body)
2956
2956
  }).then(() => void 0).catch(() => void 0);
2957
- if (ctx) ctx.waitUntil(task);
2957
+ if (ctx) {
2958
+ ctx.waitUntil(task);
2959
+ return Promise.resolve();
2960
+ }
2961
+ return task;
2958
2962
  }
2959
2963
  function classifyBotFromCfRequest(request) {
2960
2964
  const cf = request.cf;
@@ -1 +1 @@
1
- export { cq as ApiKeysV2Client, cf as BaseV2Client, ci as CategoriesV2Client, cj as CollectionsV2Client, cn as ContactsV2Client, cg as ContentV2Client, ct as CreditsV2Client, cu as EmailV2Client, cm as NewsletterV2Client, ck as OrdersV2Client, co as OrganizationsV2Client, ay as PerspectApiV2Client, ce as PerspectApiV2Config, aA as PerspectV2Error, ch as ProductsV2Client, cl as SiteUsersV2Client, cp as SitesV2Client, cs as SubscriptionsV2Client, bH as V2ApiKey, bS as V2CancelSubscriptionResult, b4 as V2Category, b5 as V2CategoryCreateParams, b6 as V2CategoryUpdateParams, b7 as V2Collection, b9 as V2CollectionCreateParams, b8 as V2CollectionItem, ba as V2CollectionUpdateParams, bE as V2ContactSubmission, aY as V2Content, aZ as V2ContentCreateParams, a$ as V2ContentListParams, a_ as V2ContentUpdateParams, bU as V2CreditBalance, bT as V2CreditTransaction, aT as V2Deleted, bX as V2EmailSendParams, bY as V2EmailSendResult, aU as V2Error, aV as V2ErrorType, bV as V2GrantCreditParams, bW as V2GrantCreditResult, aS as V2List, aX as V2Media, bv as V2NewsletterCampaign, bC as V2NewsletterImportRequest, bD as V2NewsletterImportResult, bu as V2NewsletterList, bx as V2NewsletterListCreateParams, by as V2NewsletterListUpdateParams, bt as V2NewsletterSubscription, bB as V2NewsletterSubscriptionListMembershipUpdate, bz as V2NewsletterSyncInput, bA as V2NewsletterSyncResult, bw as V2NewsletterTrackingResponse, aR as V2Object, bb as V2Order, bf as V2OrderAddress, bh as V2OrderCreateParams, bm as V2OrderCreateResult, bj as V2OrderFulfillmentNotificationLineItem, bk as V2OrderFulfillmentNotificationParams, bl as V2OrderFulfillmentNotificationResult, bi as V2OrderFulfillmentUpdate, be as V2OrderLineItem, bd as V2OrderLineItemPriceData, bc as V2OrderListParams, bg as V2OrderTaxRequest, bF as V2Organization, aW as V2PaginationParams, b0 as V2Product, b1 as V2ProductCreateParams, b3 as V2ProductListParams, b2 as V2ProductUpdateParams, bG as V2Site, bn as V2SiteUser, bq as V2SiteUserListParams, bp as V2SiteUserMeUpdateParams, bs as V2SiteUserProfile, bM as V2SiteUserSubscription, bo as V2SiteUserUpdateParams, br as V2SiteUserWithProfile, bO as V2SubscriptionCancelParams, bP as V2SubscriptionChangePlanParams, bQ as V2SubscriptionChargeParams, bR as V2SubscriptionChargeResult, bN as V2SubscriptionPauseParams, bJ as V2Webhook, bK as V2WebhookCreateParams, bI as V2WebhookEventType, bL as V2WebhookUpdateParams, cr as WebhooksV2Client, az as createPerspectApiV2Client } from '../index-BbTVcEl5.mjs';
1
+ export { cI as ApiKeysV2Client, cx as BaseV2Client, cA as CategoriesV2Client, cB as CollectionsV2Client, cF as ContactsV2Client, cy as ContentV2Client, cL as CreditsV2Client, cN as EmailTemplatesV2Client, cM as EmailV2Client, cE as NewsletterV2Client, cC as OrdersV2Client, cG as OrganizationsV2Client, ay as PerspectApiV2Client, cw as PerspectApiV2Config, aA as PerspectV2Error, cz as ProductsV2Client, cD as SiteUsersV2Client, cH as SitesV2Client, cK as SubscriptionsV2Client, bS as V2ApiKey, c1 as V2CancelSubscriptionResult, b4 as V2Category, b5 as V2CategoryCreateParams, b6 as V2CategoryUpdateParams, b7 as V2Collection, b9 as V2CollectionCreateParams, b8 as V2CollectionItem, ba as V2CollectionUpdateParams, bP as V2ContactSubmission, aY as V2Content, aZ as V2ContentCreateParams, a$ as V2ContentListParams, a_ as V2ContentUpdateParams, c3 as V2CreditBalance, c2 as V2CreditTransaction, aT as V2Deleted, c8 as V2EmailSendParams, ce as V2EmailSendResult, c9 as V2EmailTemplate, cd as V2EmailTemplateCloneParams, cc as V2EmailTemplateCreateParams, cb as V2EmailTemplateListParams, c7 as V2EmailTemplateStatus, c6 as V2EmailTemplateType, ca as V2EmailTemplateType_Listing, aU as V2Error, aV as V2ErrorType, c4 as V2GrantCreditParams, c5 as V2GrantCreditResult, aS as V2List, aX as V2Media, bN as V2NewsletterBulkParams, bO as V2NewsletterBulkResult, bw as V2NewsletterCampaign, bH as V2NewsletterCampaignCreateParams, bv as V2NewsletterCampaignStatus, bI as V2NewsletterCampaignUpdateParams, bF as V2NewsletterImportRequest, bG as V2NewsletterImportResult, bu as V2NewsletterList, bA as V2NewsletterListCreateParams, bB as V2NewsletterListUpdateParams, bx as V2NewsletterSeries, bJ as V2NewsletterSeriesCreateParams, bK as V2NewsletterSeriesUpdateParams, by as V2NewsletterStatistics, bL as V2NewsletterStatisticsParams, bt as V2NewsletterSubscription, bE as V2NewsletterSubscriptionListMembershipUpdate, bM as V2NewsletterSubscriptionStatusUpdate, bC as V2NewsletterSyncInput, bD as V2NewsletterSyncResult, bz as V2NewsletterTrackingResponse, aR as V2Object, bb as V2Order, bf as V2OrderAddress, bh as V2OrderCreateParams, bm as V2OrderCreateResult, bj as V2OrderFulfillmentNotificationLineItem, bk as V2OrderFulfillmentNotificationParams, bl as V2OrderFulfillmentNotificationResult, bi as V2OrderFulfillmentUpdate, be as V2OrderLineItem, bd as V2OrderLineItemPriceData, bc as V2OrderListParams, bg as V2OrderTaxRequest, bQ as V2Organization, aW as V2PaginationParams, b0 as V2Product, b1 as V2ProductCreateParams, b3 as V2ProductListParams, b2 as V2ProductUpdateParams, bR as V2Site, bn as V2SiteUser, bq as V2SiteUserListParams, bp as V2SiteUserMeUpdateParams, bs as V2SiteUserProfile, bX as V2SiteUserSubscription, bo as V2SiteUserUpdateParams, br as V2SiteUserWithProfile, bZ as V2SubscriptionCancelParams, b_ as V2SubscriptionChangePlanParams, b$ as V2SubscriptionChargeParams, c0 as V2SubscriptionChargeResult, bY as V2SubscriptionPauseParams, bU as V2Webhook, bV as V2WebhookCreateParams, bT as V2WebhookEventType, bW as V2WebhookUpdateParams, cJ as WebhooksV2Client, az as createPerspectApiV2Client } from '../index-CmcHYKO8.mjs';
@@ -1 +1 @@
1
- export { cq as ApiKeysV2Client, cf as BaseV2Client, ci as CategoriesV2Client, cj as CollectionsV2Client, cn as ContactsV2Client, cg as ContentV2Client, ct as CreditsV2Client, cu as EmailV2Client, cm as NewsletterV2Client, ck as OrdersV2Client, co as OrganizationsV2Client, ay as PerspectApiV2Client, ce as PerspectApiV2Config, aA as PerspectV2Error, ch as ProductsV2Client, cl as SiteUsersV2Client, cp as SitesV2Client, cs as SubscriptionsV2Client, bH as V2ApiKey, bS as V2CancelSubscriptionResult, b4 as V2Category, b5 as V2CategoryCreateParams, b6 as V2CategoryUpdateParams, b7 as V2Collection, b9 as V2CollectionCreateParams, b8 as V2CollectionItem, ba as V2CollectionUpdateParams, bE as V2ContactSubmission, aY as V2Content, aZ as V2ContentCreateParams, a$ as V2ContentListParams, a_ as V2ContentUpdateParams, bU as V2CreditBalance, bT as V2CreditTransaction, aT as V2Deleted, bX as V2EmailSendParams, bY as V2EmailSendResult, aU as V2Error, aV as V2ErrorType, bV as V2GrantCreditParams, bW as V2GrantCreditResult, aS as V2List, aX as V2Media, bv as V2NewsletterCampaign, bC as V2NewsletterImportRequest, bD as V2NewsletterImportResult, bu as V2NewsletterList, bx as V2NewsletterListCreateParams, by as V2NewsletterListUpdateParams, bt as V2NewsletterSubscription, bB as V2NewsletterSubscriptionListMembershipUpdate, bz as V2NewsletterSyncInput, bA as V2NewsletterSyncResult, bw as V2NewsletterTrackingResponse, aR as V2Object, bb as V2Order, bf as V2OrderAddress, bh as V2OrderCreateParams, bm as V2OrderCreateResult, bj as V2OrderFulfillmentNotificationLineItem, bk as V2OrderFulfillmentNotificationParams, bl as V2OrderFulfillmentNotificationResult, bi as V2OrderFulfillmentUpdate, be as V2OrderLineItem, bd as V2OrderLineItemPriceData, bc as V2OrderListParams, bg as V2OrderTaxRequest, bF as V2Organization, aW as V2PaginationParams, b0 as V2Product, b1 as V2ProductCreateParams, b3 as V2ProductListParams, b2 as V2ProductUpdateParams, bG as V2Site, bn as V2SiteUser, bq as V2SiteUserListParams, bp as V2SiteUserMeUpdateParams, bs as V2SiteUserProfile, bM as V2SiteUserSubscription, bo as V2SiteUserUpdateParams, br as V2SiteUserWithProfile, bO as V2SubscriptionCancelParams, bP as V2SubscriptionChangePlanParams, bQ as V2SubscriptionChargeParams, bR as V2SubscriptionChargeResult, bN as V2SubscriptionPauseParams, bJ as V2Webhook, bK as V2WebhookCreateParams, bI as V2WebhookEventType, bL as V2WebhookUpdateParams, cr as WebhooksV2Client, az as createPerspectApiV2Client } from '../index-BbTVcEl5.js';
1
+ export { cI as ApiKeysV2Client, cx as BaseV2Client, cA as CategoriesV2Client, cB as CollectionsV2Client, cF as ContactsV2Client, cy as ContentV2Client, cL as CreditsV2Client, cN as EmailTemplatesV2Client, cM as EmailV2Client, cE as NewsletterV2Client, cC as OrdersV2Client, cG as OrganizationsV2Client, ay as PerspectApiV2Client, cw as PerspectApiV2Config, aA as PerspectV2Error, cz as ProductsV2Client, cD as SiteUsersV2Client, cH as SitesV2Client, cK as SubscriptionsV2Client, bS as V2ApiKey, c1 as V2CancelSubscriptionResult, b4 as V2Category, b5 as V2CategoryCreateParams, b6 as V2CategoryUpdateParams, b7 as V2Collection, b9 as V2CollectionCreateParams, b8 as V2CollectionItem, ba as V2CollectionUpdateParams, bP as V2ContactSubmission, aY as V2Content, aZ as V2ContentCreateParams, a$ as V2ContentListParams, a_ as V2ContentUpdateParams, c3 as V2CreditBalance, c2 as V2CreditTransaction, aT as V2Deleted, c8 as V2EmailSendParams, ce as V2EmailSendResult, c9 as V2EmailTemplate, cd as V2EmailTemplateCloneParams, cc as V2EmailTemplateCreateParams, cb as V2EmailTemplateListParams, c7 as V2EmailTemplateStatus, c6 as V2EmailTemplateType, ca as V2EmailTemplateType_Listing, aU as V2Error, aV as V2ErrorType, c4 as V2GrantCreditParams, c5 as V2GrantCreditResult, aS as V2List, aX as V2Media, bN as V2NewsletterBulkParams, bO as V2NewsletterBulkResult, bw as V2NewsletterCampaign, bH as V2NewsletterCampaignCreateParams, bv as V2NewsletterCampaignStatus, bI as V2NewsletterCampaignUpdateParams, bF as V2NewsletterImportRequest, bG as V2NewsletterImportResult, bu as V2NewsletterList, bA as V2NewsletterListCreateParams, bB as V2NewsletterListUpdateParams, bx as V2NewsletterSeries, bJ as V2NewsletterSeriesCreateParams, bK as V2NewsletterSeriesUpdateParams, by as V2NewsletterStatistics, bL as V2NewsletterStatisticsParams, bt as V2NewsletterSubscription, bE as V2NewsletterSubscriptionListMembershipUpdate, bM as V2NewsletterSubscriptionStatusUpdate, bC as V2NewsletterSyncInput, bD as V2NewsletterSyncResult, bz as V2NewsletterTrackingResponse, aR as V2Object, bb as V2Order, bf as V2OrderAddress, bh as V2OrderCreateParams, bm as V2OrderCreateResult, bj as V2OrderFulfillmentNotificationLineItem, bk as V2OrderFulfillmentNotificationParams, bl as V2OrderFulfillmentNotificationResult, bi as V2OrderFulfillmentUpdate, be as V2OrderLineItem, bd as V2OrderLineItemPriceData, bc as V2OrderListParams, bg as V2OrderTaxRequest, bQ as V2Organization, aW as V2PaginationParams, b0 as V2Product, b1 as V2ProductCreateParams, b3 as V2ProductListParams, b2 as V2ProductUpdateParams, bR as V2Site, bn as V2SiteUser, bq as V2SiteUserListParams, bp as V2SiteUserMeUpdateParams, bs as V2SiteUserProfile, bX as V2SiteUserSubscription, bo as V2SiteUserUpdateParams, br as V2SiteUserWithProfile, bZ as V2SubscriptionCancelParams, b_ as V2SubscriptionChangePlanParams, b$ as V2SubscriptionChargeParams, c0 as V2SubscriptionChargeResult, bY as V2SubscriptionPauseParams, bU as V2Webhook, bV as V2WebhookCreateParams, bT as V2WebhookEventType, bW as V2WebhookUpdateParams, cJ as WebhooksV2Client, az as createPerspectApiV2Client } from '../index-CmcHYKO8.js';
package/dist/v2/index.js CHANGED
@@ -27,6 +27,7 @@ __export(v2_exports, {
27
27
  ContactsV2Client: () => ContactsV2Client,
28
28
  ContentV2Client: () => ContentV2Client,
29
29
  CreditsV2Client: () => CreditsV2Client,
30
+ EmailTemplatesV2Client: () => EmailTemplatesV2Client,
30
31
  EmailV2Client: () => EmailV2Client,
31
32
  NewsletterV2Client: () => NewsletterV2Client,
32
33
  OrdersV2Client: () => OrdersV2Client,
@@ -140,6 +141,8 @@ var HttpClient = class {
140
141
  }
141
142
  this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
142
143
  let lastError;
144
+ const method = (options.method || "GET").toUpperCase();
145
+ const isIdempotent = method === "GET" || method === "HEAD";
143
146
  for (let attempt = 0; attempt <= this.retries; attempt++) {
144
147
  try {
145
148
  const response = await this.fetchWithTimeout(url, requestOptions);
@@ -149,6 +152,9 @@ var HttpClient = class {
149
152
  if (error && typeof error === "object" && "status" in error && typeof error.status === "number" && error.status < 500) {
150
153
  throw error;
151
154
  }
155
+ if (!isIdempotent) {
156
+ throw error;
157
+ }
152
158
  if (attempt === this.retries) {
153
159
  break;
154
160
  }
@@ -1293,6 +1299,97 @@ var NewsletterV2Client = class extends BaseV2Client {
1293
1299
  data
1294
1300
  );
1295
1301
  }
1302
+ // --- Lists (get by id) ---
1303
+ async getListById(siteName, id, cachePolicy) {
1304
+ return this.getOne(
1305
+ this.sitePath(siteName, "newsletter", `lists/${encodeURIComponent(id)}`),
1306
+ void 0,
1307
+ cachePolicy
1308
+ );
1309
+ }
1310
+ // --- Subscriptions (admin: lookup / status / delete / bulk) ---
1311
+ async getSubscriptionByEmail(siteName, email, cachePolicy) {
1312
+ return this.getOne(
1313
+ this.sitePath(siteName, "newsletter", "subscriptions/by-email"),
1314
+ { email },
1315
+ cachePolicy
1316
+ );
1317
+ }
1318
+ async updateSubscriptionStatus(siteName, id, data) {
1319
+ return this.post(
1320
+ this.sitePath(siteName, "newsletter", `subscriptions/${encodeURIComponent(id)}/status`),
1321
+ data
1322
+ );
1323
+ }
1324
+ async deleteSubscription(siteName, id) {
1325
+ return this.deleteOne(
1326
+ this.sitePath(siteName, "newsletter", `subscriptions/${encodeURIComponent(id)}`)
1327
+ );
1328
+ }
1329
+ async bulkUpdateSubscriptions(siteName, data) {
1330
+ return this.post(
1331
+ this.sitePath(siteName, "newsletter", "subscriptions/bulk"),
1332
+ data
1333
+ );
1334
+ }
1335
+ // --- Statistics ---
1336
+ async getStatistics(siteName, params, cachePolicy) {
1337
+ return this.getOne(
1338
+ this.sitePath(siteName, "newsletter", "statistics"),
1339
+ params,
1340
+ cachePolicy
1341
+ );
1342
+ }
1343
+ // --- Campaigns (create / update / delete) ---
1344
+ async createCampaign(siteName, data) {
1345
+ return this.post(
1346
+ this.sitePath(siteName, "newsletter", "campaigns"),
1347
+ data
1348
+ );
1349
+ }
1350
+ async updateCampaign(siteName, id, data) {
1351
+ return this.patchOne(
1352
+ this.sitePath(siteName, "newsletter", `campaigns/${encodeURIComponent(id)}`),
1353
+ data
1354
+ );
1355
+ }
1356
+ async deleteCampaign(siteName, id) {
1357
+ return this.deleteOne(
1358
+ this.sitePath(siteName, "newsletter", `campaigns/${encodeURIComponent(id)}`)
1359
+ );
1360
+ }
1361
+ // --- Series ---
1362
+ async listSeries(siteName, params, cachePolicy) {
1363
+ return this.getList(
1364
+ this.sitePath(siteName, "newsletter", "series"),
1365
+ params,
1366
+ cachePolicy
1367
+ );
1368
+ }
1369
+ async getSeries(siteName, id, cachePolicy) {
1370
+ return this.getOne(
1371
+ this.sitePath(siteName, "newsletter", `series/${encodeURIComponent(id)}`),
1372
+ void 0,
1373
+ cachePolicy
1374
+ );
1375
+ }
1376
+ async createSeries(siteName, data) {
1377
+ return this.post(
1378
+ this.sitePath(siteName, "newsletter", "series"),
1379
+ data
1380
+ );
1381
+ }
1382
+ async updateSeries(siteName, id, data) {
1383
+ return this.patchOne(
1384
+ this.sitePath(siteName, "newsletter", `series/${encodeURIComponent(id)}`),
1385
+ data
1386
+ );
1387
+ }
1388
+ async deleteSeries(siteName, id) {
1389
+ return this.deleteOne(
1390
+ this.sitePath(siteName, "newsletter", `series/${encodeURIComponent(id)}`)
1391
+ );
1392
+ }
1296
1393
  };
1297
1394
 
1298
1395
  // src/v2/client/contacts-client.ts
@@ -1481,11 +1578,18 @@ var CreditsV2Client = class extends BaseV2Client {
1481
1578
  // src/v2/client/email-client.ts
1482
1579
  var EmailV2Client = class extends BaseV2Client {
1483
1580
  /**
1484
- * Send a transactional email using the site's configured email provider.
1581
+ * Send a transactional email through the site's email provider.
1582
+ *
1583
+ * Requires a server-side API key — never call this from a browser. Sites
1584
+ * without a configured provider fall back to the platform default with
1585
+ * policy restrictions (owner-only recipients, daily quota).
1485
1586
  *
1486
- * Requires a server-side API key never call this from a browser.
1487
- * The site must have an email provider configured in its settings.
1488
- * At least one of `html` or `text` must be provided.
1587
+ * Provide content one of two ways (mutually exclusive):
1588
+ * - Raw: `subject` plus at least one of `html` / `text`.
1589
+ * - Template: `template_type` (published template for that type) or
1590
+ * `template_id` (published template by ID) plus optional `variables`; the
1591
+ * template is rendered server-side. `subject` is optional here and
1592
+ * overrides the rendered one.
1489
1593
  */
1490
1594
  async send(siteName, params) {
1491
1595
  return this.post(
@@ -1495,6 +1599,66 @@ var EmailV2Client = class extends BaseV2Client {
1495
1599
  }
1496
1600
  };
1497
1601
 
1602
+ // src/v2/client/email-templates-client.ts
1603
+ var EmailTemplatesV2Client = class extends BaseV2Client {
1604
+ path(siteName, suffix = "") {
1605
+ return this.sitePath(siteName, "email", suffix ? `templates/${suffix}` : "templates");
1606
+ }
1607
+ /** List every supported template type with its allowed variables. */
1608
+ async listTypes(siteName, cachePolicy) {
1609
+ return this.getList(
1610
+ this.path(siteName, "types"),
1611
+ void 0,
1612
+ cachePolicy
1613
+ );
1614
+ }
1615
+ /** Get the allowed variables for a single template type. */
1616
+ async getTypeVariables(siteName, templateType, cachePolicy) {
1617
+ return this.getOne(
1618
+ this.path(siteName, `types/${templateType}`),
1619
+ void 0,
1620
+ cachePolicy
1621
+ );
1622
+ }
1623
+ /** List template versions, optionally filtered by type and/or status. */
1624
+ async list(siteName, params, cachePolicy) {
1625
+ return this.getList(this.path(siteName), params, cachePolicy);
1626
+ }
1627
+ /** Get a specific template version by ID (`etpl_…`). */
1628
+ async get(siteName, templateId, cachePolicy) {
1629
+ return this.getOne(
1630
+ this.path(siteName, encodeURIComponent(templateId)),
1631
+ void 0,
1632
+ cachePolicy
1633
+ );
1634
+ }
1635
+ /** Get the currently published version for a template type. */
1636
+ async getPublished(siteName, templateType, cachePolicy) {
1637
+ return this.getOne(
1638
+ this.path(siteName, `published/${templateType}`),
1639
+ void 0,
1640
+ cachePolicy
1641
+ );
1642
+ }
1643
+ /** Create a new template version. */
1644
+ async create(siteName, params) {
1645
+ return this.post(this.path(siteName), params);
1646
+ }
1647
+ /** Clone a template version, optionally overriding fields. */
1648
+ async clone(siteName, templateId, params) {
1649
+ return this.post(
1650
+ this.path(siteName, `${encodeURIComponent(templateId)}/clone`),
1651
+ params ?? {}
1652
+ );
1653
+ }
1654
+ /** Publish a template version (archives the previously published one). */
1655
+ async publish(siteName, templateId) {
1656
+ return this.post(
1657
+ this.path(siteName, `${encodeURIComponent(templateId)}/publish`)
1658
+ );
1659
+ }
1660
+ };
1661
+
1498
1662
  // src/v2/index.ts
1499
1663
  var PerspectApiV2Client = class {
1500
1664
  http;
@@ -1514,6 +1678,7 @@ var PerspectApiV2Client = class {
1514
1678
  subscriptions;
1515
1679
  credits;
1516
1680
  email;
1681
+ emailTemplates;
1517
1682
  constructor(config) {
1518
1683
  const baseUrl = config.baseUrl.replace(/\/+$/, "");
1519
1684
  const v2BaseUrl = baseUrl.endsWith("/api/v2") ? baseUrl : `${baseUrl}/api/v2`;
@@ -1536,6 +1701,7 @@ var PerspectApiV2Client = class {
1536
1701
  this.subscriptions = new SubscriptionsV2Client(this.http, basePath, cache);
1537
1702
  this.credits = new CreditsV2Client(this.http, basePath, cache);
1538
1703
  this.email = new EmailV2Client(this.http, basePath, cache);
1704
+ this.emailTemplates = new EmailTemplatesV2Client(this.http, basePath, cache);
1539
1705
  }
1540
1706
  /** Update the JWT token for authenticated requests. */
1541
1707
  setAuth(jwt) {
@@ -1562,6 +1728,7 @@ function createPerspectApiV2Client(config) {
1562
1728
  ContactsV2Client,
1563
1729
  ContentV2Client,
1564
1730
  CreditsV2Client,
1731
+ EmailTemplatesV2Client,
1565
1732
  EmailV2Client,
1566
1733
  NewsletterV2Client,
1567
1734
  OrdersV2Client,
package/dist/v2/index.mjs CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  ContactsV2Client,
7
7
  ContentV2Client,
8
8
  CreditsV2Client,
9
+ EmailTemplatesV2Client,
9
10
  EmailV2Client,
10
11
  NewsletterV2Client,
11
12
  OrdersV2Client,
@@ -18,7 +19,7 @@ import {
18
19
  SubscriptionsV2Client,
19
20
  WebhooksV2Client,
20
21
  createPerspectApiV2Client
21
- } from "../chunk-WD62IBI5.mjs";
22
+ } from "../chunk-PWZSSX4V.mjs";
22
23
  export {
23
24
  ApiKeysV2Client,
24
25
  BaseV2Client,
@@ -27,6 +28,7 @@ export {
27
28
  ContactsV2Client,
28
29
  ContentV2Client,
29
30
  CreditsV2Client,
31
+ EmailTemplatesV2Client,
30
32
  EmailV2Client,
31
33
  NewsletterV2Client,
32
34
  OrdersV2Client,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perspectapi-ts-sdk",
3
- "version": "7.2.6",
3
+ "version": "7.4.0",
4
4
  "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -124,7 +124,7 @@ function buildAbClient(
124
124
  return { variant: flag.defaultVariant, enrolled: false, config: null };
125
125
  }
126
126
  const variantCfg = flag.variants.find((v) => v.key === assigned)?.config ?? null;
127
- emit(apiKey, baseUrl, ctx, {
127
+ await emit(apiKey, baseUrl, ctx, {
128
128
  site: siteName,
129
129
  kind: 'exposure',
130
130
  flag: flagKey,
@@ -146,7 +146,7 @@ function buildAbClient(
146
146
  const bucket = bucketFor(visitorId, flagKey, flag.currentVersion);
147
147
  const assigned = variantForBucket(bucket, flag.variants, flag.trafficAllocationBp);
148
148
  if (assigned === null) continue;
149
- emit(apiKey, baseUrl, ctx, {
149
+ await emit(apiKey, baseUrl, ctx, {
150
150
  site: siteName,
151
151
  kind: 'conversion',
152
152
  flag: flagKey,
@@ -225,7 +225,7 @@ function emit(
225
225
  baseUrl: string,
226
226
  ctx: { waitUntil(p: Promise<unknown>): void } | undefined,
227
227
  body: EventBody,
228
- ): void {
228
+ ): Promise<void> {
229
229
  const task = fetch(`${baseUrl}/_ab/event`, {
230
230
  method: 'POST',
231
231
  headers: { 'content-type': 'application/json', 'x-api-key': apiKey },
@@ -233,7 +233,11 @@ function emit(
233
233
  })
234
234
  .then(() => undefined)
235
235
  .catch(() => undefined);
236
- if (ctx) ctx.waitUntil(task);
236
+ if (ctx) {
237
+ ctx.waitUntil(task);
238
+ return Promise.resolve();
239
+ }
240
+ return task;
237
241
  }
238
242
 
239
243
  // ---------------------------------------------------------------------------
@@ -115,25 +115,37 @@ export class HttpClient {
115
115
  this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
116
116
 
117
117
  let lastError: Error;
118
-
118
+
119
+ // Only retry idempotent methods. Retrying POST/PATCH/PUT/DELETE on a
120
+ // timeout or 5xx can duplicate a side effect the server already applied
121
+ // (e.g. send the same transactional email twice, create two campaigns).
122
+ const method = (options.method || 'GET').toUpperCase();
123
+ const isIdempotent = method === 'GET' || method === 'HEAD';
124
+
119
125
  for (let attempt = 0; attempt <= this.retries; attempt++) {
120
126
  try {
121
127
  const response = await this.fetchWithTimeout(url, requestOptions);
122
128
  return await this.handleResponse<T>(response);
123
129
  } catch (error) {
124
130
  lastError = error as Error;
125
-
131
+
126
132
  // Don't retry on client errors (4xx)
127
- if (error && typeof error === 'object' && 'status' in error &&
133
+ if (error && typeof error === 'object' && 'status' in error &&
128
134
  typeof (error as any).status === 'number' && (error as any).status < 500) {
129
135
  throw error;
130
136
  }
131
-
137
+
138
+ // Don't retry non-idempotent methods — the request may have been
139
+ // applied server-side even though we saw a timeout/5xx.
140
+ if (!isIdempotent) {
141
+ throw error;
142
+ }
143
+
132
144
  // Don't retry on last attempt
133
145
  if (attempt === this.retries) {
134
146
  break;
135
147
  }
136
-
148
+
137
149
  // Exponential backoff
138
150
  await this.delay(Math.pow(2, attempt) * 1000);
139
151
  }