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.d.mts +237 -2
- package/dist/index.d.ts +237 -2
- package/dist/index.js +207 -12
- package/dist/index.mjs +207 -12
- package/package.json +1 -1
- package/src/cache/cache-manager.ts +2 -8
- package/src/index.ts +1 -0
- package/src/v2/client/base-v2-client.ts +6 -0
- package/src/v2/client/content-client.ts +125 -4
- package/src/v2/client/newsletter-client.ts +85 -1
- package/src/v2/client/orders-client.ts +27 -1
- package/src/v2/client/site-users-client.ts +76 -3
- package/src/v2/types.ts +194 -0
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
-
|
|
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
|
|
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);
|