perspectapi-ts-sdk 2.5.0 → 2.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 +14 -1
- package/dist/index.d.mts +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.js +80 -5
- package/dist/index.mjs +80 -5
- package/package.json +1 -1
- package/src/client/content-client.ts +81 -2
- package/src/client/products-client.ts +19 -3
- package/src/index.ts +3 -1
- package/src/types/index.ts +18 -0
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ export default {
|
|
|
147
147
|
};
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
When PerspectAPI sends a webhook—or when your Worker mutates data directly—call `perspect.cache.invalidate({ tags: [...] })` using the tags emitted by the SDK (`products:site:<site>`, `content:slug:<site>:<slug>`, etc.) so stale entries are purged immediately.
|
|
150
|
+
When PerspectAPI sends a webhook—or when your Worker mutates data directly—call `perspect.cache.invalidate({ tags: [...] })` using the tags emitted by the SDK (`products:site:<site>`, `content:slug:<site>:<slug>`, `content:category:<site>:<category_slug>`, etc.) so stale entries are purged immediately.
|
|
151
151
|
|
|
152
152
|
### Webhook-driven cache invalidation
|
|
153
153
|
|
|
@@ -200,6 +200,8 @@ const tagMap: Record<string, (e: WebhookEvent) => string[]> = {
|
|
|
200
200
|
`categories`,
|
|
201
201
|
`categories:site:${event.site}`,
|
|
202
202
|
`categories:product:${event.site}:${event.slug}`,
|
|
203
|
+
`products:category:${event.site}:${event.slug}`,
|
|
204
|
+
`content:category:${event.site}:${event.slug}`,
|
|
203
205
|
],
|
|
204
206
|
};
|
|
205
207
|
|
|
@@ -392,6 +394,17 @@ const post = await client.content.getContentById(123);
|
|
|
392
394
|
// Get content by slug
|
|
393
395
|
const page = await client.content.getContentBySlug('your-site-name', 'about-us');
|
|
394
396
|
|
|
397
|
+
// Get content by category slug
|
|
398
|
+
const categoryContent = await client.content.getContentByCategorySlug(
|
|
399
|
+
'your-site-name',
|
|
400
|
+
'news',
|
|
401
|
+
{
|
|
402
|
+
page: 1,
|
|
403
|
+
limit: 20,
|
|
404
|
+
page_type: 'post'
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
|
|
395
408
|
// Create new content
|
|
396
409
|
const newPost = await client.content.createContent({
|
|
397
410
|
page_title: 'My New Post',
|
package/dist/index.d.mts
CHANGED
|
@@ -312,6 +312,22 @@ interface Category {
|
|
|
312
312
|
createdAt: string;
|
|
313
313
|
updatedAt: string;
|
|
314
314
|
}
|
|
315
|
+
interface CategorySummary {
|
|
316
|
+
id: number;
|
|
317
|
+
name: string;
|
|
318
|
+
slug: string;
|
|
319
|
+
description?: string;
|
|
320
|
+
}
|
|
321
|
+
interface ContentCategoryResponse {
|
|
322
|
+
category: CategorySummary;
|
|
323
|
+
items: Content[];
|
|
324
|
+
pagination: {
|
|
325
|
+
page: number;
|
|
326
|
+
limit: number;
|
|
327
|
+
total: number;
|
|
328
|
+
pages: number;
|
|
329
|
+
};
|
|
330
|
+
}
|
|
315
331
|
interface CreateCategoryRequest {
|
|
316
332
|
name: string;
|
|
317
333
|
slug?: string;
|
|
@@ -781,6 +797,10 @@ declare class ContentClient extends BaseClient {
|
|
|
781
797
|
* Get content by ID
|
|
782
798
|
*/
|
|
783
799
|
getContentById(id: number, cachePolicy?: CachePolicy): Promise<ApiResponse<Content>>;
|
|
800
|
+
/**
|
|
801
|
+
* Get content by category slug for a site
|
|
802
|
+
*/
|
|
803
|
+
getContentByCategorySlug(siteName: string, categorySlug: string, params?: ContentQueryParams, cachePolicy?: CachePolicy): Promise<ApiResponse<ContentCategoryResponse>>;
|
|
784
804
|
/**
|
|
785
805
|
* Get content by slug for a site
|
|
786
806
|
*/
|
|
@@ -828,6 +848,11 @@ declare class ContentClient extends BaseClient {
|
|
|
828
848
|
*/
|
|
829
849
|
duplicateContent(id: number): Promise<ApiResponse<Content>>;
|
|
830
850
|
private buildContentTags;
|
|
851
|
+
/**
|
|
852
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
853
|
+
* e.g., 'blog/my-post' -> 'blog', 'about' -> undefined
|
|
854
|
+
*/
|
|
855
|
+
private extractSlugPrefix;
|
|
831
856
|
}
|
|
832
857
|
|
|
833
858
|
/**
|
|
@@ -1226,6 +1251,11 @@ declare class ProductsClient extends BaseClient {
|
|
|
1226
1251
|
};
|
|
1227
1252
|
}>>;
|
|
1228
1253
|
private buildProductTags;
|
|
1254
|
+
/**
|
|
1255
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
1256
|
+
* e.g., 'shop/shoes/nike-air' -> 'shop', 'product-name' -> undefined
|
|
1257
|
+
*/
|
|
1258
|
+
private extractSlugPrefix;
|
|
1229
1259
|
}
|
|
1230
1260
|
|
|
1231
1261
|
/**
|
|
@@ -2442,4 +2472,4 @@ declare function createCheckoutSession(options: CheckoutSessionOptions): Promise
|
|
|
2442
2472
|
error: string;
|
|
2443
2473
|
}>;
|
|
2444
2474
|
|
|
2445
|
-
export { type ApiError, type ApiKey, ApiKeysClient, type ApiResponse, AuthClient, type AuthResponse, BaseClient, type BlogPost, type CacheConfig, CacheManager, CategoriesClient, type Category, type CheckoutAddress, CheckoutClient, type CheckoutMetadata, type CheckoutMetadataValue, type CheckoutSession, type CheckoutSessionOptions, type CheckoutSessionTax, type CheckoutTaxBreakdownItem, type CheckoutTaxCustomerExemptionRequest, type CheckoutTaxExemptionStatus, type CheckoutTaxRequest, type CheckoutTaxStrategy, ContactClient, type ContactStatusResponse, type ContactSubmission, type ContactSubmitResponse, type Content, ContentClient, type ContentQueryParams, type ContentStatus, type ContentType, type CreateApiKeyRequest, type CreateCategoryRequest, type CreateCheckoutSessionRequest, type CreateContactRequest, type CreateContentRequest, type CreateNewsletterSubscriptionRequest, type CreateOrganizationRequest, type CreatePaymentGatewayRequest, type CreateProductRequest, type CreateSiteRequest, type CreateWebhookRequest, DEFAULT_IMAGE_SIZES, HttpClient, type HttpMethod, type ImageTransformOptions, InMemoryCacheAdapter, type LoadContentBySlugOptions, type LoadContentOptions, type LoadProductBySlugOptions, type LoadProductsOptions, type LoaderLogger, type LoaderOptions, type MediaItem, NewsletterClient, type NewsletterConfirmResponse, type NewsletterList, type NewsletterPreferences, type NewsletterStatusResponse, type NewsletterSubscribeResponse, type NewsletterSubscription, type NewsletterUnsubscribeRequest, type NewsletterUnsubscribeResponse, NoopCacheAdapter, type Organization, OrganizationsClient, type PaginatedResponse, type PaginationParams, type PaymentGateway, PerspectApiClient, type PerspectApiConfig, type Product, type ProductQueryParams, ProductsClient, type RequestOptions, type ResponsiveImageSizes, type SignInRequest, type SignUpRequest, type Site, SitesClient, type UpdateApiKeyRequest, type UpdateContentRequest, type User, type Webhook, WebhooksClient, buildImageUrl, createApiError, createCheckoutSession, createPerspectApiClient, PerspectApiClient as default, generateResponsiveImageHtml, generateResponsiveUrls, generateSizesAttribute, generateSrcSet, loadAllContent, loadContentBySlug, loadPages, loadPosts, loadProductBySlug, loadProducts, transformContent, transformMediaItem, transformProduct };
|
|
2475
|
+
export { type ApiError, type ApiKey, ApiKeysClient, type ApiResponse, AuthClient, type AuthResponse, BaseClient, type BlogPost, type CacheConfig, CacheManager, CategoriesClient, type Category, type CategorySummary, type CheckoutAddress, CheckoutClient, type CheckoutMetadata, type CheckoutMetadataValue, type CheckoutSession, type CheckoutSessionOptions, type CheckoutSessionTax, type CheckoutTaxBreakdownItem, type CheckoutTaxCustomerExemptionRequest, type CheckoutTaxExemptionStatus, type CheckoutTaxRequest, type CheckoutTaxStrategy, ContactClient, type ContactStatusResponse, type ContactSubmission, type ContactSubmitResponse, type Content, type ContentCategoryResponse, ContentClient, type ContentQueryParams, type ContentStatus, type ContentType, type CreateApiKeyRequest, type CreateCategoryRequest, type CreateCheckoutSessionRequest, type CreateContactRequest, type CreateContentRequest, type CreateNewsletterSubscriptionRequest, type CreateOrganizationRequest, type CreatePaymentGatewayRequest, type CreateProductRequest, type CreateSiteRequest, type CreateWebhookRequest, DEFAULT_IMAGE_SIZES, HttpClient, type HttpMethod, type ImageTransformOptions, InMemoryCacheAdapter, type LoadContentBySlugOptions, type LoadContentOptions, type LoadProductBySlugOptions, type LoadProductsOptions, type LoaderLogger, type LoaderOptions, type MediaItem, NewsletterClient, type NewsletterConfirmResponse, type NewsletterList, type NewsletterPreferences, type NewsletterStatusResponse, type NewsletterSubscribeResponse, type NewsletterSubscription, type NewsletterUnsubscribeRequest, type NewsletterUnsubscribeResponse, NoopCacheAdapter, type Organization, OrganizationsClient, type PaginatedResponse, type PaginationParams, type PaymentGateway, PerspectApiClient, type PerspectApiConfig, type Product, type ProductQueryParams, ProductsClient, type RequestOptions, type ResponsiveImageSizes, type SignInRequest, type SignUpRequest, type Site, SitesClient, type UpdateApiKeyRequest, type UpdateContentRequest, type User, type Webhook, WebhooksClient, buildImageUrl, createApiError, createCheckoutSession, createPerspectApiClient, PerspectApiClient as default, generateResponsiveImageHtml, generateResponsiveUrls, generateSizesAttribute, generateSrcSet, loadAllContent, loadContentBySlug, loadPages, loadPosts, loadProductBySlug, loadProducts, transformContent, transformMediaItem, transformProduct };
|
package/dist/index.d.ts
CHANGED
|
@@ -312,6 +312,22 @@ interface Category {
|
|
|
312
312
|
createdAt: string;
|
|
313
313
|
updatedAt: string;
|
|
314
314
|
}
|
|
315
|
+
interface CategorySummary {
|
|
316
|
+
id: number;
|
|
317
|
+
name: string;
|
|
318
|
+
slug: string;
|
|
319
|
+
description?: string;
|
|
320
|
+
}
|
|
321
|
+
interface ContentCategoryResponse {
|
|
322
|
+
category: CategorySummary;
|
|
323
|
+
items: Content[];
|
|
324
|
+
pagination: {
|
|
325
|
+
page: number;
|
|
326
|
+
limit: number;
|
|
327
|
+
total: number;
|
|
328
|
+
pages: number;
|
|
329
|
+
};
|
|
330
|
+
}
|
|
315
331
|
interface CreateCategoryRequest {
|
|
316
332
|
name: string;
|
|
317
333
|
slug?: string;
|
|
@@ -781,6 +797,10 @@ declare class ContentClient extends BaseClient {
|
|
|
781
797
|
* Get content by ID
|
|
782
798
|
*/
|
|
783
799
|
getContentById(id: number, cachePolicy?: CachePolicy): Promise<ApiResponse<Content>>;
|
|
800
|
+
/**
|
|
801
|
+
* Get content by category slug for a site
|
|
802
|
+
*/
|
|
803
|
+
getContentByCategorySlug(siteName: string, categorySlug: string, params?: ContentQueryParams, cachePolicy?: CachePolicy): Promise<ApiResponse<ContentCategoryResponse>>;
|
|
784
804
|
/**
|
|
785
805
|
* Get content by slug for a site
|
|
786
806
|
*/
|
|
@@ -828,6 +848,11 @@ declare class ContentClient extends BaseClient {
|
|
|
828
848
|
*/
|
|
829
849
|
duplicateContent(id: number): Promise<ApiResponse<Content>>;
|
|
830
850
|
private buildContentTags;
|
|
851
|
+
/**
|
|
852
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
853
|
+
* e.g., 'blog/my-post' -> 'blog', 'about' -> undefined
|
|
854
|
+
*/
|
|
855
|
+
private extractSlugPrefix;
|
|
831
856
|
}
|
|
832
857
|
|
|
833
858
|
/**
|
|
@@ -1226,6 +1251,11 @@ declare class ProductsClient extends BaseClient {
|
|
|
1226
1251
|
};
|
|
1227
1252
|
}>>;
|
|
1228
1253
|
private buildProductTags;
|
|
1254
|
+
/**
|
|
1255
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
1256
|
+
* e.g., 'shop/shoes/nike-air' -> 'shop', 'product-name' -> undefined
|
|
1257
|
+
*/
|
|
1258
|
+
private extractSlugPrefix;
|
|
1229
1259
|
}
|
|
1230
1260
|
|
|
1231
1261
|
/**
|
|
@@ -2442,4 +2472,4 @@ declare function createCheckoutSession(options: CheckoutSessionOptions): Promise
|
|
|
2442
2472
|
error: string;
|
|
2443
2473
|
}>;
|
|
2444
2474
|
|
|
2445
|
-
export { type ApiError, type ApiKey, ApiKeysClient, type ApiResponse, AuthClient, type AuthResponse, BaseClient, type BlogPost, type CacheConfig, CacheManager, CategoriesClient, type Category, type CheckoutAddress, CheckoutClient, type CheckoutMetadata, type CheckoutMetadataValue, type CheckoutSession, type CheckoutSessionOptions, type CheckoutSessionTax, type CheckoutTaxBreakdownItem, type CheckoutTaxCustomerExemptionRequest, type CheckoutTaxExemptionStatus, type CheckoutTaxRequest, type CheckoutTaxStrategy, ContactClient, type ContactStatusResponse, type ContactSubmission, type ContactSubmitResponse, type Content, ContentClient, type ContentQueryParams, type ContentStatus, type ContentType, type CreateApiKeyRequest, type CreateCategoryRequest, type CreateCheckoutSessionRequest, type CreateContactRequest, type CreateContentRequest, type CreateNewsletterSubscriptionRequest, type CreateOrganizationRequest, type CreatePaymentGatewayRequest, type CreateProductRequest, type CreateSiteRequest, type CreateWebhookRequest, DEFAULT_IMAGE_SIZES, HttpClient, type HttpMethod, type ImageTransformOptions, InMemoryCacheAdapter, type LoadContentBySlugOptions, type LoadContentOptions, type LoadProductBySlugOptions, type LoadProductsOptions, type LoaderLogger, type LoaderOptions, type MediaItem, NewsletterClient, type NewsletterConfirmResponse, type NewsletterList, type NewsletterPreferences, type NewsletterStatusResponse, type NewsletterSubscribeResponse, type NewsletterSubscription, type NewsletterUnsubscribeRequest, type NewsletterUnsubscribeResponse, NoopCacheAdapter, type Organization, OrganizationsClient, type PaginatedResponse, type PaginationParams, type PaymentGateway, PerspectApiClient, type PerspectApiConfig, type Product, type ProductQueryParams, ProductsClient, type RequestOptions, type ResponsiveImageSizes, type SignInRequest, type SignUpRequest, type Site, SitesClient, type UpdateApiKeyRequest, type UpdateContentRequest, type User, type Webhook, WebhooksClient, buildImageUrl, createApiError, createCheckoutSession, createPerspectApiClient, PerspectApiClient as default, generateResponsiveImageHtml, generateResponsiveUrls, generateSizesAttribute, generateSrcSet, loadAllContent, loadContentBySlug, loadPages, loadPosts, loadProductBySlug, loadProducts, transformContent, transformMediaItem, transformProduct };
|
|
2475
|
+
export { type ApiError, type ApiKey, ApiKeysClient, type ApiResponse, AuthClient, type AuthResponse, BaseClient, type BlogPost, type CacheConfig, CacheManager, CategoriesClient, type Category, type CategorySummary, type CheckoutAddress, CheckoutClient, type CheckoutMetadata, type CheckoutMetadataValue, type CheckoutSession, type CheckoutSessionOptions, type CheckoutSessionTax, type CheckoutTaxBreakdownItem, type CheckoutTaxCustomerExemptionRequest, type CheckoutTaxExemptionStatus, type CheckoutTaxRequest, type CheckoutTaxStrategy, ContactClient, type ContactStatusResponse, type ContactSubmission, type ContactSubmitResponse, type Content, type ContentCategoryResponse, ContentClient, type ContentQueryParams, type ContentStatus, type ContentType, type CreateApiKeyRequest, type CreateCategoryRequest, type CreateCheckoutSessionRequest, type CreateContactRequest, type CreateContentRequest, type CreateNewsletterSubscriptionRequest, type CreateOrganizationRequest, type CreatePaymentGatewayRequest, type CreateProductRequest, type CreateSiteRequest, type CreateWebhookRequest, DEFAULT_IMAGE_SIZES, HttpClient, type HttpMethod, type ImageTransformOptions, InMemoryCacheAdapter, type LoadContentBySlugOptions, type LoadContentOptions, type LoadProductBySlugOptions, type LoadProductsOptions, type LoaderLogger, type LoaderOptions, type MediaItem, NewsletterClient, type NewsletterConfirmResponse, type NewsletterList, type NewsletterPreferences, type NewsletterStatusResponse, type NewsletterSubscribeResponse, type NewsletterSubscription, type NewsletterUnsubscribeRequest, type NewsletterUnsubscribeResponse, NoopCacheAdapter, type Organization, OrganizationsClient, type PaginatedResponse, type PaginationParams, type PaymentGateway, PerspectApiClient, type PerspectApiConfig, type Product, type ProductQueryParams, ProductsClient, type RequestOptions, type ResponsiveImageSizes, type SignInRequest, type SignUpRequest, type Site, SitesClient, type UpdateApiKeyRequest, type UpdateContentRequest, type User, type Webhook, WebhooksClient, buildImageUrl, createApiError, createCheckoutSession, createPerspectApiClient, PerspectApiClient as default, generateResponsiveImageHtml, generateResponsiveUrls, generateSizesAttribute, generateSrcSet, loadAllContent, loadContentBySlug, loadPages, loadPosts, loadProductBySlug, loadProducts, transformContent, transformMediaItem, transformProduct };
|
package/dist/index.js
CHANGED
|
@@ -902,16 +902,61 @@ var ContentClient = class extends BaseClient {
|
|
|
902
902
|
() => this.http.get(path)
|
|
903
903
|
);
|
|
904
904
|
}
|
|
905
|
+
/**
|
|
906
|
+
* Get content by category slug for a site
|
|
907
|
+
*/
|
|
908
|
+
async getContentByCategorySlug(siteName, categorySlug, params, cachePolicy) {
|
|
909
|
+
const endpoint = this.siteScopedEndpoint(
|
|
910
|
+
siteName,
|
|
911
|
+
`/category/${encodeURIComponent(categorySlug)}`
|
|
912
|
+
);
|
|
913
|
+
const path = this.buildPath(endpoint);
|
|
914
|
+
const normalizedParams = params ? { ...params } : void 0;
|
|
915
|
+
if (normalizedParams) {
|
|
916
|
+
const validatedLimit = validateOptionalLimit(
|
|
917
|
+
normalizedParams.limit,
|
|
918
|
+
"content category query"
|
|
919
|
+
);
|
|
920
|
+
if (validatedLimit !== void 0) {
|
|
921
|
+
normalizedParams.limit = validatedLimit;
|
|
922
|
+
} else {
|
|
923
|
+
delete normalizedParams.limit;
|
|
924
|
+
}
|
|
925
|
+
const validatedPageType = validateOptionalContentType(
|
|
926
|
+
normalizedParams.page_type,
|
|
927
|
+
"content category query"
|
|
928
|
+
);
|
|
929
|
+
if (validatedPageType !== void 0) {
|
|
930
|
+
normalizedParams.page_type = validatedPageType;
|
|
931
|
+
} else {
|
|
932
|
+
delete normalizedParams.page_type;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return this.fetchWithCache(
|
|
936
|
+
endpoint,
|
|
937
|
+
normalizedParams,
|
|
938
|
+
this.buildContentTags(
|
|
939
|
+
siteName,
|
|
940
|
+
void 0,
|
|
941
|
+
void 0,
|
|
942
|
+
normalizedParams?.slug_prefix,
|
|
943
|
+
categorySlug
|
|
944
|
+
),
|
|
945
|
+
cachePolicy,
|
|
946
|
+
() => this.http.get(path, normalizedParams)
|
|
947
|
+
);
|
|
948
|
+
}
|
|
905
949
|
/**
|
|
906
950
|
* Get content by slug for a site
|
|
907
951
|
*/
|
|
908
952
|
async getContentBySlug(siteName, slug, cachePolicy) {
|
|
909
953
|
const endpoint = this.siteScopedEndpoint(siteName, `/slug/${encodeURIComponent(slug)}`);
|
|
910
954
|
const path = this.buildPath(endpoint);
|
|
955
|
+
const slugPrefix = this.extractSlugPrefix(slug);
|
|
911
956
|
return this.fetchWithCache(
|
|
912
957
|
endpoint,
|
|
913
958
|
void 0,
|
|
914
|
-
this.buildContentTags(siteName, slug),
|
|
959
|
+
this.buildContentTags(siteName, slug, void 0, slugPrefix),
|
|
915
960
|
cachePolicy,
|
|
916
961
|
() => this.http.get(path)
|
|
917
962
|
);
|
|
@@ -976,7 +1021,7 @@ var ContentClient = class extends BaseClient {
|
|
|
976
1021
|
async duplicateContent(id) {
|
|
977
1022
|
return this.create(`/content/${id}/duplicate`, {});
|
|
978
1023
|
}
|
|
979
|
-
buildContentTags(siteName, slug, id, slugPrefix) {
|
|
1024
|
+
buildContentTags(siteName, slug, id, slugPrefix, categorySlug) {
|
|
980
1025
|
const tags = /* @__PURE__ */ new Set(["content"]);
|
|
981
1026
|
if (siteName) {
|
|
982
1027
|
tags.add(`content:site:${siteName}`);
|
|
@@ -990,8 +1035,23 @@ var ContentClient = class extends BaseClient {
|
|
|
990
1035
|
if (slugPrefix) {
|
|
991
1036
|
tags.add(`content:prefix:${slugPrefix}`);
|
|
992
1037
|
}
|
|
1038
|
+
if (categorySlug && siteName) {
|
|
1039
|
+
tags.add("content:category");
|
|
1040
|
+
tags.add(`content:category:${siteName}:${categorySlug}`);
|
|
1041
|
+
}
|
|
993
1042
|
return Array.from(tags.values());
|
|
994
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
1046
|
+
* e.g., 'blog/my-post' -> 'blog', 'about' -> undefined
|
|
1047
|
+
*/
|
|
1048
|
+
extractSlugPrefix(slug) {
|
|
1049
|
+
const slashIndex = slug.indexOf("/");
|
|
1050
|
+
if (slashIndex > 0) {
|
|
1051
|
+
return slug.substring(0, slashIndex);
|
|
1052
|
+
}
|
|
1053
|
+
return void 0;
|
|
1054
|
+
}
|
|
995
1055
|
};
|
|
996
1056
|
|
|
997
1057
|
// src/client/api-keys-client.ts
|
|
@@ -1277,7 +1337,7 @@ var ProductsClient = class extends BaseClient {
|
|
|
1277
1337
|
return this.fetchWithCache(
|
|
1278
1338
|
endpoint,
|
|
1279
1339
|
normalizedParams,
|
|
1280
|
-
this.buildProductTags(siteName, ["products:list"]),
|
|
1340
|
+
this.buildProductTags(siteName, ["products:list"], normalizedParams?.slug_prefix),
|
|
1281
1341
|
cachePolicy,
|
|
1282
1342
|
() => this.http.get(path, normalizedParams)
|
|
1283
1343
|
);
|
|
@@ -1320,10 +1380,11 @@ var ProductsClient = class extends BaseClient {
|
|
|
1320
1380
|
{ includeSitesSegment: false }
|
|
1321
1381
|
);
|
|
1322
1382
|
const path = this.buildPath(endpoint);
|
|
1383
|
+
const slugPrefix = this.extractSlugPrefix(slug);
|
|
1323
1384
|
return this.fetchWithCache(
|
|
1324
1385
|
endpoint,
|
|
1325
1386
|
void 0,
|
|
1326
|
-
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`]),
|
|
1387
|
+
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`], slugPrefix),
|
|
1327
1388
|
cachePolicy,
|
|
1328
1389
|
() => this.http.get(path)
|
|
1329
1390
|
);
|
|
@@ -1439,14 +1500,28 @@ var ProductsClient = class extends BaseClient {
|
|
|
1439
1500
|
() => this.http.get(path, queryParams)
|
|
1440
1501
|
);
|
|
1441
1502
|
}
|
|
1442
|
-
buildProductTags(siteName, extraTags = []) {
|
|
1503
|
+
buildProductTags(siteName, extraTags = [], slugPrefix) {
|
|
1443
1504
|
const tags = /* @__PURE__ */ new Set(["products"]);
|
|
1444
1505
|
if (siteName) {
|
|
1445
1506
|
tags.add(`products:site:${siteName}`);
|
|
1446
1507
|
}
|
|
1508
|
+
if (slugPrefix) {
|
|
1509
|
+
tags.add(`products:prefix:${slugPrefix}`);
|
|
1510
|
+
}
|
|
1447
1511
|
extraTags.filter(Boolean).forEach((tag) => tags.add(tag));
|
|
1448
1512
|
return Array.from(tags.values());
|
|
1449
1513
|
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
1516
|
+
* e.g., 'shop/shoes/nike-air' -> 'shop', 'product-name' -> undefined
|
|
1517
|
+
*/
|
|
1518
|
+
extractSlugPrefix(slug) {
|
|
1519
|
+
const slashIndex = slug.indexOf("/");
|
|
1520
|
+
if (slashIndex > 0) {
|
|
1521
|
+
return slug.substring(0, slashIndex);
|
|
1522
|
+
}
|
|
1523
|
+
return void 0;
|
|
1524
|
+
}
|
|
1450
1525
|
};
|
|
1451
1526
|
|
|
1452
1527
|
// src/client/categories-client.ts
|
package/dist/index.mjs
CHANGED
|
@@ -841,16 +841,61 @@ var ContentClient = class extends BaseClient {
|
|
|
841
841
|
() => this.http.get(path)
|
|
842
842
|
);
|
|
843
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* Get content by category slug for a site
|
|
846
|
+
*/
|
|
847
|
+
async getContentByCategorySlug(siteName, categorySlug, params, cachePolicy) {
|
|
848
|
+
const endpoint = this.siteScopedEndpoint(
|
|
849
|
+
siteName,
|
|
850
|
+
`/category/${encodeURIComponent(categorySlug)}`
|
|
851
|
+
);
|
|
852
|
+
const path = this.buildPath(endpoint);
|
|
853
|
+
const normalizedParams = params ? { ...params } : void 0;
|
|
854
|
+
if (normalizedParams) {
|
|
855
|
+
const validatedLimit = validateOptionalLimit(
|
|
856
|
+
normalizedParams.limit,
|
|
857
|
+
"content category query"
|
|
858
|
+
);
|
|
859
|
+
if (validatedLimit !== void 0) {
|
|
860
|
+
normalizedParams.limit = validatedLimit;
|
|
861
|
+
} else {
|
|
862
|
+
delete normalizedParams.limit;
|
|
863
|
+
}
|
|
864
|
+
const validatedPageType = validateOptionalContentType(
|
|
865
|
+
normalizedParams.page_type,
|
|
866
|
+
"content category query"
|
|
867
|
+
);
|
|
868
|
+
if (validatedPageType !== void 0) {
|
|
869
|
+
normalizedParams.page_type = validatedPageType;
|
|
870
|
+
} else {
|
|
871
|
+
delete normalizedParams.page_type;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return this.fetchWithCache(
|
|
875
|
+
endpoint,
|
|
876
|
+
normalizedParams,
|
|
877
|
+
this.buildContentTags(
|
|
878
|
+
siteName,
|
|
879
|
+
void 0,
|
|
880
|
+
void 0,
|
|
881
|
+
normalizedParams?.slug_prefix,
|
|
882
|
+
categorySlug
|
|
883
|
+
),
|
|
884
|
+
cachePolicy,
|
|
885
|
+
() => this.http.get(path, normalizedParams)
|
|
886
|
+
);
|
|
887
|
+
}
|
|
844
888
|
/**
|
|
845
889
|
* Get content by slug for a site
|
|
846
890
|
*/
|
|
847
891
|
async getContentBySlug(siteName, slug, cachePolicy) {
|
|
848
892
|
const endpoint = this.siteScopedEndpoint(siteName, `/slug/${encodeURIComponent(slug)}`);
|
|
849
893
|
const path = this.buildPath(endpoint);
|
|
894
|
+
const slugPrefix = this.extractSlugPrefix(slug);
|
|
850
895
|
return this.fetchWithCache(
|
|
851
896
|
endpoint,
|
|
852
897
|
void 0,
|
|
853
|
-
this.buildContentTags(siteName, slug),
|
|
898
|
+
this.buildContentTags(siteName, slug, void 0, slugPrefix),
|
|
854
899
|
cachePolicy,
|
|
855
900
|
() => this.http.get(path)
|
|
856
901
|
);
|
|
@@ -915,7 +960,7 @@ var ContentClient = class extends BaseClient {
|
|
|
915
960
|
async duplicateContent(id) {
|
|
916
961
|
return this.create(`/content/${id}/duplicate`, {});
|
|
917
962
|
}
|
|
918
|
-
buildContentTags(siteName, slug, id, slugPrefix) {
|
|
963
|
+
buildContentTags(siteName, slug, id, slugPrefix, categorySlug) {
|
|
919
964
|
const tags = /* @__PURE__ */ new Set(["content"]);
|
|
920
965
|
if (siteName) {
|
|
921
966
|
tags.add(`content:site:${siteName}`);
|
|
@@ -929,8 +974,23 @@ var ContentClient = class extends BaseClient {
|
|
|
929
974
|
if (slugPrefix) {
|
|
930
975
|
tags.add(`content:prefix:${slugPrefix}`);
|
|
931
976
|
}
|
|
977
|
+
if (categorySlug && siteName) {
|
|
978
|
+
tags.add("content:category");
|
|
979
|
+
tags.add(`content:category:${siteName}:${categorySlug}`);
|
|
980
|
+
}
|
|
932
981
|
return Array.from(tags.values());
|
|
933
982
|
}
|
|
983
|
+
/**
|
|
984
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
985
|
+
* e.g., 'blog/my-post' -> 'blog', 'about' -> undefined
|
|
986
|
+
*/
|
|
987
|
+
extractSlugPrefix(slug) {
|
|
988
|
+
const slashIndex = slug.indexOf("/");
|
|
989
|
+
if (slashIndex > 0) {
|
|
990
|
+
return slug.substring(0, slashIndex);
|
|
991
|
+
}
|
|
992
|
+
return void 0;
|
|
993
|
+
}
|
|
934
994
|
};
|
|
935
995
|
|
|
936
996
|
// src/client/api-keys-client.ts
|
|
@@ -1216,7 +1276,7 @@ var ProductsClient = class extends BaseClient {
|
|
|
1216
1276
|
return this.fetchWithCache(
|
|
1217
1277
|
endpoint,
|
|
1218
1278
|
normalizedParams,
|
|
1219
|
-
this.buildProductTags(siteName, ["products:list"]),
|
|
1279
|
+
this.buildProductTags(siteName, ["products:list"], normalizedParams?.slug_prefix),
|
|
1220
1280
|
cachePolicy,
|
|
1221
1281
|
() => this.http.get(path, normalizedParams)
|
|
1222
1282
|
);
|
|
@@ -1259,10 +1319,11 @@ var ProductsClient = class extends BaseClient {
|
|
|
1259
1319
|
{ includeSitesSegment: false }
|
|
1260
1320
|
);
|
|
1261
1321
|
const path = this.buildPath(endpoint);
|
|
1322
|
+
const slugPrefix = this.extractSlugPrefix(slug);
|
|
1262
1323
|
return this.fetchWithCache(
|
|
1263
1324
|
endpoint,
|
|
1264
1325
|
void 0,
|
|
1265
|
-
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`]),
|
|
1326
|
+
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`], slugPrefix),
|
|
1266
1327
|
cachePolicy,
|
|
1267
1328
|
() => this.http.get(path)
|
|
1268
1329
|
);
|
|
@@ -1378,14 +1439,28 @@ var ProductsClient = class extends BaseClient {
|
|
|
1378
1439
|
() => this.http.get(path, queryParams)
|
|
1379
1440
|
);
|
|
1380
1441
|
}
|
|
1381
|
-
buildProductTags(siteName, extraTags = []) {
|
|
1442
|
+
buildProductTags(siteName, extraTags = [], slugPrefix) {
|
|
1382
1443
|
const tags = /* @__PURE__ */ new Set(["products"]);
|
|
1383
1444
|
if (siteName) {
|
|
1384
1445
|
tags.add(`products:site:${siteName}`);
|
|
1385
1446
|
}
|
|
1447
|
+
if (slugPrefix) {
|
|
1448
|
+
tags.add(`products:prefix:${slugPrefix}`);
|
|
1449
|
+
}
|
|
1386
1450
|
extraTags.filter(Boolean).forEach((tag) => tags.add(tag));
|
|
1387
1451
|
return Array.from(tags.values());
|
|
1388
1452
|
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
1455
|
+
* e.g., 'shop/shoes/nike-air' -> 'shop', 'product-name' -> undefined
|
|
1456
|
+
*/
|
|
1457
|
+
extractSlugPrefix(slug) {
|
|
1458
|
+
const slashIndex = slug.indexOf("/");
|
|
1459
|
+
if (slashIndex > 0) {
|
|
1460
|
+
return slug.substring(0, slashIndex);
|
|
1461
|
+
}
|
|
1462
|
+
return void 0;
|
|
1463
|
+
}
|
|
1389
1464
|
};
|
|
1390
1465
|
|
|
1391
1466
|
// src/client/categories-client.ts
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
ContentQueryParams,
|
|
13
13
|
PaginatedResponse,
|
|
14
14
|
ApiResponse,
|
|
15
|
+
ContentCategoryResponse,
|
|
15
16
|
} from '../types';
|
|
16
17
|
import { validateOptionalContentType, validateOptionalLimit } from '../utils/validators';
|
|
17
18
|
|
|
@@ -139,6 +140,61 @@ export class ContentClient extends BaseClient {
|
|
|
139
140
|
);
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Get content by category slug for a site
|
|
145
|
+
*/
|
|
146
|
+
async getContentByCategorySlug(
|
|
147
|
+
siteName: string,
|
|
148
|
+
categorySlug: string,
|
|
149
|
+
params?: ContentQueryParams,
|
|
150
|
+
cachePolicy?: CachePolicy
|
|
151
|
+
): Promise<ApiResponse<ContentCategoryResponse>> {
|
|
152
|
+
const endpoint = this.siteScopedEndpoint(
|
|
153
|
+
siteName,
|
|
154
|
+
`/category/${encodeURIComponent(categorySlug)}`
|
|
155
|
+
);
|
|
156
|
+
const path = this.buildPath(endpoint);
|
|
157
|
+
const normalizedParams: ContentQueryParams | undefined = params
|
|
158
|
+
? { ...params }
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
if (normalizedParams) {
|
|
162
|
+
const validatedLimit = validateOptionalLimit(
|
|
163
|
+
normalizedParams.limit,
|
|
164
|
+
'content category query',
|
|
165
|
+
);
|
|
166
|
+
if (validatedLimit !== undefined) {
|
|
167
|
+
normalizedParams.limit = validatedLimit;
|
|
168
|
+
} else {
|
|
169
|
+
delete normalizedParams.limit;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const validatedPageType = validateOptionalContentType(
|
|
173
|
+
normalizedParams.page_type,
|
|
174
|
+
'content category query',
|
|
175
|
+
);
|
|
176
|
+
if (validatedPageType !== undefined) {
|
|
177
|
+
normalizedParams.page_type = validatedPageType;
|
|
178
|
+
} else {
|
|
179
|
+
delete normalizedParams.page_type;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return this.fetchWithCache<ApiResponse<ContentCategoryResponse>>(
|
|
184
|
+
endpoint,
|
|
185
|
+
normalizedParams,
|
|
186
|
+
this.buildContentTags(
|
|
187
|
+
siteName,
|
|
188
|
+
undefined,
|
|
189
|
+
undefined,
|
|
190
|
+
normalizedParams?.slug_prefix,
|
|
191
|
+
categorySlug
|
|
192
|
+
),
|
|
193
|
+
cachePolicy,
|
|
194
|
+
() => this.http.get<ContentCategoryResponse>(path, normalizedParams)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
142
198
|
/**
|
|
143
199
|
* Get content by slug for a site
|
|
144
200
|
*/
|
|
@@ -149,11 +205,12 @@ export class ContentClient extends BaseClient {
|
|
|
149
205
|
): Promise<ApiResponse<Content>> {
|
|
150
206
|
const endpoint = this.siteScopedEndpoint(siteName, `/slug/${encodeURIComponent(slug)}`);
|
|
151
207
|
const path = this.buildPath(endpoint);
|
|
208
|
+
const slugPrefix = this.extractSlugPrefix(slug);
|
|
152
209
|
|
|
153
210
|
return this.fetchWithCache<ApiResponse<Content>>(
|
|
154
211
|
endpoint,
|
|
155
212
|
undefined,
|
|
156
|
-
this.buildContentTags(siteName, slug),
|
|
213
|
+
this.buildContentTags(siteName, slug, undefined, slugPrefix),
|
|
157
214
|
cachePolicy,
|
|
158
215
|
() => this.http.get<Content>(path)
|
|
159
216
|
);
|
|
@@ -229,7 +286,13 @@ export class ContentClient extends BaseClient {
|
|
|
229
286
|
return this.create<Record<string, never>, Content>(`/content/${id}/duplicate`, {});
|
|
230
287
|
}
|
|
231
288
|
|
|
232
|
-
private buildContentTags(
|
|
289
|
+
private buildContentTags(
|
|
290
|
+
siteName?: string,
|
|
291
|
+
slug?: string,
|
|
292
|
+
id?: number,
|
|
293
|
+
slugPrefix?: string,
|
|
294
|
+
categorySlug?: string
|
|
295
|
+
): string[] {
|
|
233
296
|
const tags = new Set<string>(['content']);
|
|
234
297
|
if (siteName) {
|
|
235
298
|
tags.add(`content:site:${siteName}`);
|
|
@@ -243,6 +306,22 @@ export class ContentClient extends BaseClient {
|
|
|
243
306
|
if (slugPrefix) {
|
|
244
307
|
tags.add(`content:prefix:${slugPrefix}`);
|
|
245
308
|
}
|
|
309
|
+
if (categorySlug && siteName) {
|
|
310
|
+
tags.add('content:category');
|
|
311
|
+
tags.add(`content:category:${siteName}:${categorySlug}`);
|
|
312
|
+
}
|
|
246
313
|
return Array.from(tags.values());
|
|
247
314
|
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
318
|
+
* e.g., 'blog/my-post' -> 'blog', 'about' -> undefined
|
|
319
|
+
*/
|
|
320
|
+
private extractSlugPrefix(slug: string): string | undefined {
|
|
321
|
+
const slashIndex = slug.indexOf('/');
|
|
322
|
+
if (slashIndex > 0) {
|
|
323
|
+
return slug.substring(0, slashIndex);
|
|
324
|
+
}
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
248
327
|
}
|
|
@@ -73,7 +73,7 @@ export class ProductsClient extends BaseClient {
|
|
|
73
73
|
return this.fetchWithCache<PaginatedResponse<Product>>(
|
|
74
74
|
endpoint,
|
|
75
75
|
normalizedParams,
|
|
76
|
-
this.buildProductTags(siteName, ['products:list']),
|
|
76
|
+
this.buildProductTags(siteName, ['products:list'], normalizedParams?.slug_prefix),
|
|
77
77
|
cachePolicy,
|
|
78
78
|
() => this.http.get(path, normalizedParams) as Promise<PaginatedResponse<Product>>
|
|
79
79
|
);
|
|
@@ -125,11 +125,12 @@ export class ProductsClient extends BaseClient {
|
|
|
125
125
|
{ includeSitesSegment: false }
|
|
126
126
|
);
|
|
127
127
|
const path = this.buildPath(endpoint);
|
|
128
|
+
const slugPrefix = this.extractSlugPrefix(slug);
|
|
128
129
|
|
|
129
130
|
return this.fetchWithCache<ApiResponse<Product & { variants?: any[] }>>(
|
|
130
131
|
endpoint,
|
|
131
132
|
undefined,
|
|
132
|
-
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`]),
|
|
133
|
+
this.buildProductTags(siteName, [`products:slug:${siteName}:${slug}`], slugPrefix),
|
|
133
134
|
cachePolicy,
|
|
134
135
|
() => this.http.get<Product & { variants?: any[] }>(path)
|
|
135
136
|
);
|
|
@@ -347,12 +348,27 @@ export class ProductsClient extends BaseClient {
|
|
|
347
348
|
);
|
|
348
349
|
}
|
|
349
350
|
|
|
350
|
-
private buildProductTags(siteName?: string, extraTags: string[] = []): string[] {
|
|
351
|
+
private buildProductTags(siteName?: string, extraTags: string[] = [], slugPrefix?: string): string[] {
|
|
351
352
|
const tags = new Set<string>(['products']);
|
|
352
353
|
if (siteName) {
|
|
353
354
|
tags.add(`products:site:${siteName}`);
|
|
354
355
|
}
|
|
356
|
+
if (slugPrefix) {
|
|
357
|
+
tags.add(`products:prefix:${slugPrefix}`);
|
|
358
|
+
}
|
|
355
359
|
extraTags.filter(Boolean).forEach(tag => tags.add(tag));
|
|
356
360
|
return Array.from(tags.values());
|
|
357
361
|
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Extract the prefix from a slug (first segment before '/')
|
|
365
|
+
* e.g., 'shop/shoes/nike-air' -> 'shop', 'product-name' -> undefined
|
|
366
|
+
*/
|
|
367
|
+
private extractSlugPrefix(slug: string): string | undefined {
|
|
368
|
+
const slashIndex = slug.indexOf('/');
|
|
369
|
+
if (slashIndex > 0) {
|
|
370
|
+
return slug.substring(0, slashIndex);
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
358
374
|
}
|
package/src/index.ts
CHANGED
|
@@ -80,12 +80,14 @@ export type {
|
|
|
80
80
|
PaginatedResponse,
|
|
81
81
|
ApiError,
|
|
82
82
|
User,
|
|
83
|
-
Content,
|
|
83
|
+
Content,
|
|
84
|
+
ContentCategoryResponse,
|
|
84
85
|
Product,
|
|
85
86
|
ProductQueryParams,
|
|
86
87
|
MediaItem,
|
|
87
88
|
BlogPost,
|
|
88
89
|
Category,
|
|
90
|
+
CategorySummary,
|
|
89
91
|
Organization,
|
|
90
92
|
Site,
|
|
91
93
|
ApiKey,
|
package/src/types/index.ts
CHANGED
|
@@ -268,6 +268,24 @@ export interface Category {
|
|
|
268
268
|
updatedAt: string;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
export interface CategorySummary {
|
|
272
|
+
id: number;
|
|
273
|
+
name: string;
|
|
274
|
+
slug: string;
|
|
275
|
+
description?: string;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export interface ContentCategoryResponse {
|
|
279
|
+
category: CategorySummary;
|
|
280
|
+
items: Content[];
|
|
281
|
+
pagination: {
|
|
282
|
+
page: number;
|
|
283
|
+
limit: number;
|
|
284
|
+
total: number;
|
|
285
|
+
pages: number;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
271
289
|
export interface CreateCategoryRequest {
|
|
272
290
|
name: string;
|
|
273
291
|
slug?: string;
|