perspectapi-ts-sdk 3.5.1 → 3.7.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/README.md +29 -1
- package/dist/index.d.mts +116 -27
- package/dist/index.d.ts +116 -27
- package/dist/index.js +246 -4
- package/dist/index.mjs +246 -4
- package/package.json +1 -1
- package/src/client/newsletter-client.ts +343 -3
- package/src/client/products-client.ts +59 -27
- package/src/index.ts +3 -0
- package/src/types/index.ts +90 -0
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import { BaseClient } from './base-client';
|
|
6
6
|
import type { CacheManager } from '../cache/cache-manager';
|
|
7
|
+
import type { CachePolicy } from '../cache/types';
|
|
7
8
|
import type {
|
|
8
9
|
NewsletterSubscription,
|
|
9
10
|
CreateNewsletterSubscriptionRequest,
|
|
10
11
|
NewsletterList,
|
|
12
|
+
NewsletterCampaignDetail,
|
|
13
|
+
NewsletterCampaignListResponse,
|
|
11
14
|
NewsletterPreferences,
|
|
12
15
|
NewsletterStatusResponse,
|
|
13
16
|
NewsletterSubscribeResponse,
|
|
@@ -17,6 +20,7 @@ import type {
|
|
|
17
20
|
PaginatedResponse,
|
|
18
21
|
ApiResponse,
|
|
19
22
|
} from '../types';
|
|
23
|
+
import { validateOptionalLimit } from '../utils/validators';
|
|
20
24
|
|
|
21
25
|
export class NewsletterClient extends BaseClient {
|
|
22
26
|
constructor(http: any, cache?: CacheManager) {
|
|
@@ -119,15 +123,174 @@ export class NewsletterClient extends BaseClient {
|
|
|
119
123
|
/**
|
|
120
124
|
* Get available newsletter lists
|
|
121
125
|
*/
|
|
122
|
-
async getLists(
|
|
126
|
+
async getLists(
|
|
127
|
+
siteName: string,
|
|
128
|
+
cachePolicy?: CachePolicy
|
|
129
|
+
): Promise<ApiResponse<{
|
|
123
130
|
lists: NewsletterList[];
|
|
124
131
|
total: number;
|
|
125
132
|
}>> {
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
const endpoint = this.newsletterEndpoint(siteName, '/newsletter/lists');
|
|
134
|
+
const path = this.buildPath(endpoint);
|
|
135
|
+
|
|
136
|
+
return this.fetchWithCache<ApiResponse<{
|
|
137
|
+
lists: NewsletterList[];
|
|
138
|
+
total: number;
|
|
139
|
+
}>>(
|
|
140
|
+
endpoint,
|
|
141
|
+
undefined,
|
|
142
|
+
this.buildNewsletterTags(siteName, [
|
|
143
|
+
'newsletter:lists',
|
|
144
|
+
`newsletter:lists:site:${siteName}`,
|
|
145
|
+
]),
|
|
146
|
+
cachePolicy,
|
|
147
|
+
() => this.http.get(path)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* List publicly available (sent) newsletter campaigns
|
|
153
|
+
*/
|
|
154
|
+
async getPublishedCampaigns(
|
|
155
|
+
siteName: string,
|
|
156
|
+
params?: {
|
|
157
|
+
page?: number;
|
|
158
|
+
limit?: number;
|
|
159
|
+
search?: string;
|
|
160
|
+
},
|
|
161
|
+
cachePolicy?: CachePolicy
|
|
162
|
+
): Promise<ApiResponse<NewsletterCampaignListResponse>> {
|
|
163
|
+
const endpoint = this.newsletterEndpoint(siteName, '/newsletter/campaigns');
|
|
164
|
+
const path = this.buildPath(endpoint);
|
|
165
|
+
const normalizedParams = params ? { ...params } : undefined;
|
|
166
|
+
|
|
167
|
+
if (normalizedParams) {
|
|
168
|
+
const validatedLimit = validateOptionalLimit(
|
|
169
|
+
normalizedParams.limit,
|
|
170
|
+
'newsletter campaigns query',
|
|
171
|
+
);
|
|
172
|
+
if (validatedLimit !== undefined) {
|
|
173
|
+
normalizedParams.limit = validatedLimit;
|
|
174
|
+
} else {
|
|
175
|
+
delete normalizedParams.limit;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (
|
|
179
|
+
typeof normalizedParams.search === 'string' &&
|
|
180
|
+
normalizedParams.search.trim().length === 0
|
|
181
|
+
) {
|
|
182
|
+
delete normalizedParams.search;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return this.fetchWithCache<ApiResponse<NewsletterCampaignListResponse>>(
|
|
187
|
+
endpoint,
|
|
188
|
+
normalizedParams,
|
|
189
|
+
this.buildNewsletterTags(siteName, [
|
|
190
|
+
'newsletter:campaigns',
|
|
191
|
+
`newsletter:campaigns:list:${siteName}`,
|
|
192
|
+
]),
|
|
193
|
+
cachePolicy,
|
|
194
|
+
() => this.http.get<NewsletterCampaignListResponse>(path, normalizedParams)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Fetch a publicly available (sent) newsletter campaign by slug
|
|
200
|
+
*/
|
|
201
|
+
async getPublishedCampaignBySlug(
|
|
202
|
+
siteName: string,
|
|
203
|
+
slug: string,
|
|
204
|
+
optionsOrPolicy?: { slugPrefix?: string } | CachePolicy,
|
|
205
|
+
cachePolicy?: CachePolicy
|
|
206
|
+
): Promise<ApiResponse<NewsletterCampaignDetail>> {
|
|
207
|
+
let normalizedSlug = slug.trim();
|
|
208
|
+
if (!normalizedSlug) {
|
|
209
|
+
throw new Error('slug is required');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const isCachePolicyArg = this.isCachePolicy(optionsOrPolicy);
|
|
213
|
+
const providedSlugPrefix =
|
|
214
|
+
!isCachePolicyArg && optionsOrPolicy
|
|
215
|
+
? this.pickString(optionsOrPolicy as Record<string, unknown>, ['slugPrefix', 'slug_prefix'])
|
|
216
|
+
: undefined;
|
|
217
|
+
const resolvedCachePolicy = isCachePolicyArg
|
|
218
|
+
? (optionsOrPolicy as CachePolicy)
|
|
219
|
+
: cachePolicy;
|
|
220
|
+
|
|
221
|
+
let slugPrefix = providedSlugPrefix;
|
|
222
|
+
if (!slugPrefix) {
|
|
223
|
+
const parts = normalizedSlug.split('/').filter(Boolean);
|
|
224
|
+
if (parts.length > 1) {
|
|
225
|
+
slugPrefix = parts.slice(0, -1).join('/');
|
|
226
|
+
normalizedSlug = parts[parts.length - 1];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
normalizedSlug = normalizedSlug.trim();
|
|
230
|
+
if (!normalizedSlug) {
|
|
231
|
+
throw new Error('slug is required');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const endpoint = this.newsletterEndpoint(
|
|
235
|
+
siteName,
|
|
236
|
+
`/newsletter/campaigns/${encodeURIComponent(normalizedSlug)}`
|
|
237
|
+
);
|
|
238
|
+
const path = this.buildPath(endpoint);
|
|
239
|
+
const queryParams = slugPrefix ? { slug_prefix: slugPrefix } : undefined;
|
|
240
|
+
|
|
241
|
+
return this.fetchWithCache<ApiResponse<NewsletterCampaignDetail>>(
|
|
242
|
+
endpoint,
|
|
243
|
+
queryParams,
|
|
244
|
+
this.buildNewsletterTags(
|
|
245
|
+
siteName,
|
|
246
|
+
[
|
|
247
|
+
'newsletter:campaigns',
|
|
248
|
+
`newsletter:campaigns:slug:${siteName}:${normalizedSlug}`,
|
|
249
|
+
`newsletter:campaigns:detail:${siteName}`,
|
|
250
|
+
],
|
|
251
|
+
slugPrefix
|
|
252
|
+
),
|
|
253
|
+
resolvedCachePolicy,
|
|
254
|
+
() => this.http.get<NewsletterCampaignDetail>(path, queryParams)
|
|
128
255
|
);
|
|
129
256
|
}
|
|
130
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Invalidate cached published newsletter campaign data from a webhook payload.
|
|
260
|
+
* Only publish events (and legacy sent aliases) trigger invalidation.
|
|
261
|
+
*/
|
|
262
|
+
async invalidatePublishedCampaignCacheFromWebhook(
|
|
263
|
+
payload: Record<string, unknown>,
|
|
264
|
+
fallbackSiteName?: string,
|
|
265
|
+
): Promise<{ invalidated: boolean; tags: string[]; reason?: string }> {
|
|
266
|
+
const normalized = this.normalizeNewsletterWebhookPayload(payload, fallbackSiteName);
|
|
267
|
+
if (!normalized.shouldInvalidate) {
|
|
268
|
+
return {
|
|
269
|
+
invalidated: false,
|
|
270
|
+
tags: [],
|
|
271
|
+
reason: normalized.reason || 'event_not_publish_related',
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const tags = this.buildNewsletterTags(
|
|
276
|
+
normalized.siteName,
|
|
277
|
+
[
|
|
278
|
+
'newsletter:campaigns',
|
|
279
|
+
`newsletter:campaigns:list:${normalized.siteName}`,
|
|
280
|
+
normalized.slug
|
|
281
|
+
? `newsletter:campaigns:slug:${normalized.siteName}:${normalized.slug}`
|
|
282
|
+
: '',
|
|
283
|
+
normalized.campaignId
|
|
284
|
+
? `newsletter:campaigns:id:${normalized.siteName}:${normalized.campaignId}`
|
|
285
|
+
: '',
|
|
286
|
+
],
|
|
287
|
+
normalized.slugPrefix,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
await this.invalidateCache({ tags });
|
|
291
|
+
return { invalidated: true, tags };
|
|
292
|
+
}
|
|
293
|
+
|
|
131
294
|
/**
|
|
132
295
|
* Check subscription status by email
|
|
133
296
|
*/
|
|
@@ -432,4 +595,181 @@ export class NewsletterClient extends BaseClient {
|
|
|
432
595
|
0x44, 0x01, 0x00, 0x3b,
|
|
433
596
|
]);
|
|
434
597
|
}
|
|
598
|
+
|
|
599
|
+
private buildNewsletterTags(
|
|
600
|
+
siteName?: string,
|
|
601
|
+
extraTags: string[] = [],
|
|
602
|
+
slugPrefix?: string
|
|
603
|
+
): string[] {
|
|
604
|
+
const tags = new Set<string>(['newsletter']);
|
|
605
|
+
if (siteName) {
|
|
606
|
+
tags.add(`newsletter:site:${siteName}`);
|
|
607
|
+
}
|
|
608
|
+
if (slugPrefix) {
|
|
609
|
+
tags.add(`newsletter:prefix:${slugPrefix}`);
|
|
610
|
+
}
|
|
611
|
+
extraTags.filter(Boolean).forEach(tag => tags.add(tag));
|
|
612
|
+
return Array.from(tags.values());
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private extractSlugPrefix(slug: string): string | undefined {
|
|
616
|
+
const normalized = slug.trim();
|
|
617
|
+
if (!normalized.includes('/')) {
|
|
618
|
+
return undefined;
|
|
619
|
+
}
|
|
620
|
+
const [prefix] = normalized.split('/').filter(Boolean);
|
|
621
|
+
return prefix || undefined;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
private normalizeNewsletterWebhookPayload(
|
|
625
|
+
payload: Record<string, unknown>,
|
|
626
|
+
fallbackSiteName?: string
|
|
627
|
+
): {
|
|
628
|
+
shouldInvalidate: boolean;
|
|
629
|
+
reason?: string;
|
|
630
|
+
siteName: string;
|
|
631
|
+
slug?: string;
|
|
632
|
+
slugPrefix?: string;
|
|
633
|
+
campaignId?: string;
|
|
634
|
+
} {
|
|
635
|
+
const eventType = (
|
|
636
|
+
this.pickString(payload, ['event_type', 'type']) || ''
|
|
637
|
+
).toLowerCase();
|
|
638
|
+
|
|
639
|
+
const dataRaw = payload.data;
|
|
640
|
+
const data = (dataRaw && typeof dataRaw === 'object' && !Array.isArray(dataRaw))
|
|
641
|
+
? (dataRaw as Record<string, unknown>)
|
|
642
|
+
: payload;
|
|
643
|
+
|
|
644
|
+
const originType = (
|
|
645
|
+
this.pickString(data, ['origin_type', 'originType']) || ''
|
|
646
|
+
).toLowerCase();
|
|
647
|
+
const status = (
|
|
648
|
+
this.pickString(data, ['status', 'campaign_status', 'campaignStatus']) || ''
|
|
649
|
+
).toLowerCase();
|
|
650
|
+
|
|
651
|
+
if (!this.isPublishEvent(eventType, status, originType)) {
|
|
652
|
+
return {
|
|
653
|
+
shouldInvalidate: false,
|
|
654
|
+
reason: 'not_publish_or_sent_event',
|
|
655
|
+
siteName: fallbackSiteName || '',
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const siteName =
|
|
660
|
+
this.pickString(payload, ['site', 'site_name', 'siteName']) ||
|
|
661
|
+
this.pickString(data, ['site', 'site_name', 'siteName']) ||
|
|
662
|
+
fallbackSiteName ||
|
|
663
|
+
'';
|
|
664
|
+
|
|
665
|
+
if (!siteName) {
|
|
666
|
+
return {
|
|
667
|
+
shouldInvalidate: false,
|
|
668
|
+
reason: 'missing_site_name',
|
|
669
|
+
siteName: '',
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
let slug =
|
|
674
|
+
this.pickString(data, ['slug', 'canonical_slug', 'canonicalSlug']) ||
|
|
675
|
+
this.pickString(payload, ['slug', 'canonical_slug', 'canonicalSlug']) ||
|
|
676
|
+
undefined;
|
|
677
|
+
|
|
678
|
+
let slugPrefix =
|
|
679
|
+
this.pickString(data, ['slug_prefix', 'slugPrefix']) ||
|
|
680
|
+
this.pickString(payload, ['slug_prefix', 'slugPrefix']) ||
|
|
681
|
+
(slug ? this.extractSlugPrefix(slug) : undefined);
|
|
682
|
+
|
|
683
|
+
if (slug) {
|
|
684
|
+
const slugParts = slug.split('/').filter(Boolean);
|
|
685
|
+
if (slugParts.length > 1) {
|
|
686
|
+
if (!slugPrefix) {
|
|
687
|
+
slugPrefix = slugParts.slice(0, -1).join('/');
|
|
688
|
+
}
|
|
689
|
+
slug = slugParts[slugParts.length - 1];
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const campaignId =
|
|
694
|
+
this.pickString(data, ['campaign_id', 'campaignId']) ||
|
|
695
|
+
this.pickString(payload, ['campaign_id', 'campaignId']) ||
|
|
696
|
+
undefined;
|
|
697
|
+
|
|
698
|
+
return {
|
|
699
|
+
shouldInvalidate: true,
|
|
700
|
+
siteName,
|
|
701
|
+
slug,
|
|
702
|
+
slugPrefix,
|
|
703
|
+
campaignId,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
private isPublishEvent(
|
|
708
|
+
eventType: string,
|
|
709
|
+
status: string,
|
|
710
|
+
originType: string
|
|
711
|
+
): boolean {
|
|
712
|
+
const knownPublishEvents = new Set([
|
|
713
|
+
'newsletter.published',
|
|
714
|
+
'newsletter.sent',
|
|
715
|
+
'newsletter_campaign.published',
|
|
716
|
+
'newsletter_campaign.sent',
|
|
717
|
+
'newsletter.campaign.published',
|
|
718
|
+
'newsletter.campaign.sent',
|
|
719
|
+
'campaign.published',
|
|
720
|
+
'campaign.sent',
|
|
721
|
+
]);
|
|
722
|
+
|
|
723
|
+
if (knownPublishEvents.has(eventType)) {
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Content webhook event for unified newsletter campaign content.
|
|
728
|
+
if (eventType === 'content.published' && originType === 'newsletter_campaign') {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// Some systems emit update events with final status.
|
|
733
|
+
if (
|
|
734
|
+
(eventType.includes('newsletter') || eventType.includes('campaign')) &&
|
|
735
|
+
eventType.endsWith('.updated') &&
|
|
736
|
+
(status === 'published' || status === 'sent')
|
|
737
|
+
) {
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
private pickString(
|
|
745
|
+
source: Record<string, unknown> | undefined,
|
|
746
|
+
keys: string[]
|
|
747
|
+
): string | undefined {
|
|
748
|
+
if (!source) {
|
|
749
|
+
return undefined;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
for (const key of keys) {
|
|
753
|
+
const value = source[key];
|
|
754
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
755
|
+
return value.trim();
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
return undefined;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
private isCachePolicy(value: unknown): value is CachePolicy {
|
|
763
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const record = value as Record<string, unknown>;
|
|
768
|
+
return (
|
|
769
|
+
'ttlSeconds' in record ||
|
|
770
|
+
'tags' in record ||
|
|
771
|
+
'metadata' in record ||
|
|
772
|
+
'skipCache' in record
|
|
773
|
+
);
|
|
774
|
+
}
|
|
435
775
|
}
|
|
@@ -8,6 +8,8 @@ import type { CachePolicy } from '../cache/types';
|
|
|
8
8
|
import type {
|
|
9
9
|
Product,
|
|
10
10
|
CreateProductRequest,
|
|
11
|
+
CreateProductSkuRequest,
|
|
12
|
+
ProductSku,
|
|
11
13
|
PaginatedResponse,
|
|
12
14
|
ApiResponse,
|
|
13
15
|
ProductQueryParams,
|
|
@@ -457,17 +459,7 @@ export class ProductsClient extends BaseClient {
|
|
|
457
459
|
async getProductSkus(
|
|
458
460
|
siteName: string,
|
|
459
461
|
productId: number
|
|
460
|
-
): Promise<ApiResponse<
|
|
461
|
-
sku_id: number;
|
|
462
|
-
sku: string;
|
|
463
|
-
price?: number;
|
|
464
|
-
sale_price?: number;
|
|
465
|
-
stock_quantity?: number;
|
|
466
|
-
combination_key: string;
|
|
467
|
-
value_ids: number[];
|
|
468
|
-
created_at: string;
|
|
469
|
-
updated_at: string;
|
|
470
|
-
}>>> {
|
|
462
|
+
): Promise<ApiResponse<ProductSku[]>> {
|
|
471
463
|
const endpoint = this.siteScopedEndpoint(
|
|
472
464
|
siteName,
|
|
473
465
|
`/products/${productId}/skus`,
|
|
@@ -482,26 +474,66 @@ export class ProductsClient extends BaseClient {
|
|
|
482
474
|
async createProductSku(
|
|
483
475
|
siteName: string,
|
|
484
476
|
productId: number,
|
|
485
|
-
data:
|
|
486
|
-
|
|
487
|
-
price?: number | null;
|
|
488
|
-
sale_price?: number | null;
|
|
489
|
-
stock_quantity?: number | null;
|
|
490
|
-
value_ids: number[];
|
|
491
|
-
}
|
|
492
|
-
): Promise<ApiResponse<{
|
|
493
|
-
sku_id: number;
|
|
494
|
-
sku: string;
|
|
495
|
-
price?: number;
|
|
496
|
-
sale_price?: number;
|
|
497
|
-
stock_quantity?: number;
|
|
498
|
-
combination_key: string;
|
|
499
|
-
}>> {
|
|
477
|
+
data: CreateProductSkuRequest
|
|
478
|
+
): Promise<ApiResponse<ProductSku>> {
|
|
500
479
|
const endpoint = this.siteScopedEndpoint(
|
|
501
480
|
siteName,
|
|
502
481
|
`/products/${productId}/skus`,
|
|
503
482
|
{ includeSitesSegment: false }
|
|
504
483
|
);
|
|
505
|
-
|
|
484
|
+
|
|
485
|
+
const unitAmountCandidate =
|
|
486
|
+
typeof data.unit_amount === 'number'
|
|
487
|
+
? data.unit_amount
|
|
488
|
+
: typeof data.price === 'number'
|
|
489
|
+
? Math.round(data.price * 100)
|
|
490
|
+
: undefined;
|
|
491
|
+
|
|
492
|
+
if (typeof unitAmountCandidate !== 'number' || !Number.isFinite(unitAmountCandidate)) {
|
|
493
|
+
throw new Error('createProductSku requires unit_amount or price');
|
|
494
|
+
}
|
|
495
|
+
const unitAmount = unitAmountCandidate;
|
|
496
|
+
|
|
497
|
+
const quantityAvailable =
|
|
498
|
+
data.quantity_available !== undefined
|
|
499
|
+
? data.quantity_available
|
|
500
|
+
: (data.stock_quantity ?? null);
|
|
501
|
+
|
|
502
|
+
const payload: {
|
|
503
|
+
sku: string | null;
|
|
504
|
+
media_id?: string | null;
|
|
505
|
+
unit_amount: number;
|
|
506
|
+
currency: string;
|
|
507
|
+
quantity_available: number | null;
|
|
508
|
+
published?: boolean;
|
|
509
|
+
gateway_price_id_test?: string | null;
|
|
510
|
+
gateway_price_id_live?: string | null;
|
|
511
|
+
value_ids: number[];
|
|
512
|
+
} = {
|
|
513
|
+
sku: data.sku ?? null,
|
|
514
|
+
unit_amount: unitAmount,
|
|
515
|
+
currency: data.currency || 'usd',
|
|
516
|
+
quantity_available: quantityAvailable,
|
|
517
|
+
published: data.published,
|
|
518
|
+
gateway_price_id_test: data.gateway_price_id_test,
|
|
519
|
+
gateway_price_id_live: data.gateway_price_id_live,
|
|
520
|
+
value_ids: data.value_ids,
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
if (Object.prototype.hasOwnProperty.call(data, 'media_id')) {
|
|
524
|
+
if (data.media_id === null) {
|
|
525
|
+
payload.media_id = null;
|
|
526
|
+
} else if (typeof data.media_id === 'string') {
|
|
527
|
+
const trimmed = data.media_id.trim();
|
|
528
|
+
if (!trimmed) {
|
|
529
|
+
throw new Error('createProductSku media_id cannot be empty');
|
|
530
|
+
}
|
|
531
|
+
payload.media_id = trimmed;
|
|
532
|
+
} else if (data.media_id !== undefined) {
|
|
533
|
+
throw new Error('createProductSku media_id must be a string or null');
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return this.create(endpoint, payload);
|
|
506
538
|
}
|
|
507
539
|
}
|
package/src/index.ts
CHANGED
|
@@ -100,6 +100,9 @@ export type {
|
|
|
100
100
|
NewsletterSubscription,
|
|
101
101
|
CreateNewsletterSubscriptionRequest,
|
|
102
102
|
NewsletterList,
|
|
103
|
+
NewsletterCampaignSummary,
|
|
104
|
+
NewsletterCampaignDetail,
|
|
105
|
+
NewsletterCampaignListResponse,
|
|
103
106
|
NewsletterPreferences,
|
|
104
107
|
NewsletterStatusResponse,
|
|
105
108
|
NewsletterSubscribeResponse,
|
package/src/types/index.ts
CHANGED
|
@@ -141,6 +141,37 @@ export interface NewsletterList {
|
|
|
141
141
|
subscriber_count?: number;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
export interface NewsletterCampaignSummary {
|
|
145
|
+
id: string;
|
|
146
|
+
campaign_name: string;
|
|
147
|
+
slug: string;
|
|
148
|
+
slug_prefix?: string | null;
|
|
149
|
+
subject: string;
|
|
150
|
+
preview_text?: string | null;
|
|
151
|
+
status: string;
|
|
152
|
+
sent_at?: string | null;
|
|
153
|
+
completed_at?: string | null;
|
|
154
|
+
created_at: string;
|
|
155
|
+
updated_at: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface NewsletterCampaignDetail extends NewsletterCampaignSummary {
|
|
159
|
+
markdown_content?: string | null;
|
|
160
|
+
html_content?: string | null;
|
|
161
|
+
text_content?: string | null;
|
|
162
|
+
excerpt?: string | null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface NewsletterCampaignListResponse {
|
|
166
|
+
items: NewsletterCampaignSummary[];
|
|
167
|
+
pagination: {
|
|
168
|
+
page: number;
|
|
169
|
+
limit: number;
|
|
170
|
+
total: number;
|
|
171
|
+
pages: number;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
144
175
|
export interface NewsletterPreferences {
|
|
145
176
|
frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
146
177
|
topics?: string[];
|
|
@@ -289,6 +320,49 @@ export interface MediaItem {
|
|
|
289
320
|
site_name: string;
|
|
290
321
|
}
|
|
291
322
|
|
|
323
|
+
export interface ProductSkuMediaItem {
|
|
324
|
+
id?: string;
|
|
325
|
+
media_id: string;
|
|
326
|
+
file_name?: string;
|
|
327
|
+
fileName?: string;
|
|
328
|
+
link?: string;
|
|
329
|
+
url?: string;
|
|
330
|
+
content_type?: string;
|
|
331
|
+
contentType?: string;
|
|
332
|
+
width?: number;
|
|
333
|
+
height?: number;
|
|
334
|
+
filesize?: number;
|
|
335
|
+
r2_key?: string;
|
|
336
|
+
site_name?: string;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface ProductSkuOption {
|
|
340
|
+
name: string;
|
|
341
|
+
key: string;
|
|
342
|
+
value: string;
|
|
343
|
+
label: string;
|
|
344
|
+
value_id: number;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface ProductSku {
|
|
348
|
+
sku_id: number;
|
|
349
|
+
sku?: string | null;
|
|
350
|
+
combination_key: string;
|
|
351
|
+
media_id?: string | null;
|
|
352
|
+
media?: ProductSkuMediaItem | null;
|
|
353
|
+
options?: ProductSkuOption[];
|
|
354
|
+
unit_amount?: number;
|
|
355
|
+
currency?: string;
|
|
356
|
+
quantity_available?: number | null;
|
|
357
|
+
price?: number;
|
|
358
|
+
sale_price?: number;
|
|
359
|
+
stock_quantity?: number;
|
|
360
|
+
value_ids: number[];
|
|
361
|
+
created_at?: string;
|
|
362
|
+
updated_at?: string;
|
|
363
|
+
[key: string]: any;
|
|
364
|
+
}
|
|
365
|
+
|
|
292
366
|
export interface Product {
|
|
293
367
|
id: number | string;
|
|
294
368
|
name?: string;
|
|
@@ -302,6 +376,7 @@ export interface Product {
|
|
|
302
376
|
slug_prefix?: string;
|
|
303
377
|
image?: string;
|
|
304
378
|
media?: MediaItem[] | MediaItem[][];
|
|
379
|
+
skus?: ProductSku[];
|
|
305
380
|
isActive?: boolean;
|
|
306
381
|
organizationId?: number;
|
|
307
382
|
createdAt?: string;
|
|
@@ -331,6 +406,21 @@ export interface CreateProductRequest {
|
|
|
331
406
|
isActive?: boolean;
|
|
332
407
|
}
|
|
333
408
|
|
|
409
|
+
export interface CreateProductSkuRequest {
|
|
410
|
+
sku?: string | null;
|
|
411
|
+
media_id?: string | null;
|
|
412
|
+
unit_amount?: number;
|
|
413
|
+
currency?: string;
|
|
414
|
+
quantity_available?: number | null;
|
|
415
|
+
price?: number | null;
|
|
416
|
+
stock_quantity?: number | null;
|
|
417
|
+
sale_price?: number | null;
|
|
418
|
+
published?: boolean;
|
|
419
|
+
gateway_price_id_test?: string | null;
|
|
420
|
+
gateway_price_id_live?: string | null;
|
|
421
|
+
value_ids: number[];
|
|
422
|
+
}
|
|
423
|
+
|
|
334
424
|
export interface ProductQueryParams extends PaginationParams {
|
|
335
425
|
organizationId?: number;
|
|
336
426
|
isActive?: boolean;
|