perspectapi-ts-sdk 5.4.4 → 6.0.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
@@ -399,11 +399,7 @@ var CacheManager = class {
399
399
  async getOrSet(key, resolveValue, policy) {
400
400
  if (!this.enabled || policy?.skipCache) {
401
401
  console.log("[Cache] Cache disabled or skipped", { key, enabled: this.enabled, skipCache: policy?.skipCache });
402
- const value2 = await resolveValue();
403
- if (this.enabled && !policy?.skipCache && policy?.ttlSeconds !== 0) {
404
- await this.set(key, value2, policy);
405
- }
406
- return value2;
402
+ return resolveValue();
407
403
  }
408
404
  const namespacedKey = this.namespacedKey(key);
409
405
  const cachedRaw = await this.adapter.get(namespacedKey);
@@ -426,7 +422,7 @@ var CacheManager = class {
426
422
  return value;
427
423
  }
428
424
  async set(key, value, options) {
429
- if (!this.enabled || options?.ttlSeconds === 0) {
425
+ if (!this.enabled) {
430
426
  return;
431
427
  }
432
428
  const namespacedKey = this.namespacedKey(key);
@@ -3422,6 +3418,11 @@ var BaseV2Client = class {
3422
3418
  const response = await this.http.patch(path, body);
3423
3419
  return this.extractData(response);
3424
3420
  }
3421
+ /** PUT to upsert a resource. */
3422
+ async putOne(path, body) {
3423
+ const response = await this.http.put(path, body);
3424
+ return this.extractData(response);
3425
+ }
3425
3426
  /** DELETE a resource. */
3426
3427
  async deleteOne(path) {
3427
3428
  const response = await this.http.delete(path);
@@ -3495,13 +3496,47 @@ var BaseV2Client = class {
3495
3496
  // src/v2/client/content-client.ts
3496
3497
  var ContentV2Client = class extends BaseV2Client {
3497
3498
  async list(siteName, params, cachePolicy) {
3498
- return this.getList(this.sitePath(siteName, "content"), params, cachePolicy);
3499
+ return this.getList(
3500
+ this.sitePath(siteName, "content"),
3501
+ params,
3502
+ this.withContentTags(siteName, cachePolicy, {
3503
+ category: params?.category,
3504
+ slugPrefix: params?.slug_prefix,
3505
+ type: params?.type
3506
+ })
3507
+ );
3499
3508
  }
3500
- async *listAutoPaginated(siteName, params) {
3501
- yield* this.listAutoPaginate(this.sitePath(siteName, "content"), params);
3509
+ async *listAutoPaginated(siteName, params, cachePolicy) {
3510
+ let startingAfter;
3511
+ let hasMore = true;
3512
+ while (hasMore) {
3513
+ const queryParams = { ...params ?? {} };
3514
+ if (startingAfter) {
3515
+ queryParams.starting_after = startingAfter;
3516
+ }
3517
+ const page = await this.list(siteName, queryParams, cachePolicy);
3518
+ for (const item of page.data) {
3519
+ yield item;
3520
+ }
3521
+ hasMore = page.has_more;
3522
+ if (page.data.length > 0) {
3523
+ startingAfter = page.data[page.data.length - 1].id;
3524
+ } else {
3525
+ hasMore = false;
3526
+ }
3527
+ }
3502
3528
  }
3503
3529
  async get(siteName, idOrSlug, cachePolicy) {
3504
- return this.getOne(this.sitePath(siteName, "content", idOrSlug), void 0, cachePolicy);
3530
+ const isContentId = this.isContentId(idOrSlug);
3531
+ return this.getOne(
3532
+ this.sitePath(siteName, "content", idOrSlug),
3533
+ void 0,
3534
+ this.withContentTags(siteName, cachePolicy, {
3535
+ id: isContentId ? idOrSlug : void 0,
3536
+ slug: isContentId ? void 0 : idOrSlug,
3537
+ slugPrefix: isContentId ? void 0 : this.extractSlugPrefix(idOrSlug)
3538
+ })
3539
+ );
3505
3540
  }
3506
3541
  async create(siteName, data) {
3507
3542
  return this.post(this.sitePath(siteName, "content"), data);
@@ -3518,6 +3553,56 @@ var ContentV2Client = class extends BaseV2Client {
3518
3553
  async unpublish(siteName, id) {
3519
3554
  return this.post(this.sitePath(siteName, "content", `${id}/unpublish`));
3520
3555
  }
3556
+ withContentTags(siteName, cachePolicy, options) {
3557
+ const tags = new Set(cachePolicy?.tags ?? []);
3558
+ for (const tag of this.buildContentTags(siteName, options)) {
3559
+ tags.add(tag);
3560
+ }
3561
+ return {
3562
+ ...cachePolicy,
3563
+ tags: Array.from(tags)
3564
+ };
3565
+ }
3566
+ buildContentTags(siteName, options) {
3567
+ const tags = /* @__PURE__ */ new Set(["content", `content:site:${siteName}`]);
3568
+ const normalizedPrefix = this.normalizeTagPart(options.slugPrefix);
3569
+ const normalizedCategory = this.normalizeTagPart(options.category);
3570
+ const normalizedType = this.normalizeTagPart(options.type);
3571
+ if (options.slug) {
3572
+ tags.add(`content:slug:${siteName}:${options.slug}`);
3573
+ }
3574
+ if (options.id) {
3575
+ tags.add(`content:id:${options.id}`);
3576
+ }
3577
+ if (normalizedPrefix) {
3578
+ tags.add(`content:prefix:${normalizedPrefix}`);
3579
+ }
3580
+ if (normalizedType) {
3581
+ tags.add(`content:${normalizedType}`);
3582
+ }
3583
+ if (normalizedCategory) {
3584
+ tags.add("content:category");
3585
+ tags.add(`content:category:${siteName}:${normalizedCategory}`);
3586
+ }
3587
+ return Array.from(tags);
3588
+ }
3589
+ normalizeTagPart(value) {
3590
+ if (typeof value !== "string") {
3591
+ return void 0;
3592
+ }
3593
+ const normalized = value.trim().toLowerCase();
3594
+ return normalized === "" ? void 0 : normalized;
3595
+ }
3596
+ extractSlugPrefix(slug) {
3597
+ const slashIndex = slug.indexOf("/");
3598
+ if (slashIndex > 0) {
3599
+ return slug.slice(0, slashIndex);
3600
+ }
3601
+ return void 0;
3602
+ }
3603
+ isContentId(idOrSlug) {
3604
+ return /^cnt_/i.test(idOrSlug);
3605
+ }
3521
3606
  };
3522
3607
 
3523
3608
  // src/v2/client/products-client.ts
@@ -3614,18 +3699,31 @@ var OrdersV2Client = class extends BaseV2Client {
3614
3699
  async get(siteName, id, cachePolicy) {
3615
3700
  return this.getOne(this.sitePath(siteName, "orders", id), void 0, cachePolicy);
3616
3701
  }
3702
+ /**
3703
+ * Create a checkout session via Stripe. Returns the session ID and a
3704
+ * `checkout_url` that the client should redirect to (or open in a new tab).
3705
+ *
3706
+ * This replaces the v1 `checkout.createCheckoutSession()` + `getCsrfToken()`
3707
+ * dance — v2 is API-key-only and requires no CSRF token.
3708
+ */
3709
+ async create(siteName, data) {
3710
+ return this.post(
3711
+ this.sitePath(siteName, "orders"),
3712
+ data
3713
+ );
3714
+ }
3617
3715
  };
3618
3716
 
3619
3717
  // src/v2/client/site-users-client.ts
3620
3718
  var SiteUsersV2Client = class extends BaseV2Client {
3621
- // --- OTP Auth ---
3719
+ // --- OTP Auth (public, API-key-scoped) ---
3622
3720
  async requestOtp(siteName, data) {
3623
3721
  return this.post(this.sitePath(siteName, "users", "request-otp"), data);
3624
3722
  }
3625
3723
  async verifyOtp(siteName, data) {
3626
3724
  return this.post(this.sitePath(siteName, "users", "verify-otp"), data);
3627
3725
  }
3628
- // --- Admin ---
3726
+ // --- Admin (API key) ---
3629
3727
  async list(siteName, params) {
3630
3728
  return this.getList(this.sitePath(siteName, "users"), params);
3631
3729
  }
@@ -3638,6 +3736,48 @@ var SiteUsersV2Client = class extends BaseV2Client {
3638
3736
  async update(siteName, id, data) {
3639
3737
  return this.patchOne(this.sitePath(siteName, "users", id), data);
3640
3738
  }
3739
+ // --- Authenticated "me" endpoints (site-user JWT) ---
3740
+ /**
3741
+ * Load the currently-authenticated site user's canonical record plus their
3742
+ * profile KV map. Requires `client.setAuth(jwt)` to have been called with
3743
+ * the token returned from `verifyOtp`.
3744
+ */
3745
+ async getMe(siteName) {
3746
+ return this.getOne(
3747
+ this.sitePath(siteName, "users", "me")
3748
+ );
3749
+ }
3750
+ /** Update the authenticated user's own fields. */
3751
+ async updateMe(siteName, data) {
3752
+ return this.patchOne(
3753
+ this.sitePath(siteName, "users", "me"),
3754
+ data
3755
+ );
3756
+ }
3757
+ /** Fetch the profile KV map as a dedicated `site_user_profile` envelope. */
3758
+ async getProfile(siteName) {
3759
+ return this.getOne(
3760
+ this.sitePath(siteName, "users", "me/profile")
3761
+ );
3762
+ }
3763
+ /**
3764
+ * Set a single profile key. The value is persisted verbatim — callers wanting
3765
+ * structured data should JSON-stringify their value first.
3766
+ */
3767
+ async setProfileValue(siteName, key, value) {
3768
+ const encoded = encodeURIComponent(key);
3769
+ return this.putOne(
3770
+ this.sitePath(siteName, "users", `me/profile/${encoded}`),
3771
+ { value }
3772
+ );
3773
+ }
3774
+ /** Delete a single profile key. */
3775
+ async deleteProfileValue(siteName, key) {
3776
+ const encoded = encodeURIComponent(key);
3777
+ return this.deleteOne(
3778
+ this.sitePath(siteName, "users", `me/profile/${encoded}`)
3779
+ );
3780
+ }
3641
3781
  };
3642
3782
 
3643
3783
  // src/v2/client/newsletter-client.ts
@@ -3709,6 +3849,61 @@ var NewsletterV2Client = class extends BaseV2Client {
3709
3849
  cachePolicy
3710
3850
  );
3711
3851
  }
3852
+ // --- Admin writes: Lists CRUD ---
3853
+ async createList(siteName, data) {
3854
+ return this.post(
3855
+ this.sitePath(siteName, "newsletter", "lists"),
3856
+ data
3857
+ );
3858
+ }
3859
+ async updateList(siteName, id, data) {
3860
+ return this.patchOne(
3861
+ this.sitePath(siteName, "newsletter", `lists/${id}`),
3862
+ data
3863
+ );
3864
+ }
3865
+ async deleteList(siteName, id) {
3866
+ return this.deleteOne(
3867
+ this.sitePath(siteName, "newsletter", `lists/${id}`)
3868
+ );
3869
+ }
3870
+ // --- Admin writes: Subscription sync / membership / import ---
3871
+ /**
3872
+ * Upsert a subscription by email and (optionally) replace its list
3873
+ * memberships. Returns a `newsletter_sync_result` envelope with the
3874
+ * outcome (created / updated / resubscribed / already-unsubscribed).
3875
+ */
3876
+ async syncSubscription(siteName, data) {
3877
+ return this.post(
3878
+ this.sitePath(siteName, "newsletter", "subscriptions/sync"),
3879
+ data
3880
+ );
3881
+ }
3882
+ /**
3883
+ * Add/remove/replace the list memberships for an existing subscription.
3884
+ * Returns the refreshed subscription record.
3885
+ */
3886
+ async updateSubscriptionListMembership(siteName, subscriptionId, data) {
3887
+ return this.post(
3888
+ this.sitePath(
3889
+ siteName,
3890
+ "newsletter",
3891
+ `subscriptions/${subscriptionId}/list-membership`
3892
+ ),
3893
+ data
3894
+ );
3895
+ }
3896
+ /**
3897
+ * Bulk import subscriptions. Each row is upserted via the same sync
3898
+ * path; `refreshListCounts` is deferred until after all rows are
3899
+ * processed on the server.
3900
+ */
3901
+ async importSubscriptions(siteName, data) {
3902
+ return this.post(
3903
+ this.sitePath(siteName, "newsletter", "subscriptions/import"),
3904
+ data
3905
+ );
3906
+ }
3712
3907
  };
3713
3908
 
3714
3909
  // src/v2/client/contacts-client.ts
package/dist/index.mjs CHANGED
@@ -331,11 +331,7 @@ var CacheManager = class {
331
331
  async getOrSet(key, resolveValue, policy) {
332
332
  if (!this.enabled || policy?.skipCache) {
333
333
  console.log("[Cache] Cache disabled or skipped", { key, enabled: this.enabled, skipCache: policy?.skipCache });
334
- const value2 = await resolveValue();
335
- if (this.enabled && !policy?.skipCache && policy?.ttlSeconds !== 0) {
336
- await this.set(key, value2, policy);
337
- }
338
- return value2;
334
+ return resolveValue();
339
335
  }
340
336
  const namespacedKey = this.namespacedKey(key);
341
337
  const cachedRaw = await this.adapter.get(namespacedKey);
@@ -358,7 +354,7 @@ var CacheManager = class {
358
354
  return value;
359
355
  }
360
356
  async set(key, value, options) {
361
- if (!this.enabled || options?.ttlSeconds === 0) {
357
+ if (!this.enabled) {
362
358
  return;
363
359
  }
364
360
  const namespacedKey = this.namespacedKey(key);
@@ -3354,6 +3350,11 @@ var BaseV2Client = class {
3354
3350
  const response = await this.http.patch(path, body);
3355
3351
  return this.extractData(response);
3356
3352
  }
3353
+ /** PUT to upsert a resource. */
3354
+ async putOne(path, body) {
3355
+ const response = await this.http.put(path, body);
3356
+ return this.extractData(response);
3357
+ }
3357
3358
  /** DELETE a resource. */
3358
3359
  async deleteOne(path) {
3359
3360
  const response = await this.http.delete(path);
@@ -3427,13 +3428,47 @@ var BaseV2Client = class {
3427
3428
  // src/v2/client/content-client.ts
3428
3429
  var ContentV2Client = class extends BaseV2Client {
3429
3430
  async list(siteName, params, cachePolicy) {
3430
- return this.getList(this.sitePath(siteName, "content"), params, cachePolicy);
3431
+ return this.getList(
3432
+ this.sitePath(siteName, "content"),
3433
+ params,
3434
+ this.withContentTags(siteName, cachePolicy, {
3435
+ category: params?.category,
3436
+ slugPrefix: params?.slug_prefix,
3437
+ type: params?.type
3438
+ })
3439
+ );
3431
3440
  }
3432
- async *listAutoPaginated(siteName, params) {
3433
- yield* this.listAutoPaginate(this.sitePath(siteName, "content"), params);
3441
+ async *listAutoPaginated(siteName, params, cachePolicy) {
3442
+ let startingAfter;
3443
+ let hasMore = true;
3444
+ while (hasMore) {
3445
+ const queryParams = { ...params ?? {} };
3446
+ if (startingAfter) {
3447
+ queryParams.starting_after = startingAfter;
3448
+ }
3449
+ const page = await this.list(siteName, queryParams, cachePolicy);
3450
+ for (const item of page.data) {
3451
+ yield item;
3452
+ }
3453
+ hasMore = page.has_more;
3454
+ if (page.data.length > 0) {
3455
+ startingAfter = page.data[page.data.length - 1].id;
3456
+ } else {
3457
+ hasMore = false;
3458
+ }
3459
+ }
3434
3460
  }
3435
3461
  async get(siteName, idOrSlug, cachePolicy) {
3436
- return this.getOne(this.sitePath(siteName, "content", idOrSlug), void 0, cachePolicy);
3462
+ const isContentId = this.isContentId(idOrSlug);
3463
+ return this.getOne(
3464
+ this.sitePath(siteName, "content", idOrSlug),
3465
+ void 0,
3466
+ this.withContentTags(siteName, cachePolicy, {
3467
+ id: isContentId ? idOrSlug : void 0,
3468
+ slug: isContentId ? void 0 : idOrSlug,
3469
+ slugPrefix: isContentId ? void 0 : this.extractSlugPrefix(idOrSlug)
3470
+ })
3471
+ );
3437
3472
  }
3438
3473
  async create(siteName, data) {
3439
3474
  return this.post(this.sitePath(siteName, "content"), data);
@@ -3450,6 +3485,56 @@ var ContentV2Client = class extends BaseV2Client {
3450
3485
  async unpublish(siteName, id) {
3451
3486
  return this.post(this.sitePath(siteName, "content", `${id}/unpublish`));
3452
3487
  }
3488
+ withContentTags(siteName, cachePolicy, options) {
3489
+ const tags = new Set(cachePolicy?.tags ?? []);
3490
+ for (const tag of this.buildContentTags(siteName, options)) {
3491
+ tags.add(tag);
3492
+ }
3493
+ return {
3494
+ ...cachePolicy,
3495
+ tags: Array.from(tags)
3496
+ };
3497
+ }
3498
+ buildContentTags(siteName, options) {
3499
+ const tags = /* @__PURE__ */ new Set(["content", `content:site:${siteName}`]);
3500
+ const normalizedPrefix = this.normalizeTagPart(options.slugPrefix);
3501
+ const normalizedCategory = this.normalizeTagPart(options.category);
3502
+ const normalizedType = this.normalizeTagPart(options.type);
3503
+ if (options.slug) {
3504
+ tags.add(`content:slug:${siteName}:${options.slug}`);
3505
+ }
3506
+ if (options.id) {
3507
+ tags.add(`content:id:${options.id}`);
3508
+ }
3509
+ if (normalizedPrefix) {
3510
+ tags.add(`content:prefix:${normalizedPrefix}`);
3511
+ }
3512
+ if (normalizedType) {
3513
+ tags.add(`content:${normalizedType}`);
3514
+ }
3515
+ if (normalizedCategory) {
3516
+ tags.add("content:category");
3517
+ tags.add(`content:category:${siteName}:${normalizedCategory}`);
3518
+ }
3519
+ return Array.from(tags);
3520
+ }
3521
+ normalizeTagPart(value) {
3522
+ if (typeof value !== "string") {
3523
+ return void 0;
3524
+ }
3525
+ const normalized = value.trim().toLowerCase();
3526
+ return normalized === "" ? void 0 : normalized;
3527
+ }
3528
+ extractSlugPrefix(slug) {
3529
+ const slashIndex = slug.indexOf("/");
3530
+ if (slashIndex > 0) {
3531
+ return slug.slice(0, slashIndex);
3532
+ }
3533
+ return void 0;
3534
+ }
3535
+ isContentId(idOrSlug) {
3536
+ return /^cnt_/i.test(idOrSlug);
3537
+ }
3453
3538
  };
3454
3539
 
3455
3540
  // src/v2/client/products-client.ts
@@ -3546,18 +3631,31 @@ var OrdersV2Client = class extends BaseV2Client {
3546
3631
  async get(siteName, id, cachePolicy) {
3547
3632
  return this.getOne(this.sitePath(siteName, "orders", id), void 0, cachePolicy);
3548
3633
  }
3634
+ /**
3635
+ * Create a checkout session via Stripe. Returns the session ID and a
3636
+ * `checkout_url` that the client should redirect to (or open in a new tab).
3637
+ *
3638
+ * This replaces the v1 `checkout.createCheckoutSession()` + `getCsrfToken()`
3639
+ * dance — v2 is API-key-only and requires no CSRF token.
3640
+ */
3641
+ async create(siteName, data) {
3642
+ return this.post(
3643
+ this.sitePath(siteName, "orders"),
3644
+ data
3645
+ );
3646
+ }
3549
3647
  };
3550
3648
 
3551
3649
  // src/v2/client/site-users-client.ts
3552
3650
  var SiteUsersV2Client = class extends BaseV2Client {
3553
- // --- OTP Auth ---
3651
+ // --- OTP Auth (public, API-key-scoped) ---
3554
3652
  async requestOtp(siteName, data) {
3555
3653
  return this.post(this.sitePath(siteName, "users", "request-otp"), data);
3556
3654
  }
3557
3655
  async verifyOtp(siteName, data) {
3558
3656
  return this.post(this.sitePath(siteName, "users", "verify-otp"), data);
3559
3657
  }
3560
- // --- Admin ---
3658
+ // --- Admin (API key) ---
3561
3659
  async list(siteName, params) {
3562
3660
  return this.getList(this.sitePath(siteName, "users"), params);
3563
3661
  }
@@ -3570,6 +3668,48 @@ var SiteUsersV2Client = class extends BaseV2Client {
3570
3668
  async update(siteName, id, data) {
3571
3669
  return this.patchOne(this.sitePath(siteName, "users", id), data);
3572
3670
  }
3671
+ // --- Authenticated "me" endpoints (site-user JWT) ---
3672
+ /**
3673
+ * Load the currently-authenticated site user's canonical record plus their
3674
+ * profile KV map. Requires `client.setAuth(jwt)` to have been called with
3675
+ * the token returned from `verifyOtp`.
3676
+ */
3677
+ async getMe(siteName) {
3678
+ return this.getOne(
3679
+ this.sitePath(siteName, "users", "me")
3680
+ );
3681
+ }
3682
+ /** Update the authenticated user's own fields. */
3683
+ async updateMe(siteName, data) {
3684
+ return this.patchOne(
3685
+ this.sitePath(siteName, "users", "me"),
3686
+ data
3687
+ );
3688
+ }
3689
+ /** Fetch the profile KV map as a dedicated `site_user_profile` envelope. */
3690
+ async getProfile(siteName) {
3691
+ return this.getOne(
3692
+ this.sitePath(siteName, "users", "me/profile")
3693
+ );
3694
+ }
3695
+ /**
3696
+ * Set a single profile key. The value is persisted verbatim — callers wanting
3697
+ * structured data should JSON-stringify their value first.
3698
+ */
3699
+ async setProfileValue(siteName, key, value) {
3700
+ const encoded = encodeURIComponent(key);
3701
+ return this.putOne(
3702
+ this.sitePath(siteName, "users", `me/profile/${encoded}`),
3703
+ { value }
3704
+ );
3705
+ }
3706
+ /** Delete a single profile key. */
3707
+ async deleteProfileValue(siteName, key) {
3708
+ const encoded = encodeURIComponent(key);
3709
+ return this.deleteOne(
3710
+ this.sitePath(siteName, "users", `me/profile/${encoded}`)
3711
+ );
3712
+ }
3573
3713
  };
3574
3714
 
3575
3715
  // src/v2/client/newsletter-client.ts
@@ -3641,6 +3781,61 @@ var NewsletterV2Client = class extends BaseV2Client {
3641
3781
  cachePolicy
3642
3782
  );
3643
3783
  }
3784
+ // --- Admin writes: Lists CRUD ---
3785
+ async createList(siteName, data) {
3786
+ return this.post(
3787
+ this.sitePath(siteName, "newsletter", "lists"),
3788
+ data
3789
+ );
3790
+ }
3791
+ async updateList(siteName, id, data) {
3792
+ return this.patchOne(
3793
+ this.sitePath(siteName, "newsletter", `lists/${id}`),
3794
+ data
3795
+ );
3796
+ }
3797
+ async deleteList(siteName, id) {
3798
+ return this.deleteOne(
3799
+ this.sitePath(siteName, "newsletter", `lists/${id}`)
3800
+ );
3801
+ }
3802
+ // --- Admin writes: Subscription sync / membership / import ---
3803
+ /**
3804
+ * Upsert a subscription by email and (optionally) replace its list
3805
+ * memberships. Returns a `newsletter_sync_result` envelope with the
3806
+ * outcome (created / updated / resubscribed / already-unsubscribed).
3807
+ */
3808
+ async syncSubscription(siteName, data) {
3809
+ return this.post(
3810
+ this.sitePath(siteName, "newsletter", "subscriptions/sync"),
3811
+ data
3812
+ );
3813
+ }
3814
+ /**
3815
+ * Add/remove/replace the list memberships for an existing subscription.
3816
+ * Returns the refreshed subscription record.
3817
+ */
3818
+ async updateSubscriptionListMembership(siteName, subscriptionId, data) {
3819
+ return this.post(
3820
+ this.sitePath(
3821
+ siteName,
3822
+ "newsletter",
3823
+ `subscriptions/${subscriptionId}/list-membership`
3824
+ ),
3825
+ data
3826
+ );
3827
+ }
3828
+ /**
3829
+ * Bulk import subscriptions. Each row is upserted via the same sync
3830
+ * path; `refreshListCounts` is deferred until after all rows are
3831
+ * processed on the server.
3832
+ */
3833
+ async importSubscriptions(siteName, data) {
3834
+ return this.post(
3835
+ this.sitePath(siteName, "newsletter", "subscriptions/import"),
3836
+ data
3837
+ );
3838
+ }
3644
3839
  };
3645
3840
 
3646
3841
  // src/v2/client/contacts-client.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perspectapi-ts-sdk",
3
- "version": "5.4.4",
3
+ "version": "6.0.0",
4
4
  "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -76,13 +76,7 @@ export class CacheManager {
76
76
  ): Promise<T> {
77
77
  if (!this.enabled || policy?.skipCache) {
78
78
  console.log('[Cache] Cache disabled or skipped', { key, enabled: this.enabled, skipCache: policy?.skipCache });
79
- const value = await resolveValue();
80
-
81
- if (this.enabled && !policy?.skipCache && policy?.ttlSeconds !== 0) {
82
- await this.set(key, value, policy);
83
- }
84
-
85
- return value;
79
+ return resolveValue();
86
80
  }
87
81
 
88
82
  const namespacedKey = this.namespacedKey(key);
@@ -113,7 +107,7 @@ export class CacheManager {
113
107
  }
114
108
 
115
109
  async set<T>(key: string, value: T, options?: CacheSetOptions): Promise<void> {
116
- if (!this.enabled || options?.ttlSeconds === 0) {
110
+ if (!this.enabled) {
117
111
  return;
118
112
  }
119
113
 
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export { default } from './perspect-api-client';
10
10
  // v2 client
11
11
  export { PerspectApiV2Client, createPerspectApiV2Client } from './v2';
12
12
  export { PerspectV2Error } from './v2/client/base-v2-client';
13
+ export type * from './v2/types';
13
14
 
14
15
  // Individual clients (for advanced usage)
15
16
  export { AuthClient } from './client/auth-client';
@@ -129,6 +129,12 @@ export abstract class BaseV2Client {
129
129
  return this.extractData<T>(response);
130
130
  }
131
131
 
132
+ /** PUT to upsert a resource. */
133
+ protected async putOne<T>(path: string, body?: unknown): Promise<T> {
134
+ const response = await this.http.put<T>(path, body);
135
+ return this.extractData<T>(response);
136
+ }
137
+
132
138
  /** DELETE a resource. */
133
139
  protected async deleteOne(path: string): Promise<V2Deleted> {
134
140
  const response = await this.http.delete<V2Deleted>(path);