perspectapi-ts-sdk 7.2.2 → 7.2.4

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.
@@ -18,10 +18,20 @@ var HttpClient = class {
18
18
  defaultHeaders;
19
19
  timeout;
20
20
  retries;
21
+ debug;
22
+ /**
23
+ * Debug-only logger. Off by default so request URLs, params, headers, and
24
+ * response bodies (emails, profiles, orders, Stripe identifiers) never
25
+ * leak into host logs. Opt in explicitly via `config.debug`.
26
+ */
27
+ dbg(...args) {
28
+ if (this.debug) console["log"](...args);
29
+ }
21
30
  constructor(config) {
22
31
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
23
32
  this.timeout = config.timeout || 3e4;
24
33
  this.retries = config.retries || 3;
34
+ this.debug = config.debug ?? false;
25
35
  this.defaultHeaders = {
26
36
  "Content-Type": "application/json",
27
37
  "User-Agent": "perspectapi-ts-sdk/1.0.0",
@@ -66,15 +76,15 @@ var HttpClient = class {
66
76
  async request(endpoint, options = {}) {
67
77
  const url = this.buildUrl(endpoint, options.params);
68
78
  const requestOptions = this.buildRequestOptions(options);
69
- console.log(`[HTTP Client] ====== Request Details ======`);
70
- console.log(`[HTTP Client] Method: ${options.method || "GET"}`);
71
- console.log(`[HTTP Client] Base URL: ${this.baseUrl}`);
72
- console.log(`[HTTP Client] Endpoint: ${endpoint}`);
73
- console.log(`[HTTP Client] Full URL: ${url}`);
79
+ this.dbg(`[HTTP Client] ====== Request Details ======`);
80
+ this.dbg(`[HTTP Client] Method: ${options.method || "GET"}`);
81
+ this.dbg(`[HTTP Client] Base URL: ${this.baseUrl}`);
82
+ this.dbg(`[HTTP Client] Endpoint: ${endpoint}`);
83
+ this.dbg(`[HTTP Client] Full URL: ${url}`);
74
84
  if (options.params) {
75
- console.log(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
85
+ this.dbg(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
76
86
  if (options.params.slug_prefix) {
77
- console.log(`[HTTP Client] \u26A0\uFE0F slug_prefix detected: "${options.params.slug_prefix}"`);
87
+ this.dbg(`[HTTP Client] \u26A0\uFE0F slug_prefix detected: "${options.params.slug_prefix}"`);
78
88
  }
79
89
  }
80
90
  const safeHeaders = { ...requestOptions.headers };
@@ -84,7 +94,7 @@ var HttpClient = class {
84
94
  if (safeHeaders["X-API-Key"]) {
85
95
  safeHeaders["X-API-Key"] = "[REDACTED]";
86
96
  }
87
- console.log(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
97
+ this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
88
98
  let lastError;
89
99
  for (let attempt = 0; attempt <= this.retries; attempt++) {
90
100
  try {
@@ -138,25 +148,25 @@ var HttpClient = class {
138
148
  */
139
149
  buildUrl(endpoint, params) {
140
150
  const url = `${this.baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
141
- console.log(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
142
- console.log(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
143
- console.log(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
151
+ this.dbg(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
152
+ this.dbg(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
153
+ this.dbg(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
144
154
  if (!params || Object.keys(params).length === 0) {
145
- console.log(`[HTTP Client - URL Builder] No params, returning: ${url}`);
155
+ this.dbg(`[HTTP Client - URL Builder] No params, returning: ${url}`);
146
156
  return url;
147
157
  }
148
- console.log(`[HTTP Client - URL Builder] Processing params:`, params);
158
+ this.dbg(`[HTTP Client - URL Builder] Processing params:`, params);
149
159
  const searchParams = new URLSearchParams();
150
160
  Object.entries(params).forEach(([key, value]) => {
151
161
  if (value !== void 0 && value !== null) {
152
- console.log(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
162
+ this.dbg(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
153
163
  searchParams.append(key, String(value));
154
164
  } else {
155
- console.log(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
165
+ this.dbg(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
156
166
  }
157
167
  });
158
168
  const finalUrl = `${url}?${searchParams.toString()}`;
159
- console.log(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
169
+ this.dbg(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
160
170
  return finalUrl;
161
171
  }
162
172
  /**
@@ -205,42 +215,42 @@ var HttpClient = class {
205
215
  async handleResponse(response) {
206
216
  const contentType = response.headers.get("content-type");
207
217
  const isJson = contentType?.includes("application/json");
208
- console.log(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
209
- console.log(`[HTTP Client - Response] Content-Type: ${contentType}`);
218
+ this.dbg(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
219
+ this.dbg(`[HTTP Client - Response] Content-Type: ${contentType}`);
210
220
  let data;
211
221
  try {
212
222
  data = isJson ? await response.json() : await response.text();
213
223
  } catch (error) {
214
- console.error(`[HTTP Client - Response] Failed to parse response:`, error);
224
+ this.dbg(`[HTTP Client - Response] Failed to parse response:`, error);
215
225
  data = null;
216
226
  }
217
227
  const dataStr = JSON.stringify(data);
218
228
  if (dataStr && dataStr.length > 1e3) {
219
- console.log(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1e3) + "...");
229
+ this.dbg(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1e3) + "...");
220
230
  } else {
221
- console.log(`[HTTP Client - Response] Data:`, data);
231
+ this.dbg(`[HTTP Client - Response] Data:`, data);
222
232
  }
223
233
  if (!response.ok) {
224
- console.error(`[HTTP Client - Response] Error response received`);
234
+ this.dbg(`[HTTP Client - Response] Error response received`);
225
235
  const error = new PerspectApiError({
226
236
  message: data?.error || data?.message || `HTTP ${response.status}: ${response.statusText}`,
227
237
  status: response.status,
228
238
  code: data?.code,
229
239
  details: data
230
240
  });
231
- console.error(`[HTTP Client - Response] Throwing error:`, error);
241
+ this.dbg(`[HTTP Client - Response] Throwing error:`, error);
232
242
  throw error;
233
243
  }
234
244
  if (isJson && typeof data === "object") {
235
245
  if ("data" in data || "message" in data || "error" in data) {
236
246
  const apiResponse = data;
237
- console.log(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : "N/A"}`);
247
+ this.dbg(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : "N/A"}`);
238
248
  return apiResponse;
239
249
  }
240
- console.log(`[HTTP Client - Response] Wrapping response in data property`);
250
+ this.dbg(`[HTTP Client - Response] Wrapping response in data property`);
241
251
  return { data, success: true };
242
252
  }
243
- console.log(`[HTTP Client - Response] Returning text response`);
253
+ this.dbg(`[HTTP Client - Response] Returning text response`);
244
254
  return { data, success: true };
245
255
  }
246
256
  /**
@@ -570,7 +580,9 @@ var BaseV2Client = class {
570
580
  /** GET a single resource, with optional caching. */
571
581
  async getOne(path, params, cachePolicy) {
572
582
  const fetcher = async () => {
573
- const response = await this.http.get(path, this.toParams(params));
583
+ const response = await this.runRequest(
584
+ () => this.http.get(path, this.toParams(params))
585
+ );
574
586
  return this.extractData(response);
575
587
  };
576
588
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
@@ -578,29 +590,31 @@ var BaseV2Client = class {
578
590
  /** GET a list of resources with cursor pagination, with optional caching. */
579
591
  async getList(path, params, cachePolicy) {
580
592
  const fetcher = async () => {
581
- const response = await this.http.get(path, this.toParams(params));
593
+ const response = await this.runRequest(
594
+ () => this.http.get(path, this.toParams(params))
595
+ );
582
596
  return this.extractData(response);
583
597
  };
584
598
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
585
599
  }
586
600
  /** POST to create a resource. */
587
601
  async post(path, body) {
588
- const response = await this.http.post(path, body);
602
+ const response = await this.runRequest(() => this.http.post(path, body));
589
603
  return this.extractData(response);
590
604
  }
591
605
  /** PATCH to update a resource. */
592
606
  async patchOne(path, body) {
593
- const response = await this.http.patch(path, body);
607
+ const response = await this.runRequest(() => this.http.patch(path, body));
594
608
  return this.extractData(response);
595
609
  }
596
610
  /** PUT to upsert a resource. */
597
611
  async putOne(path, body) {
598
- const response = await this.http.put(path, body);
612
+ const response = await this.runRequest(() => this.http.put(path, body));
599
613
  return this.extractData(response);
600
614
  }
601
615
  /** DELETE a resource. */
602
616
  async deleteOne(path) {
603
- const response = await this.http.delete(path);
617
+ const response = await this.runRequest(() => this.http.delete(path));
604
618
  return this.extractData(response);
605
619
  }
606
620
  /** Fetch with optional cache. Bypasses cache for writes or when no cache is configured. */
@@ -656,6 +670,38 @@ var BaseV2Client = class {
656
670
  }
657
671
  }
658
672
  }
673
+ /**
674
+ * Run an HttpClient call and convert v2 error responses to PerspectV2Error.
675
+ *
676
+ * `HttpClient` throws `PerspectApiError` on any non-2xx, with the parsed
677
+ * body on `.details` and the HTTP status on `.status`. For v2 endpoints
678
+ * that body is `{ error: { type, code, message, param? } }` — the
679
+ * documented `PerspectV2Error` shape. Anything else (network failures,
680
+ * non-JSON bodies, malformed envelopes) is rethrown unchanged so callers
681
+ * still see the original error. Named `runRequest` rather than `send` to
682
+ * avoid colliding with subclass domain methods (e.g. EmailV2Client.send).
683
+ */
684
+ async runRequest(call) {
685
+ try {
686
+ return await call();
687
+ } catch (err) {
688
+ const v2 = this.toV2ErrorFromThrown(err);
689
+ if (v2) throw v2;
690
+ throw err;
691
+ }
692
+ }
693
+ toV2ErrorFromThrown(err) {
694
+ if (!err || typeof err !== "object") return void 0;
695
+ const e = err;
696
+ const details = e.details;
697
+ if (!details || typeof details !== "object") return void 0;
698
+ const envelope = details.error;
699
+ if (!envelope || typeof envelope !== "object" || !("type" in envelope) || !("message" in envelope)) {
700
+ return void 0;
701
+ }
702
+ const status = typeof e.status === "number" ? e.status : 500;
703
+ return new PerspectV2Error(envelope, status);
704
+ }
659
705
  toError(response) {
660
706
  const data = response.data;
661
707
  const errorObj = data?.error ?? response.error;
@@ -1087,6 +1087,11 @@ interface PerspectApiConfig {
1087
1087
  retries?: number;
1088
1088
  headers?: Record<string, string>;
1089
1089
  cache?: CacheConfig;
1090
+ /**
1091
+ * Opt-in verbose HTTP logging (URLs, params, headers, response bodies).
1092
+ * Off by default so PII never leaks into host logs.
1093
+ */
1094
+ debug?: boolean;
1090
1095
  }
1091
1096
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
1092
1097
  interface RequestOptions {
@@ -1108,6 +1113,13 @@ declare class HttpClient {
1108
1113
  private defaultHeaders;
1109
1114
  private timeout;
1110
1115
  private retries;
1116
+ private debug;
1117
+ /**
1118
+ * Debug-only logger. Off by default so request URLs, params, headers, and
1119
+ * response bodies (emails, profiles, orders, Stripe identifiers) never
1120
+ * leak into host logs. Opt in explicitly via `config.debug`.
1121
+ */
1122
+ private dbg;
1111
1123
  constructor(config: PerspectApiConfig);
1112
1124
  /**
1113
1125
  * Update authentication token
@@ -1938,6 +1950,19 @@ declare abstract class BaseV2Client {
1938
1950
  protected listAutoPaginate<T extends {
1939
1951
  id: string;
1940
1952
  }>(path: string, params?: object): AsyncGenerator<T, void, unknown>;
1953
+ /**
1954
+ * Run an HttpClient call and convert v2 error responses to PerspectV2Error.
1955
+ *
1956
+ * `HttpClient` throws `PerspectApiError` on any non-2xx, with the parsed
1957
+ * body on `.details` and the HTTP status on `.status`. For v2 endpoints
1958
+ * that body is `{ error: { type, code, message, param? } }` — the
1959
+ * documented `PerspectV2Error` shape. Anything else (network failures,
1960
+ * non-JSON bodies, malformed envelopes) is rethrown unchanged so callers
1961
+ * still see the original error. Named `runRequest` rather than `send` to
1962
+ * avoid colliding with subclass domain methods (e.g. EmailV2Client.send).
1963
+ */
1964
+ private runRequest;
1965
+ private toV2ErrorFromThrown;
1941
1966
  private toError;
1942
1967
  }
1943
1968
 
@@ -1087,6 +1087,11 @@ interface PerspectApiConfig {
1087
1087
  retries?: number;
1088
1088
  headers?: Record<string, string>;
1089
1089
  cache?: CacheConfig;
1090
+ /**
1091
+ * Opt-in verbose HTTP logging (URLs, params, headers, response bodies).
1092
+ * Off by default so PII never leaks into host logs.
1093
+ */
1094
+ debug?: boolean;
1090
1095
  }
1091
1096
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
1092
1097
  interface RequestOptions {
@@ -1108,6 +1113,13 @@ declare class HttpClient {
1108
1113
  private defaultHeaders;
1109
1114
  private timeout;
1110
1115
  private retries;
1116
+ private debug;
1117
+ /**
1118
+ * Debug-only logger. Off by default so request URLs, params, headers, and
1119
+ * response bodies (emails, profiles, orders, Stripe identifiers) never
1120
+ * leak into host logs. Opt in explicitly via `config.debug`.
1121
+ */
1122
+ private dbg;
1111
1123
  constructor(config: PerspectApiConfig);
1112
1124
  /**
1113
1125
  * Update authentication token
@@ -1938,6 +1950,19 @@ declare abstract class BaseV2Client {
1938
1950
  protected listAutoPaginate<T extends {
1939
1951
  id: string;
1940
1952
  }>(path: string, params?: object): AsyncGenerator<T, void, unknown>;
1953
+ /**
1954
+ * Run an HttpClient call and convert v2 error responses to PerspectV2Error.
1955
+ *
1956
+ * `HttpClient` throws `PerspectApiError` on any non-2xx, with the parsed
1957
+ * body on `.details` and the HTTP status on `.status`. For v2 endpoints
1958
+ * that body is `{ error: { type, code, message, param? } }` — the
1959
+ * documented `PerspectV2Error` shape. Anything else (network failures,
1960
+ * non-JSON bodies, malformed envelopes) is rethrown unchanged so callers
1961
+ * still see the original error. Named `runRequest` rather than `send` to
1962
+ * avoid colliding with subclass domain methods (e.g. EmailV2Client.send).
1963
+ */
1964
+ private runRequest;
1965
+ private toV2ErrorFromThrown;
1941
1966
  private toError;
1942
1967
  }
1943
1968
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { H as HttpClient, C as CacheManager, A as ApiResponse, a as CachePolicy, b as CacheInvalidateOptions, U as User, c as ContentQueryParams, P as PaginatedResponse, d as Content, e as ContentCategoryResponse, f as CreateContentRequest, g as UpdateContentRequest, h as ApiKey, i as CreateApiKeyRequest, j as UpdateApiKeyRequest, O as Organization, k as CreateOrganizationRequest, S as Site, l as CreateSiteRequest, m as ProductQueryParams, n as Product, o as CreateProductRequest, p as ProductSku, q as CreateProductSkuRequest, r as Category, s as CreateCategoryRequest, W as Webhook, t as CreateWebhookRequest, u as CreateCheckoutSessionRequest, v as CheckoutSession, w as CreateContactRequest, x as ContactSubmitResponse, y as ContactStatusResponse, z as ContactSubmission, B as CreateNewsletterSubscriptionRequest, N as NewsletterSubscribeResponse, D as NewsletterConfirmResponse, E as NewsletterUnsubscribeRequest, F as NewsletterUnsubscribeResponse, G as NewsletterPreferences, I as NewsletterList, J as NewsletterCampaignListResponse, K as NewsletterCampaignDetail, L as NewsletterStatusResponse, M as NewsletterManagementSubscriptionsListResponse, Q as NewsletterManagementSubscription, R as NewsletterSubscriptionSyncRequest, T as NewsletterSubscriptionSyncResponse, V as NewsletterSubscriptionsBulkUpdateRequest, X as NewsletterSubscriptionsBulkUpdateResponse, Y as NewsletterSubscriptionMembershipUpdateRequest, Z as NewsletterSubscriptionsImportRequest, _ as NewsletterSubscriptionsImportResponse, $ as NewsletterManagementList, a0 as NewsletterManagementSeries, a1 as NewsletterManagementCampaignListResponse, a2 as NewsletterManagementCampaign, a3 as NewsletterCampaignTestSendRequest, a4 as NewsletterCampaignTestSendResponse, a5 as NewsletterManagementStatsResponse, a6 as NewsletterExportCreateRequest, a7 as NewsletterExportCreateResponse, a8 as RequestOtpRequest, a9 as VerifyOtpRequest, aa as VerifyOtpResponse, ab as SiteUser, ac as SiteUserProfile, ad as UpdateSiteUserRequest, ae as SiteUserOrder, af as SiteUserSubscription, ag as CancelSubscriptionRequest, ah as CancelSubscriptionResponse, ai as CreditBalance, aj as CreditBalanceWithTransactions, ak as GrantCreditRequest, al as ProductBundleGroup, am as CreateBundleGroupRequest, an as BundleCollection, ao as BundleCollectionItemWithProduct, ap as CreateBundleCollectionRequest, aq as AddCollectionItemRequest, ar as BundleCollectionItem, as as PerspectApiConfig, at as CacheAdapter, au as BlogPost, av as CheckoutMetadata, aw as CheckoutAddress, ax as CheckoutTaxRequest } from './index-uyP4WdHB.mjs';
2
- export { aE as ApiError, aD as CacheConfig, aG as CategorySummary, c4 as CheckoutMetadataValue, c9 as CheckoutSessionTax, c8 as CheckoutTaxBreakdownItem, c7 as CheckoutTaxCustomerExemptionRequest, c6 as CheckoutTaxExemptionStatus, c5 as CheckoutTaxStrategy, b_ as ContentStatus, b$ as ContentType, c2 as CreatePaymentGatewayRequest, cb as CreditTransaction, cc as HttpMethod, aF as MediaItem, aJ as NewsletterCampaignSummary, aK as NewsletterManagementListMembership, aP as NewsletterManagementPagination, aI as NewsletterSubscription, aN as NewsletterSubscriptionImportRowRequest, aL as NewsletterSubscriptionsBulkAction, aM as NewsletterSubscriptionsBulkOutcome, aO as NewsletterSubscriptionsImportRowResult, bZ as PaginationParams, aH as PaymentGateway, aC as PerspectApiError, ay as PerspectApiV2Client, aA as PerspectV2Error, c0 as ProductSkuMediaItem, c1 as ProductSkuOption, cd as RequestOptions, aQ as SetProfileValueRequest, ca as SubscriptionCancellationMode, 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, c3 as WebhookEventType, aB as createApiError, az as createPerspectApiV2Client } from './index-uyP4WdHB.mjs';
1
+ import { H as HttpClient, C as CacheManager, A as ApiResponse, a as CachePolicy, b as CacheInvalidateOptions, U as User, c as ContentQueryParams, P as PaginatedResponse, d as Content, e as ContentCategoryResponse, f as CreateContentRequest, g as UpdateContentRequest, h as ApiKey, i as CreateApiKeyRequest, j as UpdateApiKeyRequest, O as Organization, k as CreateOrganizationRequest, S as Site, l as CreateSiteRequest, m as ProductQueryParams, n as Product, o as CreateProductRequest, p as ProductSku, q as CreateProductSkuRequest, r as Category, s as CreateCategoryRequest, W as Webhook, t as CreateWebhookRequest, u as CreateCheckoutSessionRequest, v as CheckoutSession, w as CreateContactRequest, x as ContactSubmitResponse, y as ContactStatusResponse, z as ContactSubmission, B as CreateNewsletterSubscriptionRequest, N as NewsletterSubscribeResponse, D as NewsletterConfirmResponse, E as NewsletterUnsubscribeRequest, F as NewsletterUnsubscribeResponse, G as NewsletterPreferences, I as NewsletterList, J as NewsletterCampaignListResponse, K as NewsletterCampaignDetail, L as NewsletterStatusResponse, M as NewsletterManagementSubscriptionsListResponse, Q as NewsletterManagementSubscription, R as NewsletterSubscriptionSyncRequest, T as NewsletterSubscriptionSyncResponse, V as NewsletterSubscriptionsBulkUpdateRequest, X as NewsletterSubscriptionsBulkUpdateResponse, Y as NewsletterSubscriptionMembershipUpdateRequest, Z as NewsletterSubscriptionsImportRequest, _ as NewsletterSubscriptionsImportResponse, $ as NewsletterManagementList, a0 as NewsletterManagementSeries, a1 as NewsletterManagementCampaignListResponse, a2 as NewsletterManagementCampaign, a3 as NewsletterCampaignTestSendRequest, a4 as NewsletterCampaignTestSendResponse, a5 as NewsletterManagementStatsResponse, a6 as NewsletterExportCreateRequest, a7 as NewsletterExportCreateResponse, a8 as RequestOtpRequest, a9 as VerifyOtpRequest, aa as VerifyOtpResponse, ab as SiteUser, ac as SiteUserProfile, ad as UpdateSiteUserRequest, ae as SiteUserOrder, af as SiteUserSubscription, ag as CancelSubscriptionRequest, ah as CancelSubscriptionResponse, ai as CreditBalance, aj as CreditBalanceWithTransactions, ak as GrantCreditRequest, al as ProductBundleGroup, am as CreateBundleGroupRequest, an as BundleCollection, ao as BundleCollectionItemWithProduct, ap as CreateBundleCollectionRequest, aq as AddCollectionItemRequest, ar as BundleCollectionItem, as as PerspectApiConfig, at as CacheAdapter, au as BlogPost, av as CheckoutMetadata, aw as CheckoutAddress, ax as CheckoutTaxRequest } from './index-DVyssmFb.mjs';
2
+ export { aE as ApiError, aD as CacheConfig, aG as CategorySummary, c4 as CheckoutMetadataValue, c9 as CheckoutSessionTax, c8 as CheckoutTaxBreakdownItem, c7 as CheckoutTaxCustomerExemptionRequest, c6 as CheckoutTaxExemptionStatus, c5 as CheckoutTaxStrategy, b_ as ContentStatus, b$ as ContentType, c2 as CreatePaymentGatewayRequest, cb as CreditTransaction, cc as HttpMethod, aF as MediaItem, aJ as NewsletterCampaignSummary, aK as NewsletterManagementListMembership, aP as NewsletterManagementPagination, aI as NewsletterSubscription, aN as NewsletterSubscriptionImportRowRequest, aL as NewsletterSubscriptionsBulkAction, aM as NewsletterSubscriptionsBulkOutcome, aO as NewsletterSubscriptionsImportRowResult, bZ as PaginationParams, aH as PaymentGateway, aC as PerspectApiError, ay as PerspectApiV2Client, aA as PerspectV2Error, c0 as ProductSkuMediaItem, c1 as ProductSkuOption, cd as RequestOptions, aQ as SetProfileValueRequest, ca as SubscriptionCancellationMode, 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, c3 as WebhookEventType, aB as createApiError, az as createPerspectApiV2Client } from './index-DVyssmFb.mjs';
3
3
 
4
4
  /**
5
5
  * v1 deprecation constants — kept in sync with the backend's
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { H as HttpClient, C as CacheManager, A as ApiResponse, a as CachePolicy, b as CacheInvalidateOptions, U as User, c as ContentQueryParams, P as PaginatedResponse, d as Content, e as ContentCategoryResponse, f as CreateContentRequest, g as UpdateContentRequest, h as ApiKey, i as CreateApiKeyRequest, j as UpdateApiKeyRequest, O as Organization, k as CreateOrganizationRequest, S as Site, l as CreateSiteRequest, m as ProductQueryParams, n as Product, o as CreateProductRequest, p as ProductSku, q as CreateProductSkuRequest, r as Category, s as CreateCategoryRequest, W as Webhook, t as CreateWebhookRequest, u as CreateCheckoutSessionRequest, v as CheckoutSession, w as CreateContactRequest, x as ContactSubmitResponse, y as ContactStatusResponse, z as ContactSubmission, B as CreateNewsletterSubscriptionRequest, N as NewsletterSubscribeResponse, D as NewsletterConfirmResponse, E as NewsletterUnsubscribeRequest, F as NewsletterUnsubscribeResponse, G as NewsletterPreferences, I as NewsletterList, J as NewsletterCampaignListResponse, K as NewsletterCampaignDetail, L as NewsletterStatusResponse, M as NewsletterManagementSubscriptionsListResponse, Q as NewsletterManagementSubscription, R as NewsletterSubscriptionSyncRequest, T as NewsletterSubscriptionSyncResponse, V as NewsletterSubscriptionsBulkUpdateRequest, X as NewsletterSubscriptionsBulkUpdateResponse, Y as NewsletterSubscriptionMembershipUpdateRequest, Z as NewsletterSubscriptionsImportRequest, _ as NewsletterSubscriptionsImportResponse, $ as NewsletterManagementList, a0 as NewsletterManagementSeries, a1 as NewsletterManagementCampaignListResponse, a2 as NewsletterManagementCampaign, a3 as NewsletterCampaignTestSendRequest, a4 as NewsletterCampaignTestSendResponse, a5 as NewsletterManagementStatsResponse, a6 as NewsletterExportCreateRequest, a7 as NewsletterExportCreateResponse, a8 as RequestOtpRequest, a9 as VerifyOtpRequest, aa as VerifyOtpResponse, ab as SiteUser, ac as SiteUserProfile, ad as UpdateSiteUserRequest, ae as SiteUserOrder, af as SiteUserSubscription, ag as CancelSubscriptionRequest, ah as CancelSubscriptionResponse, ai as CreditBalance, aj as CreditBalanceWithTransactions, ak as GrantCreditRequest, al as ProductBundleGroup, am as CreateBundleGroupRequest, an as BundleCollection, ao as BundleCollectionItemWithProduct, ap as CreateBundleCollectionRequest, aq as AddCollectionItemRequest, ar as BundleCollectionItem, as as PerspectApiConfig, at as CacheAdapter, au as BlogPost, av as CheckoutMetadata, aw as CheckoutAddress, ax as CheckoutTaxRequest } from './index-uyP4WdHB.js';
2
- export { aE as ApiError, aD as CacheConfig, aG as CategorySummary, c4 as CheckoutMetadataValue, c9 as CheckoutSessionTax, c8 as CheckoutTaxBreakdownItem, c7 as CheckoutTaxCustomerExemptionRequest, c6 as CheckoutTaxExemptionStatus, c5 as CheckoutTaxStrategy, b_ as ContentStatus, b$ as ContentType, c2 as CreatePaymentGatewayRequest, cb as CreditTransaction, cc as HttpMethod, aF as MediaItem, aJ as NewsletterCampaignSummary, aK as NewsletterManagementListMembership, aP as NewsletterManagementPagination, aI as NewsletterSubscription, aN as NewsletterSubscriptionImportRowRequest, aL as NewsletterSubscriptionsBulkAction, aM as NewsletterSubscriptionsBulkOutcome, aO as NewsletterSubscriptionsImportRowResult, bZ as PaginationParams, aH as PaymentGateway, aC as PerspectApiError, ay as PerspectApiV2Client, aA as PerspectV2Error, c0 as ProductSkuMediaItem, c1 as ProductSkuOption, cd as RequestOptions, aQ as SetProfileValueRequest, ca as SubscriptionCancellationMode, 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, c3 as WebhookEventType, aB as createApiError, az as createPerspectApiV2Client } from './index-uyP4WdHB.js';
1
+ import { H as HttpClient, C as CacheManager, A as ApiResponse, a as CachePolicy, b as CacheInvalidateOptions, U as User, c as ContentQueryParams, P as PaginatedResponse, d as Content, e as ContentCategoryResponse, f as CreateContentRequest, g as UpdateContentRequest, h as ApiKey, i as CreateApiKeyRequest, j as UpdateApiKeyRequest, O as Organization, k as CreateOrganizationRequest, S as Site, l as CreateSiteRequest, m as ProductQueryParams, n as Product, o as CreateProductRequest, p as ProductSku, q as CreateProductSkuRequest, r as Category, s as CreateCategoryRequest, W as Webhook, t as CreateWebhookRequest, u as CreateCheckoutSessionRequest, v as CheckoutSession, w as CreateContactRequest, x as ContactSubmitResponse, y as ContactStatusResponse, z as ContactSubmission, B as CreateNewsletterSubscriptionRequest, N as NewsletterSubscribeResponse, D as NewsletterConfirmResponse, E as NewsletterUnsubscribeRequest, F as NewsletterUnsubscribeResponse, G as NewsletterPreferences, I as NewsletterList, J as NewsletterCampaignListResponse, K as NewsletterCampaignDetail, L as NewsletterStatusResponse, M as NewsletterManagementSubscriptionsListResponse, Q as NewsletterManagementSubscription, R as NewsletterSubscriptionSyncRequest, T as NewsletterSubscriptionSyncResponse, V as NewsletterSubscriptionsBulkUpdateRequest, X as NewsletterSubscriptionsBulkUpdateResponse, Y as NewsletterSubscriptionMembershipUpdateRequest, Z as NewsletterSubscriptionsImportRequest, _ as NewsletterSubscriptionsImportResponse, $ as NewsletterManagementList, a0 as NewsletterManagementSeries, a1 as NewsletterManagementCampaignListResponse, a2 as NewsletterManagementCampaign, a3 as NewsletterCampaignTestSendRequest, a4 as NewsletterCampaignTestSendResponse, a5 as NewsletterManagementStatsResponse, a6 as NewsletterExportCreateRequest, a7 as NewsletterExportCreateResponse, a8 as RequestOtpRequest, a9 as VerifyOtpRequest, aa as VerifyOtpResponse, ab as SiteUser, ac as SiteUserProfile, ad as UpdateSiteUserRequest, ae as SiteUserOrder, af as SiteUserSubscription, ag as CancelSubscriptionRequest, ah as CancelSubscriptionResponse, ai as CreditBalance, aj as CreditBalanceWithTransactions, ak as GrantCreditRequest, al as ProductBundleGroup, am as CreateBundleGroupRequest, an as BundleCollection, ao as BundleCollectionItemWithProduct, ap as CreateBundleCollectionRequest, aq as AddCollectionItemRequest, ar as BundleCollectionItem, as as PerspectApiConfig, at as CacheAdapter, au as BlogPost, av as CheckoutMetadata, aw as CheckoutAddress, ax as CheckoutTaxRequest } from './index-DVyssmFb.js';
2
+ export { aE as ApiError, aD as CacheConfig, aG as CategorySummary, c4 as CheckoutMetadataValue, c9 as CheckoutSessionTax, c8 as CheckoutTaxBreakdownItem, c7 as CheckoutTaxCustomerExemptionRequest, c6 as CheckoutTaxExemptionStatus, c5 as CheckoutTaxStrategy, b_ as ContentStatus, b$ as ContentType, c2 as CreatePaymentGatewayRequest, cb as CreditTransaction, cc as HttpMethod, aF as MediaItem, aJ as NewsletterCampaignSummary, aK as NewsletterManagementListMembership, aP as NewsletterManagementPagination, aI as NewsletterSubscription, aN as NewsletterSubscriptionImportRowRequest, aL as NewsletterSubscriptionsBulkAction, aM as NewsletterSubscriptionsBulkOutcome, aO as NewsletterSubscriptionsImportRowResult, bZ as PaginationParams, aH as PaymentGateway, aC as PerspectApiError, ay as PerspectApiV2Client, aA as PerspectV2Error, c0 as ProductSkuMediaItem, c1 as ProductSkuOption, cd as RequestOptions, aQ as SetProfileValueRequest, ca as SubscriptionCancellationMode, 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, c3 as WebhookEventType, aB as createApiError, az as createPerspectApiV2Client } from './index-DVyssmFb.js';
3
3
 
4
4
  /**
5
5
  * v1 deprecation constants — kept in sync with the backend's
package/dist/index.js CHANGED
@@ -104,10 +104,20 @@ var HttpClient = class {
104
104
  defaultHeaders;
105
105
  timeout;
106
106
  retries;
107
+ debug;
108
+ /**
109
+ * Debug-only logger. Off by default so request URLs, params, headers, and
110
+ * response bodies (emails, profiles, orders, Stripe identifiers) never
111
+ * leak into host logs. Opt in explicitly via `config.debug`.
112
+ */
113
+ dbg(...args) {
114
+ if (this.debug) console["log"](...args);
115
+ }
107
116
  constructor(config) {
108
117
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
109
118
  this.timeout = config.timeout || 3e4;
110
119
  this.retries = config.retries || 3;
120
+ this.debug = config.debug ?? false;
111
121
  this.defaultHeaders = {
112
122
  "Content-Type": "application/json",
113
123
  "User-Agent": "perspectapi-ts-sdk/1.0.0",
@@ -152,15 +162,15 @@ var HttpClient = class {
152
162
  async request(endpoint, options = {}) {
153
163
  const url = this.buildUrl(endpoint, options.params);
154
164
  const requestOptions = this.buildRequestOptions(options);
155
- console.log(`[HTTP Client] ====== Request Details ======`);
156
- console.log(`[HTTP Client] Method: ${options.method || "GET"}`);
157
- console.log(`[HTTP Client] Base URL: ${this.baseUrl}`);
158
- console.log(`[HTTP Client] Endpoint: ${endpoint}`);
159
- console.log(`[HTTP Client] Full URL: ${url}`);
165
+ this.dbg(`[HTTP Client] ====== Request Details ======`);
166
+ this.dbg(`[HTTP Client] Method: ${options.method || "GET"}`);
167
+ this.dbg(`[HTTP Client] Base URL: ${this.baseUrl}`);
168
+ this.dbg(`[HTTP Client] Endpoint: ${endpoint}`);
169
+ this.dbg(`[HTTP Client] Full URL: ${url}`);
160
170
  if (options.params) {
161
- console.log(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
171
+ this.dbg(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
162
172
  if (options.params.slug_prefix) {
163
- console.log(`[HTTP Client] \u26A0\uFE0F slug_prefix detected: "${options.params.slug_prefix}"`);
173
+ this.dbg(`[HTTP Client] \u26A0\uFE0F slug_prefix detected: "${options.params.slug_prefix}"`);
164
174
  }
165
175
  }
166
176
  const safeHeaders = { ...requestOptions.headers };
@@ -170,7 +180,7 @@ var HttpClient = class {
170
180
  if (safeHeaders["X-API-Key"]) {
171
181
  safeHeaders["X-API-Key"] = "[REDACTED]";
172
182
  }
173
- console.log(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
183
+ this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
174
184
  let lastError;
175
185
  for (let attempt = 0; attempt <= this.retries; attempt++) {
176
186
  try {
@@ -224,25 +234,25 @@ var HttpClient = class {
224
234
  */
225
235
  buildUrl(endpoint, params) {
226
236
  const url = `${this.baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
227
- console.log(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
228
- console.log(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
229
- console.log(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
237
+ this.dbg(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
238
+ this.dbg(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
239
+ this.dbg(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
230
240
  if (!params || Object.keys(params).length === 0) {
231
- console.log(`[HTTP Client - URL Builder] No params, returning: ${url}`);
241
+ this.dbg(`[HTTP Client - URL Builder] No params, returning: ${url}`);
232
242
  return url;
233
243
  }
234
- console.log(`[HTTP Client - URL Builder] Processing params:`, params);
244
+ this.dbg(`[HTTP Client - URL Builder] Processing params:`, params);
235
245
  const searchParams = new URLSearchParams();
236
246
  Object.entries(params).forEach(([key, value]) => {
237
247
  if (value !== void 0 && value !== null) {
238
- console.log(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
248
+ this.dbg(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
239
249
  searchParams.append(key, String(value));
240
250
  } else {
241
- console.log(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
251
+ this.dbg(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
242
252
  }
243
253
  });
244
254
  const finalUrl = `${url}?${searchParams.toString()}`;
245
- console.log(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
255
+ this.dbg(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
246
256
  return finalUrl;
247
257
  }
248
258
  /**
@@ -291,42 +301,42 @@ var HttpClient = class {
291
301
  async handleResponse(response) {
292
302
  const contentType = response.headers.get("content-type");
293
303
  const isJson = contentType?.includes("application/json");
294
- console.log(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
295
- console.log(`[HTTP Client - Response] Content-Type: ${contentType}`);
304
+ this.dbg(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
305
+ this.dbg(`[HTTP Client - Response] Content-Type: ${contentType}`);
296
306
  let data;
297
307
  try {
298
308
  data = isJson ? await response.json() : await response.text();
299
309
  } catch (error) {
300
- console.error(`[HTTP Client - Response] Failed to parse response:`, error);
310
+ this.dbg(`[HTTP Client - Response] Failed to parse response:`, error);
301
311
  data = null;
302
312
  }
303
313
  const dataStr = JSON.stringify(data);
304
314
  if (dataStr && dataStr.length > 1e3) {
305
- console.log(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1e3) + "...");
315
+ this.dbg(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1e3) + "...");
306
316
  } else {
307
- console.log(`[HTTP Client - Response] Data:`, data);
317
+ this.dbg(`[HTTP Client - Response] Data:`, data);
308
318
  }
309
319
  if (!response.ok) {
310
- console.error(`[HTTP Client - Response] Error response received`);
320
+ this.dbg(`[HTTP Client - Response] Error response received`);
311
321
  const error = new PerspectApiError({
312
322
  message: data?.error || data?.message || `HTTP ${response.status}: ${response.statusText}`,
313
323
  status: response.status,
314
324
  code: data?.code,
315
325
  details: data
316
326
  });
317
- console.error(`[HTTP Client - Response] Throwing error:`, error);
327
+ this.dbg(`[HTTP Client - Response] Throwing error:`, error);
318
328
  throw error;
319
329
  }
320
330
  if (isJson && typeof data === "object") {
321
331
  if ("data" in data || "message" in data || "error" in data) {
322
332
  const apiResponse = data;
323
- console.log(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : "N/A"}`);
333
+ this.dbg(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : "N/A"}`);
324
334
  return apiResponse;
325
335
  }
326
- console.log(`[HTTP Client - Response] Wrapping response in data property`);
336
+ this.dbg(`[HTTP Client - Response] Wrapping response in data property`);
327
337
  return { data, success: true };
328
338
  }
329
- console.log(`[HTTP Client - Response] Returning text response`);
339
+ this.dbg(`[HTTP Client - Response] Returning text response`);
330
340
  return { data, success: true };
331
341
  }
332
342
  /**
@@ -656,7 +666,9 @@ var BaseV2Client = class {
656
666
  /** GET a single resource, with optional caching. */
657
667
  async getOne(path, params, cachePolicy) {
658
668
  const fetcher = async () => {
659
- const response = await this.http.get(path, this.toParams(params));
669
+ const response = await this.runRequest(
670
+ () => this.http.get(path, this.toParams(params))
671
+ );
660
672
  return this.extractData(response);
661
673
  };
662
674
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
@@ -664,29 +676,31 @@ var BaseV2Client = class {
664
676
  /** GET a list of resources with cursor pagination, with optional caching. */
665
677
  async getList(path, params, cachePolicy) {
666
678
  const fetcher = async () => {
667
- const response = await this.http.get(path, this.toParams(params));
679
+ const response = await this.runRequest(
680
+ () => this.http.get(path, this.toParams(params))
681
+ );
668
682
  return this.extractData(response);
669
683
  };
670
684
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
671
685
  }
672
686
  /** POST to create a resource. */
673
687
  async post(path, body) {
674
- const response = await this.http.post(path, body);
688
+ const response = await this.runRequest(() => this.http.post(path, body));
675
689
  return this.extractData(response);
676
690
  }
677
691
  /** PATCH to update a resource. */
678
692
  async patchOne(path, body) {
679
- const response = await this.http.patch(path, body);
693
+ const response = await this.runRequest(() => this.http.patch(path, body));
680
694
  return this.extractData(response);
681
695
  }
682
696
  /** PUT to upsert a resource. */
683
697
  async putOne(path, body) {
684
- const response = await this.http.put(path, body);
698
+ const response = await this.runRequest(() => this.http.put(path, body));
685
699
  return this.extractData(response);
686
700
  }
687
701
  /** DELETE a resource. */
688
702
  async deleteOne(path) {
689
- const response = await this.http.delete(path);
703
+ const response = await this.runRequest(() => this.http.delete(path));
690
704
  return this.extractData(response);
691
705
  }
692
706
  /** Fetch with optional cache. Bypasses cache for writes or when no cache is configured. */
@@ -742,6 +756,38 @@ var BaseV2Client = class {
742
756
  }
743
757
  }
744
758
  }
759
+ /**
760
+ * Run an HttpClient call and convert v2 error responses to PerspectV2Error.
761
+ *
762
+ * `HttpClient` throws `PerspectApiError` on any non-2xx, with the parsed
763
+ * body on `.details` and the HTTP status on `.status`. For v2 endpoints
764
+ * that body is `{ error: { type, code, message, param? } }` — the
765
+ * documented `PerspectV2Error` shape. Anything else (network failures,
766
+ * non-JSON bodies, malformed envelopes) is rethrown unchanged so callers
767
+ * still see the original error. Named `runRequest` rather than `send` to
768
+ * avoid colliding with subclass domain methods (e.g. EmailV2Client.send).
769
+ */
770
+ async runRequest(call) {
771
+ try {
772
+ return await call();
773
+ } catch (err) {
774
+ const v2 = this.toV2ErrorFromThrown(err);
775
+ if (v2) throw v2;
776
+ throw err;
777
+ }
778
+ }
779
+ toV2ErrorFromThrown(err) {
780
+ if (!err || typeof err !== "object") return void 0;
781
+ const e = err;
782
+ const details = e.details;
783
+ if (!details || typeof details !== "object") return void 0;
784
+ const envelope = details.error;
785
+ if (!envelope || typeof envelope !== "object" || !("type" in envelope) || !("message" in envelope)) {
786
+ return void 0;
787
+ }
788
+ const status = typeof e.status === "number" ? e.status : 500;
789
+ return new PerspectV2Error(envelope, status);
790
+ }
745
791
  toError(response) {
746
792
  const data = response.data;
747
793
  const errorObj = data?.error ?? response.error;
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  PerspectV2Error,
8
8
  createApiError,
9
9
  createPerspectApiV2Client
10
- } from "./chunk-Z2LA2IWQ.mjs";
10
+ } from "./chunk-UFVZ7CRS.mjs";
11
11
 
12
12
  // src/deprecation.ts
13
13
  var V1_SUNSET_DATE = "2026-06-01";
@@ -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-uyP4WdHB.mjs';
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-DVyssmFb.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-uyP4WdHB.js';
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-DVyssmFb.js';
package/dist/v2/index.js CHANGED
@@ -62,10 +62,20 @@ var HttpClient = class {
62
62
  defaultHeaders;
63
63
  timeout;
64
64
  retries;
65
+ debug;
66
+ /**
67
+ * Debug-only logger. Off by default so request URLs, params, headers, and
68
+ * response bodies (emails, profiles, orders, Stripe identifiers) never
69
+ * leak into host logs. Opt in explicitly via `config.debug`.
70
+ */
71
+ dbg(...args) {
72
+ if (this.debug) console["log"](...args);
73
+ }
65
74
  constructor(config) {
66
75
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
67
76
  this.timeout = config.timeout || 3e4;
68
77
  this.retries = config.retries || 3;
78
+ this.debug = config.debug ?? false;
69
79
  this.defaultHeaders = {
70
80
  "Content-Type": "application/json",
71
81
  "User-Agent": "perspectapi-ts-sdk/1.0.0",
@@ -110,15 +120,15 @@ var HttpClient = class {
110
120
  async request(endpoint, options = {}) {
111
121
  const url = this.buildUrl(endpoint, options.params);
112
122
  const requestOptions = this.buildRequestOptions(options);
113
- console.log(`[HTTP Client] ====== Request Details ======`);
114
- console.log(`[HTTP Client] Method: ${options.method || "GET"}`);
115
- console.log(`[HTTP Client] Base URL: ${this.baseUrl}`);
116
- console.log(`[HTTP Client] Endpoint: ${endpoint}`);
117
- console.log(`[HTTP Client] Full URL: ${url}`);
123
+ this.dbg(`[HTTP Client] ====== Request Details ======`);
124
+ this.dbg(`[HTTP Client] Method: ${options.method || "GET"}`);
125
+ this.dbg(`[HTTP Client] Base URL: ${this.baseUrl}`);
126
+ this.dbg(`[HTTP Client] Endpoint: ${endpoint}`);
127
+ this.dbg(`[HTTP Client] Full URL: ${url}`);
118
128
  if (options.params) {
119
- console.log(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
129
+ this.dbg(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
120
130
  if (options.params.slug_prefix) {
121
- console.log(`[HTTP Client] \u26A0\uFE0F slug_prefix detected: "${options.params.slug_prefix}"`);
131
+ this.dbg(`[HTTP Client] \u26A0\uFE0F slug_prefix detected: "${options.params.slug_prefix}"`);
122
132
  }
123
133
  }
124
134
  const safeHeaders = { ...requestOptions.headers };
@@ -128,7 +138,7 @@ var HttpClient = class {
128
138
  if (safeHeaders["X-API-Key"]) {
129
139
  safeHeaders["X-API-Key"] = "[REDACTED]";
130
140
  }
131
- console.log(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
141
+ this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
132
142
  let lastError;
133
143
  for (let attempt = 0; attempt <= this.retries; attempt++) {
134
144
  try {
@@ -182,25 +192,25 @@ var HttpClient = class {
182
192
  */
183
193
  buildUrl(endpoint, params) {
184
194
  const url = `${this.baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
185
- console.log(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
186
- console.log(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
187
- console.log(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
195
+ this.dbg(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
196
+ this.dbg(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
197
+ this.dbg(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
188
198
  if (!params || Object.keys(params).length === 0) {
189
- console.log(`[HTTP Client - URL Builder] No params, returning: ${url}`);
199
+ this.dbg(`[HTTP Client - URL Builder] No params, returning: ${url}`);
190
200
  return url;
191
201
  }
192
- console.log(`[HTTP Client - URL Builder] Processing params:`, params);
202
+ this.dbg(`[HTTP Client - URL Builder] Processing params:`, params);
193
203
  const searchParams = new URLSearchParams();
194
204
  Object.entries(params).forEach(([key, value]) => {
195
205
  if (value !== void 0 && value !== null) {
196
- console.log(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
206
+ this.dbg(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
197
207
  searchParams.append(key, String(value));
198
208
  } else {
199
- console.log(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
209
+ this.dbg(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
200
210
  }
201
211
  });
202
212
  const finalUrl = `${url}?${searchParams.toString()}`;
203
- console.log(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
213
+ this.dbg(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
204
214
  return finalUrl;
205
215
  }
206
216
  /**
@@ -249,42 +259,42 @@ var HttpClient = class {
249
259
  async handleResponse(response) {
250
260
  const contentType = response.headers.get("content-type");
251
261
  const isJson = contentType?.includes("application/json");
252
- console.log(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
253
- console.log(`[HTTP Client - Response] Content-Type: ${contentType}`);
262
+ this.dbg(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
263
+ this.dbg(`[HTTP Client - Response] Content-Type: ${contentType}`);
254
264
  let data;
255
265
  try {
256
266
  data = isJson ? await response.json() : await response.text();
257
267
  } catch (error) {
258
- console.error(`[HTTP Client - Response] Failed to parse response:`, error);
268
+ this.dbg(`[HTTP Client - Response] Failed to parse response:`, error);
259
269
  data = null;
260
270
  }
261
271
  const dataStr = JSON.stringify(data);
262
272
  if (dataStr && dataStr.length > 1e3) {
263
- console.log(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1e3) + "...");
273
+ this.dbg(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1e3) + "...");
264
274
  } else {
265
- console.log(`[HTTP Client - Response] Data:`, data);
275
+ this.dbg(`[HTTP Client - Response] Data:`, data);
266
276
  }
267
277
  if (!response.ok) {
268
- console.error(`[HTTP Client - Response] Error response received`);
278
+ this.dbg(`[HTTP Client - Response] Error response received`);
269
279
  const error = new PerspectApiError({
270
280
  message: data?.error || data?.message || `HTTP ${response.status}: ${response.statusText}`,
271
281
  status: response.status,
272
282
  code: data?.code,
273
283
  details: data
274
284
  });
275
- console.error(`[HTTP Client - Response] Throwing error:`, error);
285
+ this.dbg(`[HTTP Client - Response] Throwing error:`, error);
276
286
  throw error;
277
287
  }
278
288
  if (isJson && typeof data === "object") {
279
289
  if ("data" in data || "message" in data || "error" in data) {
280
290
  const apiResponse = data;
281
- console.log(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : "N/A"}`);
291
+ this.dbg(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : "N/A"}`);
282
292
  return apiResponse;
283
293
  }
284
- console.log(`[HTTP Client - Response] Wrapping response in data property`);
294
+ this.dbg(`[HTTP Client - Response] Wrapping response in data property`);
285
295
  return { data, success: true };
286
296
  }
287
- console.log(`[HTTP Client - Response] Returning text response`);
297
+ this.dbg(`[HTTP Client - Response] Returning text response`);
288
298
  return { data, success: true };
289
299
  }
290
300
  /**
@@ -602,7 +612,9 @@ var BaseV2Client = class {
602
612
  /** GET a single resource, with optional caching. */
603
613
  async getOne(path, params, cachePolicy) {
604
614
  const fetcher = async () => {
605
- const response = await this.http.get(path, this.toParams(params));
615
+ const response = await this.runRequest(
616
+ () => this.http.get(path, this.toParams(params))
617
+ );
606
618
  return this.extractData(response);
607
619
  };
608
620
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
@@ -610,29 +622,31 @@ var BaseV2Client = class {
610
622
  /** GET a list of resources with cursor pagination, with optional caching. */
611
623
  async getList(path, params, cachePolicy) {
612
624
  const fetcher = async () => {
613
- const response = await this.http.get(path, this.toParams(params));
625
+ const response = await this.runRequest(
626
+ () => this.http.get(path, this.toParams(params))
627
+ );
614
628
  return this.extractData(response);
615
629
  };
616
630
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
617
631
  }
618
632
  /** POST to create a resource. */
619
633
  async post(path, body) {
620
- const response = await this.http.post(path, body);
634
+ const response = await this.runRequest(() => this.http.post(path, body));
621
635
  return this.extractData(response);
622
636
  }
623
637
  /** PATCH to update a resource. */
624
638
  async patchOne(path, body) {
625
- const response = await this.http.patch(path, body);
639
+ const response = await this.runRequest(() => this.http.patch(path, body));
626
640
  return this.extractData(response);
627
641
  }
628
642
  /** PUT to upsert a resource. */
629
643
  async putOne(path, body) {
630
- const response = await this.http.put(path, body);
644
+ const response = await this.runRequest(() => this.http.put(path, body));
631
645
  return this.extractData(response);
632
646
  }
633
647
  /** DELETE a resource. */
634
648
  async deleteOne(path) {
635
- const response = await this.http.delete(path);
649
+ const response = await this.runRequest(() => this.http.delete(path));
636
650
  return this.extractData(response);
637
651
  }
638
652
  /** Fetch with optional cache. Bypasses cache for writes or when no cache is configured. */
@@ -688,6 +702,38 @@ var BaseV2Client = class {
688
702
  }
689
703
  }
690
704
  }
705
+ /**
706
+ * Run an HttpClient call and convert v2 error responses to PerspectV2Error.
707
+ *
708
+ * `HttpClient` throws `PerspectApiError` on any non-2xx, with the parsed
709
+ * body on `.details` and the HTTP status on `.status`. For v2 endpoints
710
+ * that body is `{ error: { type, code, message, param? } }` — the
711
+ * documented `PerspectV2Error` shape. Anything else (network failures,
712
+ * non-JSON bodies, malformed envelopes) is rethrown unchanged so callers
713
+ * still see the original error. Named `runRequest` rather than `send` to
714
+ * avoid colliding with subclass domain methods (e.g. EmailV2Client.send).
715
+ */
716
+ async runRequest(call) {
717
+ try {
718
+ return await call();
719
+ } catch (err) {
720
+ const v2 = this.toV2ErrorFromThrown(err);
721
+ if (v2) throw v2;
722
+ throw err;
723
+ }
724
+ }
725
+ toV2ErrorFromThrown(err) {
726
+ if (!err || typeof err !== "object") return void 0;
727
+ const e = err;
728
+ const details = e.details;
729
+ if (!details || typeof details !== "object") return void 0;
730
+ const envelope = details.error;
731
+ if (!envelope || typeof envelope !== "object" || !("type" in envelope) || !("message" in envelope)) {
732
+ return void 0;
733
+ }
734
+ const status = typeof e.status === "number" ? e.status : 500;
735
+ return new PerspectV2Error(envelope, status);
736
+ }
691
737
  toError(response) {
692
738
  const data = response.data;
693
739
  const errorObj = data?.error ?? response.error;
package/dist/v2/index.mjs CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  SubscriptionsV2Client,
19
19
  WebhooksV2Client,
20
20
  createPerspectApiV2Client
21
- } from "../chunk-Z2LA2IWQ.mjs";
21
+ } from "../chunk-UFVZ7CRS.mjs";
22
22
  export {
23
23
  ApiKeysV2Client,
24
24
  BaseV2Client,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perspectapi-ts-sdk",
3
- "version": "7.2.2",
3
+ "version": "7.2.4",
4
4
  "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -1192,6 +1192,11 @@ export interface PerspectApiConfig {
1192
1192
  retries?: number;
1193
1193
  headers?: Record<string, string>;
1194
1194
  cache?: CacheConfig;
1195
+ /**
1196
+ * Opt-in verbose HTTP logging (URLs, params, headers, response bodies).
1197
+ * Off by default so PII never leaks into host logs.
1198
+ */
1199
+ debug?: boolean;
1195
1200
  }
1196
1201
 
1197
1202
  // HTTP Methods
@@ -16,11 +16,22 @@ export class HttpClient {
16
16
  private defaultHeaders: Record<string, string>;
17
17
  private timeout: number;
18
18
  private retries: number;
19
+ private debug: boolean;
20
+
21
+ /**
22
+ * Debug-only logger. Off by default so request URLs, params, headers, and
23
+ * response bodies (emails, profiles, orders, Stripe identifiers) never
24
+ * leak into host logs. Opt in explicitly via `config.debug`.
25
+ */
26
+ private dbg(...args: unknown[]): void {
27
+ if (this.debug) console["log"](...args);
28
+ }
19
29
 
20
30
  constructor(config: PerspectApiConfig) {
21
31
  this.baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
22
32
  this.timeout = config.timeout || 30000;
23
33
  this.retries = config.retries || 3;
34
+ this.debug = config.debug ?? false;
24
35
 
25
36
  this.defaultHeaders = {
26
37
  'Content-Type': 'application/json',
@@ -78,18 +89,18 @@ export class HttpClient {
78
89
  const requestOptions = this.buildRequestOptions(options);
79
90
 
80
91
  // Enhanced logging for debugging
81
- console.log(`[HTTP Client] ====== Request Details ======`);
82
- console.log(`[HTTP Client] Method: ${options.method || 'GET'}`);
83
- console.log(`[HTTP Client] Base URL: ${this.baseUrl}`);
84
- console.log(`[HTTP Client] Endpoint: ${endpoint}`);
85
- console.log(`[HTTP Client] Full URL: ${url}`);
92
+ this.dbg(`[HTTP Client] ====== Request Details ======`);
93
+ this.dbg(`[HTTP Client] Method: ${options.method || 'GET'}`);
94
+ this.dbg(`[HTTP Client] Base URL: ${this.baseUrl}`);
95
+ this.dbg(`[HTTP Client] Endpoint: ${endpoint}`);
96
+ this.dbg(`[HTTP Client] Full URL: ${url}`);
86
97
 
87
98
  if (options.params) {
88
- console.log(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
99
+ this.dbg(`[HTTP Client] Query params:`, JSON.stringify(options.params, null, 2));
89
100
 
90
101
  // Specifically log slug_prefix if present
91
102
  if (options.params.slug_prefix) {
92
- console.log(`[HTTP Client] ⚠️ slug_prefix detected: "${options.params.slug_prefix}"`);
103
+ this.dbg(`[HTTP Client] ⚠️ slug_prefix detected: "${options.params.slug_prefix}"`);
93
104
  }
94
105
  }
95
106
 
@@ -101,7 +112,7 @@ export class HttpClient {
101
112
  if (safeHeaders['X-API-Key']) {
102
113
  safeHeaders['X-API-Key'] = '[REDACTED]';
103
114
  }
104
- console.log(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
115
+ this.dbg(`[HTTP Client] Headers:`, JSON.stringify(safeHeaders, null, 2));
105
116
 
106
117
  let lastError: Error;
107
118
 
@@ -172,29 +183,29 @@ export class HttpClient {
172
183
  private buildUrl(endpoint: string, params?: Record<string, any>): string {
173
184
  const url = `${this.baseUrl}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
174
185
 
175
- console.log(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
176
- console.log(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
177
- console.log(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
186
+ this.dbg(`[HTTP Client - URL Builder] Base URL: ${this.baseUrl}`);
187
+ this.dbg(`[HTTP Client - URL Builder] Endpoint: ${endpoint}`);
188
+ this.dbg(`[HTTP Client - URL Builder] Combined URL (before params): ${url}`);
178
189
 
179
190
  if (!params || Object.keys(params).length === 0) {
180
- console.log(`[HTTP Client - URL Builder] No params, returning: ${url}`);
191
+ this.dbg(`[HTTP Client - URL Builder] No params, returning: ${url}`);
181
192
  return url;
182
193
  }
183
194
 
184
- console.log(`[HTTP Client - URL Builder] Processing params:`, params);
195
+ this.dbg(`[HTTP Client - URL Builder] Processing params:`, params);
185
196
 
186
197
  const searchParams = new URLSearchParams();
187
198
  Object.entries(params).forEach(([key, value]) => {
188
199
  if (value !== undefined && value !== null) {
189
- console.log(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
200
+ this.dbg(`[HTTP Client - URL Builder] Adding param: ${key}=${value}`);
190
201
  searchParams.append(key, String(value));
191
202
  } else {
192
- console.log(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
203
+ this.dbg(`[HTTP Client - URL Builder] Skipping null/undefined param: ${key}`);
193
204
  }
194
205
  });
195
206
 
196
207
  const finalUrl = `${url}?${searchParams.toString()}`;
197
- console.log(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
208
+ this.dbg(`[HTTP Client - URL Builder] Final URL: ${finalUrl}`);
198
209
 
199
210
  return finalUrl;
200
211
  }
@@ -254,34 +265,34 @@ export class HttpClient {
254
265
  const contentType = response.headers.get('content-type');
255
266
  const isJson = contentType?.includes('application/json');
256
267
 
257
- console.log(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
258
- console.log(`[HTTP Client - Response] Content-Type: ${contentType}`);
268
+ this.dbg(`[HTTP Client - Response] Status: ${response.status} ${response.statusText}`);
269
+ this.dbg(`[HTTP Client - Response] Content-Type: ${contentType}`);
259
270
 
260
271
  let data: any;
261
272
  try {
262
273
  data = isJson ? await response.json() : await response.text();
263
274
  } catch (error) {
264
- console.error(`[HTTP Client - Response] Failed to parse response:`, error);
275
+ this.dbg(`[HTTP Client - Response] Failed to parse response:`, error);
265
276
  data = null;
266
277
  }
267
278
 
268
279
  // Log response data (truncate if too large)
269
280
  const dataStr = JSON.stringify(data);
270
281
  if (dataStr && dataStr.length > 1000) {
271
- console.log(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1000) + '...');
282
+ this.dbg(`[HTTP Client - Response] Data (truncated):`, dataStr.substring(0, 1000) + '...');
272
283
  } else {
273
- console.log(`[HTTP Client - Response] Data:`, data);
284
+ this.dbg(`[HTTP Client - Response] Data:`, data);
274
285
  }
275
286
 
276
287
  if (!response.ok) {
277
- console.error(`[HTTP Client - Response] Error response received`);
288
+ this.dbg(`[HTTP Client - Response] Error response received`);
278
289
  const error = new PerspectApiError({
279
290
  message: data?.error || data?.message || `HTTP ${response.status}: ${response.statusText}`,
280
291
  status: response.status,
281
292
  code: data?.code,
282
293
  details: data,
283
294
  });
284
- console.error(`[HTTP Client - Response] Throwing error:`, error);
295
+ this.dbg(`[HTTP Client - Response] Throwing error:`, error);
285
296
  throw error;
286
297
  }
287
298
 
@@ -290,15 +301,15 @@ export class HttpClient {
290
301
  // If response has data property, return as-is
291
302
  if ('data' in data || 'message' in data || 'error' in data) {
292
303
  const apiResponse = data as ApiResponse<T>;
293
- console.log(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : 'N/A'}`);
304
+ this.dbg(`[HTTP Client - Response] Returning API response with success=${apiResponse.success}, data length=${Array.isArray(apiResponse.data) ? apiResponse.data.length : 'N/A'}`);
294
305
  return apiResponse;
295
306
  }
296
307
  // Otherwise wrap in data property
297
- console.log(`[HTTP Client - Response] Wrapping response in data property`);
308
+ this.dbg(`[HTTP Client - Response] Wrapping response in data property`);
298
309
  return { data: data as T, success: true };
299
310
  }
300
311
 
301
- console.log(`[HTTP Client - Response] Returning text response`);
312
+ this.dbg(`[HTTP Client - Response] Returning text response`);
302
313
  return { data: data as T, success: true };
303
314
  }
304
315
 
@@ -98,7 +98,9 @@ export abstract class BaseV2Client {
98
98
  /** GET a single resource, with optional caching. */
99
99
  protected async getOne<T>(path: string, params?: object, cachePolicy?: CachePolicy): Promise<T> {
100
100
  const fetcher = async () => {
101
- const response = await this.http.get<T>(path, this.toParams(params));
101
+ const response = await this.runRequest(() =>
102
+ this.http.get<T>(path, this.toParams(params)),
103
+ );
102
104
  return this.extractData<T>(response);
103
105
  };
104
106
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
@@ -111,7 +113,9 @@ export abstract class BaseV2Client {
111
113
  cachePolicy?: CachePolicy,
112
114
  ): Promise<V2List<T>> {
113
115
  const fetcher = async () => {
114
- const response = await this.http.get<V2List<T>>(path, this.toParams(params));
116
+ const response = await this.runRequest(() =>
117
+ this.http.get<V2List<T>>(path, this.toParams(params)),
118
+ );
115
119
  return this.extractData<V2List<T>>(response);
116
120
  };
117
121
  return this.fetchWithCache(path, params, cachePolicy, fetcher);
@@ -119,25 +123,25 @@ export abstract class BaseV2Client {
119
123
 
120
124
  /** POST to create a resource. */
121
125
  protected async post<T>(path: string, body?: unknown): Promise<T> {
122
- const response = await this.http.post<T>(path, body);
126
+ const response = await this.runRequest(() => this.http.post<T>(path, body));
123
127
  return this.extractData<T>(response);
124
128
  }
125
129
 
126
130
  /** PATCH to update a resource. */
127
131
  protected async patchOne<T>(path: string, body?: unknown): Promise<T> {
128
- const response = await this.http.patch<T>(path, body);
132
+ const response = await this.runRequest(() => this.http.patch<T>(path, body));
129
133
  return this.extractData<T>(response);
130
134
  }
131
135
 
132
136
  /** PUT to upsert a resource. */
133
137
  protected async putOne<T>(path: string, body?: unknown): Promise<T> {
134
- const response = await this.http.put<T>(path, body);
138
+ const response = await this.runRequest(() => this.http.put<T>(path, body));
135
139
  return this.extractData<T>(response);
136
140
  }
137
141
 
138
142
  /** DELETE a resource. */
139
143
  protected async deleteOne(path: string): Promise<V2Deleted> {
140
- const response = await this.http.delete<V2Deleted>(path);
144
+ const response = await this.runRequest(() => this.http.delete<V2Deleted>(path));
141
145
  return this.extractData<V2Deleted>(response);
142
146
  }
143
147
 
@@ -209,6 +213,45 @@ export abstract class BaseV2Client {
209
213
  }
210
214
  }
211
215
 
216
+ /**
217
+ * Run an HttpClient call and convert v2 error responses to PerspectV2Error.
218
+ *
219
+ * `HttpClient` throws `PerspectApiError` on any non-2xx, with the parsed
220
+ * body on `.details` and the HTTP status on `.status`. For v2 endpoints
221
+ * that body is `{ error: { type, code, message, param? } }` — the
222
+ * documented `PerspectV2Error` shape. Anything else (network failures,
223
+ * non-JSON bodies, malformed envelopes) is rethrown unchanged so callers
224
+ * still see the original error. Named `runRequest` rather than `send` to
225
+ * avoid colliding with subclass domain methods (e.g. EmailV2Client.send).
226
+ */
227
+ private async runRequest<R>(call: () => Promise<R>): Promise<R> {
228
+ try {
229
+ return await call();
230
+ } catch (err) {
231
+ const v2 = this.toV2ErrorFromThrown(err);
232
+ if (v2) throw v2;
233
+ throw err;
234
+ }
235
+ }
236
+
237
+ private toV2ErrorFromThrown(err: unknown): PerspectV2Error | undefined {
238
+ if (!err || typeof err !== 'object') return undefined;
239
+ const e = err as { status?: unknown; details?: unknown };
240
+ const details = e.details;
241
+ if (!details || typeof details !== 'object') return undefined;
242
+ const envelope = (details as { error?: unknown }).error;
243
+ if (
244
+ !envelope ||
245
+ typeof envelope !== 'object' ||
246
+ !('type' in envelope) ||
247
+ !('message' in envelope)
248
+ ) {
249
+ return undefined;
250
+ }
251
+ const status = typeof e.status === 'number' ? e.status : 500;
252
+ return new PerspectV2Error(envelope as V2Error['error'], status);
253
+ }
254
+
212
255
  private toError(response: { data?: unknown; error?: unknown; message?: string }): PerspectV2Error {
213
256
  const data = response.data as Record<string, unknown> | undefined;
214
257
  const errorObj = (data?.error ?? response.error) as V2Error['error'] | undefined;