perspectapi-ts-sdk 2.8.3 → 2.9.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 CHANGED
@@ -1411,95 +1411,66 @@ declare class ProductsClient extends BaseClient {
1411
1411
  declare class CategoriesClient extends BaseClient {
1412
1412
  constructor(http: any, cache?: CacheManager);
1413
1413
  /**
1414
- * Get all categories
1414
+ * Get all categories for a site
1415
1415
  */
1416
- getCategories(params?: {
1417
- page?: number;
1418
- limit?: number;
1419
- parentId?: number;
1420
- organizationId?: number;
1421
- }): Promise<PaginatedResponse<Category>>;
1422
- /**
1423
- * Get category by ID
1424
- */
1425
- getCategoryById(id: number): Promise<ApiResponse<Category>>;
1426
- /**
1427
- * Get category by slug
1428
- */
1429
- getCategoryBySlug(slug: string): Promise<ApiResponse<Category>>;
1430
- /**
1431
- * Get product category by slug (for products)
1432
- */
1433
- getProductCategoryBySlug(siteName: string, slug: string, cachePolicy?: CachePolicy): Promise<ApiResponse<Category>>;
1416
+ getCategories(siteName: string, params?: {
1417
+ category_type?: 'post' | 'product';
1418
+ parent_id?: string;
1419
+ include_subcategories?: 'true' | 'false';
1420
+ }, cachePolicy?: CachePolicy): Promise<ApiResponse<{
1421
+ categories: Category[];
1422
+ }>>;
1434
1423
  /**
1435
- * Create new category
1424
+ * Get category by ID (validates it belongs to the site)
1436
1425
  */
1437
- createCategory(data: CreateCategoryRequest): Promise<ApiResponse<Category>>;
1426
+ getCategoryById(siteName: string, id: number, cachePolicy?: CachePolicy): Promise<ApiResponse<{
1427
+ category: Category;
1428
+ }>>;
1438
1429
  /**
1439
- * Update category
1430
+ * Get product categories for a site
1440
1431
  */
1441
- updateCategory(id: number, data: Partial<CreateCategoryRequest>): Promise<ApiResponse<Category>>;
1432
+ getProductCategories(siteName: string, params?: {
1433
+ parent_id?: string;
1434
+ include_subcategories?: 'true' | 'false';
1435
+ }, cachePolicy?: CachePolicy): Promise<ApiResponse<{
1436
+ categories: Category[];
1437
+ }>>;
1442
1438
  /**
1443
- * Delete category
1439
+ * Create new category for a site
1444
1440
  */
1445
- deleteCategory(id: number): Promise<ApiResponse<{
1441
+ createCategory(siteName: string, data: CreateCategoryRequest, csrfToken?: string): Promise<ApiResponse<{
1446
1442
  message: string;
1443
+ category_id: number;
1447
1444
  }>>;
1448
1445
  /**
1449
- * Get category tree (hierarchical structure)
1450
- */
1451
- getCategoryTree(rootId?: number): Promise<ApiResponse<Array<Category & {
1452
- children: Category[];
1453
- }>>>;
1454
- /**
1455
- * Get category children
1456
- */
1457
- getCategoryChildren(id: number): Promise<ApiResponse<Category[]>>;
1458
- /**
1459
- * Get category parent
1460
- */
1461
- getCategoryParent(id: number): Promise<ApiResponse<Category | null>>;
1462
- /**
1463
- * Move category to new parent
1464
- */
1465
- moveCategoryToParent(id: number, parentId: number | null): Promise<ApiResponse<Category>>;
1466
- /**
1467
- * Get category breadcrumb path
1446
+ * Create new product category for a site
1468
1447
  */
1469
- getCategoryBreadcrumb(id: number): Promise<ApiResponse<Array<{
1470
- id: number;
1471
- name: string;
1472
- slug: string;
1473
- }>>>;
1448
+ createProductCategory(siteName: string, data: Omit<CreateCategoryRequest, 'category_type'>, csrfToken?: string): Promise<ApiResponse<{
1449
+ message: string;
1450
+ category_id: number;
1451
+ }>>;
1474
1452
  /**
1475
- * Get category content/products
1453
+ * Update category (validates it belongs to the site)
1476
1454
  */
1477
- getCategoryContent(id: number, params?: {
1478
- page?: number;
1479
- limit?: number;
1480
- type?: 'content' | 'products' | 'all';
1481
- }): Promise<ApiResponse<{
1482
- content: any[];
1483
- products: any[];
1484
- total: number;
1455
+ updateCategory(siteName: string, id: number, data: Partial<CreateCategoryRequest>, csrfToken?: string): Promise<ApiResponse<{
1456
+ message: string;
1485
1457
  }>>;
1486
1458
  /**
1487
- * Bulk update category order
1459
+ * Delete category (validates it belongs to the site)
1488
1460
  */
1489
- updateCategoryOrder(updates: Array<{
1490
- id: number;
1491
- order: number;
1492
- parentId?: number;
1493
- }>): Promise<ApiResponse<{
1461
+ deleteCategory(siteName: string, id: number, csrfToken?: string): Promise<ApiResponse<{
1494
1462
  message: string;
1495
1463
  }>>;
1496
1464
  /**
1497
- * Search categories
1465
+ * Associate pages or products with categories
1498
1466
  */
1499
- searchCategories(query: string, params?: {
1500
- limit?: number;
1501
- organizationId?: number;
1502
- }): Promise<ApiResponse<Category[]>>;
1467
+ associateCategories(siteName: string, data: {
1468
+ entity_id: number;
1469
+ entity_type: 'page' | 'product';
1470
+ category_ids: number[];
1471
+ }, csrfToken?: string): Promise<ApiResponse<{
1472
+ message: string;
1473
+ }>>;
1503
1474
  private buildCategoryTags;
1504
1475
  }
1505
1476
 
package/dist/index.d.ts CHANGED
@@ -1411,95 +1411,66 @@ declare class ProductsClient extends BaseClient {
1411
1411
  declare class CategoriesClient extends BaseClient {
1412
1412
  constructor(http: any, cache?: CacheManager);
1413
1413
  /**
1414
- * Get all categories
1414
+ * Get all categories for a site
1415
1415
  */
1416
- getCategories(params?: {
1417
- page?: number;
1418
- limit?: number;
1419
- parentId?: number;
1420
- organizationId?: number;
1421
- }): Promise<PaginatedResponse<Category>>;
1422
- /**
1423
- * Get category by ID
1424
- */
1425
- getCategoryById(id: number): Promise<ApiResponse<Category>>;
1426
- /**
1427
- * Get category by slug
1428
- */
1429
- getCategoryBySlug(slug: string): Promise<ApiResponse<Category>>;
1430
- /**
1431
- * Get product category by slug (for products)
1432
- */
1433
- getProductCategoryBySlug(siteName: string, slug: string, cachePolicy?: CachePolicy): Promise<ApiResponse<Category>>;
1416
+ getCategories(siteName: string, params?: {
1417
+ category_type?: 'post' | 'product';
1418
+ parent_id?: string;
1419
+ include_subcategories?: 'true' | 'false';
1420
+ }, cachePolicy?: CachePolicy): Promise<ApiResponse<{
1421
+ categories: Category[];
1422
+ }>>;
1434
1423
  /**
1435
- * Create new category
1424
+ * Get category by ID (validates it belongs to the site)
1436
1425
  */
1437
- createCategory(data: CreateCategoryRequest): Promise<ApiResponse<Category>>;
1426
+ getCategoryById(siteName: string, id: number, cachePolicy?: CachePolicy): Promise<ApiResponse<{
1427
+ category: Category;
1428
+ }>>;
1438
1429
  /**
1439
- * Update category
1430
+ * Get product categories for a site
1440
1431
  */
1441
- updateCategory(id: number, data: Partial<CreateCategoryRequest>): Promise<ApiResponse<Category>>;
1432
+ getProductCategories(siteName: string, params?: {
1433
+ parent_id?: string;
1434
+ include_subcategories?: 'true' | 'false';
1435
+ }, cachePolicy?: CachePolicy): Promise<ApiResponse<{
1436
+ categories: Category[];
1437
+ }>>;
1442
1438
  /**
1443
- * Delete category
1439
+ * Create new category for a site
1444
1440
  */
1445
- deleteCategory(id: number): Promise<ApiResponse<{
1441
+ createCategory(siteName: string, data: CreateCategoryRequest, csrfToken?: string): Promise<ApiResponse<{
1446
1442
  message: string;
1443
+ category_id: number;
1447
1444
  }>>;
1448
1445
  /**
1449
- * Get category tree (hierarchical structure)
1450
- */
1451
- getCategoryTree(rootId?: number): Promise<ApiResponse<Array<Category & {
1452
- children: Category[];
1453
- }>>>;
1454
- /**
1455
- * Get category children
1456
- */
1457
- getCategoryChildren(id: number): Promise<ApiResponse<Category[]>>;
1458
- /**
1459
- * Get category parent
1460
- */
1461
- getCategoryParent(id: number): Promise<ApiResponse<Category | null>>;
1462
- /**
1463
- * Move category to new parent
1464
- */
1465
- moveCategoryToParent(id: number, parentId: number | null): Promise<ApiResponse<Category>>;
1466
- /**
1467
- * Get category breadcrumb path
1446
+ * Create new product category for a site
1468
1447
  */
1469
- getCategoryBreadcrumb(id: number): Promise<ApiResponse<Array<{
1470
- id: number;
1471
- name: string;
1472
- slug: string;
1473
- }>>>;
1448
+ createProductCategory(siteName: string, data: Omit<CreateCategoryRequest, 'category_type'>, csrfToken?: string): Promise<ApiResponse<{
1449
+ message: string;
1450
+ category_id: number;
1451
+ }>>;
1474
1452
  /**
1475
- * Get category content/products
1453
+ * Update category (validates it belongs to the site)
1476
1454
  */
1477
- getCategoryContent(id: number, params?: {
1478
- page?: number;
1479
- limit?: number;
1480
- type?: 'content' | 'products' | 'all';
1481
- }): Promise<ApiResponse<{
1482
- content: any[];
1483
- products: any[];
1484
- total: number;
1455
+ updateCategory(siteName: string, id: number, data: Partial<CreateCategoryRequest>, csrfToken?: string): Promise<ApiResponse<{
1456
+ message: string;
1485
1457
  }>>;
1486
1458
  /**
1487
- * Bulk update category order
1459
+ * Delete category (validates it belongs to the site)
1488
1460
  */
1489
- updateCategoryOrder(updates: Array<{
1490
- id: number;
1491
- order: number;
1492
- parentId?: number;
1493
- }>): Promise<ApiResponse<{
1461
+ deleteCategory(siteName: string, id: number, csrfToken?: string): Promise<ApiResponse<{
1494
1462
  message: string;
1495
1463
  }>>;
1496
1464
  /**
1497
- * Search categories
1465
+ * Associate pages or products with categories
1498
1466
  */
1499
- searchCategories(query: string, params?: {
1500
- limit?: number;
1501
- organizationId?: number;
1502
- }): Promise<ApiResponse<Category[]>>;
1467
+ associateCategories(siteName: string, data: {
1468
+ entity_id: number;
1469
+ entity_type: 'page' | 'product';
1470
+ category_ids: number[];
1471
+ }, csrfToken?: string): Promise<ApiResponse<{
1472
+ message: string;
1473
+ }>>;
1503
1474
  private buildCategoryTags;
1504
1475
  }
1505
1476
 
package/dist/index.js CHANGED
@@ -1589,115 +1589,114 @@ var CategoriesClient = class extends BaseClient {
1589
1589
  super(http, "/api/v1", cache);
1590
1590
  }
1591
1591
  /**
1592
- * Get all categories
1592
+ * Get all categories for a site
1593
1593
  */
1594
- async getCategories(params) {
1595
- return this.getPaginated("/categories", params);
1596
- }
1597
- /**
1598
- * Get category by ID
1599
- */
1600
- async getCategoryById(id) {
1601
- return this.getSingle(`/categories/${id}`);
1602
- }
1603
- /**
1604
- * Get category by slug
1605
- */
1606
- async getCategoryBySlug(slug) {
1607
- return this.getSingle(`/categories/slug/${slug}`);
1594
+ async getCategories(siteName, params, cachePolicy) {
1595
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories", { includeSitesSegment: false });
1596
+ const path = this.buildPath(endpoint);
1597
+ return this.fetchWithCache(
1598
+ endpoint,
1599
+ params,
1600
+ this.buildCategoryTags(siteName),
1601
+ cachePolicy,
1602
+ () => this.http.get(path, params)
1603
+ );
1608
1604
  }
1609
1605
  /**
1610
- * Get product category by slug (for products)
1606
+ * Get category by ID (validates it belongs to the site)
1611
1607
  */
1612
- async getProductCategoryBySlug(siteName, slug, cachePolicy) {
1613
- const endpoint = this.siteScopedEndpoint(
1614
- siteName,
1615
- `/product_category/slug/${encodeURIComponent(slug)}`,
1616
- { includeSitesSegment: false }
1617
- );
1608
+ async getCategoryById(siteName, id, cachePolicy) {
1609
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
1618
1610
  const path = this.buildPath(endpoint);
1619
1611
  return this.fetchWithCache(
1620
1612
  endpoint,
1621
1613
  void 0,
1622
- this.buildCategoryTags(siteName, slug),
1614
+ this.buildCategoryTags(siteName, `categories:id:${id}`),
1623
1615
  cachePolicy,
1624
1616
  () => this.http.get(path)
1625
1617
  );
1626
1618
  }
1627
1619
  /**
1628
- * Create new category
1620
+ * Get product categories for a site
1629
1621
  */
1630
- async createCategory(data) {
1631
- return this.create("/categories", data);
1632
- }
1633
- /**
1634
- * Update category
1635
- */
1636
- async updateCategory(id, data) {
1637
- return this.update(`/categories/${id}`, data);
1638
- }
1639
- /**
1640
- * Delete category
1641
- */
1642
- async deleteCategory(id) {
1643
- return this.delete(`/categories/${id}`);
1644
- }
1645
- /**
1646
- * Get category tree (hierarchical structure)
1647
- */
1648
- async getCategoryTree(rootId) {
1649
- const endpoint = rootId ? `/categories/tree/${rootId}` : "/categories/tree";
1650
- return this.getSingle(endpoint);
1651
- }
1652
- /**
1653
- * Get category children
1654
- */
1655
- async getCategoryChildren(id) {
1656
- return this.getSingle(`/categories/${id}/children`);
1657
- }
1658
- /**
1659
- * Get category parent
1660
- */
1661
- async getCategoryParent(id) {
1662
- return this.getSingle(`/categories/${id}/parent`);
1622
+ async getProductCategories(siteName, params, cachePolicy) {
1623
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories/product", { includeSitesSegment: false });
1624
+ const path = this.buildPath(endpoint);
1625
+ return this.fetchWithCache(
1626
+ endpoint,
1627
+ params,
1628
+ this.buildCategoryTags(siteName, "categories:product"),
1629
+ cachePolicy,
1630
+ () => this.http.get(path, params)
1631
+ );
1663
1632
  }
1664
1633
  /**
1665
- * Move category to new parent
1634
+ * Create new category for a site
1666
1635
  */
1667
- async moveCategoryToParent(id, parentId) {
1668
- return this.patch(`/categories/${id}`, { parentId });
1636
+ async createCategory(siteName, data, csrfToken) {
1637
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories", { includeSitesSegment: false });
1638
+ const path = this.buildPath(endpoint);
1639
+ const result = await this.http.post(path, data, { csrfToken });
1640
+ if (this.cache) {
1641
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
1642
+ }
1643
+ return result;
1669
1644
  }
1670
1645
  /**
1671
- * Get category breadcrumb path
1646
+ * Create new product category for a site
1672
1647
  */
1673
- async getCategoryBreadcrumb(id) {
1674
- return this.getSingle(`/categories/${id}/breadcrumb`);
1648
+ async createProductCategory(siteName, data, csrfToken) {
1649
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories/product", { includeSitesSegment: false });
1650
+ const path = this.buildPath(endpoint);
1651
+ const result = await this.http.post(path, data, { csrfToken });
1652
+ if (this.cache) {
1653
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName, "categories:product") });
1654
+ }
1655
+ return result;
1675
1656
  }
1676
1657
  /**
1677
- * Get category content/products
1658
+ * Update category (validates it belongs to the site)
1678
1659
  */
1679
- async getCategoryContent(id, params) {
1680
- return this.http.get(`/categories/${id}/content`, params);
1660
+ async updateCategory(siteName, id, data, csrfToken) {
1661
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
1662
+ const path = this.buildPath(endpoint);
1663
+ const result = await this.http.put(path, data, { csrfToken });
1664
+ if (this.cache) {
1665
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName, `categories:id:${id}`) });
1666
+ }
1667
+ return result;
1681
1668
  }
1682
1669
  /**
1683
- * Bulk update category order
1670
+ * Delete category (validates it belongs to the site)
1684
1671
  */
1685
- async updateCategoryOrder(updates) {
1686
- return this.create("/categories/reorder", { updates });
1672
+ async deleteCategory(siteName, id, csrfToken) {
1673
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
1674
+ const path = this.buildPath(endpoint);
1675
+ const result = await this.http.delete(path, { csrfToken });
1676
+ if (this.cache) {
1677
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
1678
+ }
1679
+ return result;
1687
1680
  }
1688
1681
  /**
1689
- * Search categories
1682
+ * Associate pages or products with categories
1690
1683
  */
1691
- async searchCategories(query, params) {
1692
- return this.http.get(`/categories/search`, { q: query, ...params });
1684
+ async associateCategories(siteName, data, csrfToken) {
1685
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories/associate", { includeSitesSegment: false });
1686
+ const path = this.buildPath(endpoint);
1687
+ const result = await this.http.post(path, data, { csrfToken });
1688
+ if (this.cache) {
1689
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
1690
+ }
1691
+ return result;
1693
1692
  }
1694
- buildCategoryTags(siteName, slug) {
1693
+ buildCategoryTags(siteName, extraTag) {
1695
1694
  const tags = /* @__PURE__ */ new Set(["categories"]);
1696
1695
  if (siteName) {
1697
1696
  tags.add(`categories:site:${siteName}`);
1698
1697
  }
1699
- if (slug) {
1700
- tags.add(`categories:product:${siteName}:${slug}`);
1698
+ if (extraTag) {
1699
+ tags.add(extraTag);
1701
1700
  }
1702
1701
  return Array.from(tags.values());
1703
1702
  }
package/dist/index.mjs CHANGED
@@ -1527,115 +1527,114 @@ var CategoriesClient = class extends BaseClient {
1527
1527
  super(http, "/api/v1", cache);
1528
1528
  }
1529
1529
  /**
1530
- * Get all categories
1530
+ * Get all categories for a site
1531
1531
  */
1532
- async getCategories(params) {
1533
- return this.getPaginated("/categories", params);
1534
- }
1535
- /**
1536
- * Get category by ID
1537
- */
1538
- async getCategoryById(id) {
1539
- return this.getSingle(`/categories/${id}`);
1540
- }
1541
- /**
1542
- * Get category by slug
1543
- */
1544
- async getCategoryBySlug(slug) {
1545
- return this.getSingle(`/categories/slug/${slug}`);
1532
+ async getCategories(siteName, params, cachePolicy) {
1533
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories", { includeSitesSegment: false });
1534
+ const path = this.buildPath(endpoint);
1535
+ return this.fetchWithCache(
1536
+ endpoint,
1537
+ params,
1538
+ this.buildCategoryTags(siteName),
1539
+ cachePolicy,
1540
+ () => this.http.get(path, params)
1541
+ );
1546
1542
  }
1547
1543
  /**
1548
- * Get product category by slug (for products)
1544
+ * Get category by ID (validates it belongs to the site)
1549
1545
  */
1550
- async getProductCategoryBySlug(siteName, slug, cachePolicy) {
1551
- const endpoint = this.siteScopedEndpoint(
1552
- siteName,
1553
- `/product_category/slug/${encodeURIComponent(slug)}`,
1554
- { includeSitesSegment: false }
1555
- );
1546
+ async getCategoryById(siteName, id, cachePolicy) {
1547
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
1556
1548
  const path = this.buildPath(endpoint);
1557
1549
  return this.fetchWithCache(
1558
1550
  endpoint,
1559
1551
  void 0,
1560
- this.buildCategoryTags(siteName, slug),
1552
+ this.buildCategoryTags(siteName, `categories:id:${id}`),
1561
1553
  cachePolicy,
1562
1554
  () => this.http.get(path)
1563
1555
  );
1564
1556
  }
1565
1557
  /**
1566
- * Create new category
1558
+ * Get product categories for a site
1567
1559
  */
1568
- async createCategory(data) {
1569
- return this.create("/categories", data);
1570
- }
1571
- /**
1572
- * Update category
1573
- */
1574
- async updateCategory(id, data) {
1575
- return this.update(`/categories/${id}`, data);
1576
- }
1577
- /**
1578
- * Delete category
1579
- */
1580
- async deleteCategory(id) {
1581
- return this.delete(`/categories/${id}`);
1582
- }
1583
- /**
1584
- * Get category tree (hierarchical structure)
1585
- */
1586
- async getCategoryTree(rootId) {
1587
- const endpoint = rootId ? `/categories/tree/${rootId}` : "/categories/tree";
1588
- return this.getSingle(endpoint);
1589
- }
1590
- /**
1591
- * Get category children
1592
- */
1593
- async getCategoryChildren(id) {
1594
- return this.getSingle(`/categories/${id}/children`);
1595
- }
1596
- /**
1597
- * Get category parent
1598
- */
1599
- async getCategoryParent(id) {
1600
- return this.getSingle(`/categories/${id}/parent`);
1560
+ async getProductCategories(siteName, params, cachePolicy) {
1561
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories/product", { includeSitesSegment: false });
1562
+ const path = this.buildPath(endpoint);
1563
+ return this.fetchWithCache(
1564
+ endpoint,
1565
+ params,
1566
+ this.buildCategoryTags(siteName, "categories:product"),
1567
+ cachePolicy,
1568
+ () => this.http.get(path, params)
1569
+ );
1601
1570
  }
1602
1571
  /**
1603
- * Move category to new parent
1572
+ * Create new category for a site
1604
1573
  */
1605
- async moveCategoryToParent(id, parentId) {
1606
- return this.patch(`/categories/${id}`, { parentId });
1574
+ async createCategory(siteName, data, csrfToken) {
1575
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories", { includeSitesSegment: false });
1576
+ const path = this.buildPath(endpoint);
1577
+ const result = await this.http.post(path, data, { csrfToken });
1578
+ if (this.cache) {
1579
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
1580
+ }
1581
+ return result;
1607
1582
  }
1608
1583
  /**
1609
- * Get category breadcrumb path
1584
+ * Create new product category for a site
1610
1585
  */
1611
- async getCategoryBreadcrumb(id) {
1612
- return this.getSingle(`/categories/${id}/breadcrumb`);
1586
+ async createProductCategory(siteName, data, csrfToken) {
1587
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories/product", { includeSitesSegment: false });
1588
+ const path = this.buildPath(endpoint);
1589
+ const result = await this.http.post(path, data, { csrfToken });
1590
+ if (this.cache) {
1591
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName, "categories:product") });
1592
+ }
1593
+ return result;
1613
1594
  }
1614
1595
  /**
1615
- * Get category content/products
1596
+ * Update category (validates it belongs to the site)
1616
1597
  */
1617
- async getCategoryContent(id, params) {
1618
- return this.http.get(`/categories/${id}/content`, params);
1598
+ async updateCategory(siteName, id, data, csrfToken) {
1599
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
1600
+ const path = this.buildPath(endpoint);
1601
+ const result = await this.http.put(path, data, { csrfToken });
1602
+ if (this.cache) {
1603
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName, `categories:id:${id}`) });
1604
+ }
1605
+ return result;
1619
1606
  }
1620
1607
  /**
1621
- * Bulk update category order
1608
+ * Delete category (validates it belongs to the site)
1622
1609
  */
1623
- async updateCategoryOrder(updates) {
1624
- return this.create("/categories/reorder", { updates });
1610
+ async deleteCategory(siteName, id, csrfToken) {
1611
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
1612
+ const path = this.buildPath(endpoint);
1613
+ const result = await this.http.delete(path, { csrfToken });
1614
+ if (this.cache) {
1615
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
1616
+ }
1617
+ return result;
1625
1618
  }
1626
1619
  /**
1627
- * Search categories
1620
+ * Associate pages or products with categories
1628
1621
  */
1629
- async searchCategories(query, params) {
1630
- return this.http.get(`/categories/search`, { q: query, ...params });
1622
+ async associateCategories(siteName, data, csrfToken) {
1623
+ const endpoint = this.siteScopedEndpoint(siteName, "/categories/associate", { includeSitesSegment: false });
1624
+ const path = this.buildPath(endpoint);
1625
+ const result = await this.http.post(path, data, { csrfToken });
1626
+ if (this.cache) {
1627
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
1628
+ }
1629
+ return result;
1631
1630
  }
1632
- buildCategoryTags(siteName, slug) {
1631
+ buildCategoryTags(siteName, extraTag) {
1633
1632
  const tags = /* @__PURE__ */ new Set(["categories"]);
1634
1633
  if (siteName) {
1635
1634
  tags.add(`categories:site:${siteName}`);
1636
1635
  }
1637
- if (slug) {
1638
- tags.add(`categories:product:${siteName}:${slug}`);
1636
+ if (extraTag) {
1637
+ tags.add(extraTag);
1639
1638
  }
1640
1639
  return Array.from(tags.values());
1641
1640
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perspectapi-ts-sdk",
3
- "version": "2.8.3",
3
+ "version": "2.9.0",
4
4
  "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -8,7 +8,6 @@ import type { CachePolicy } from '../cache/types';
8
8
  import type {
9
9
  Category,
10
10
  CreateCategoryRequest,
11
- PaginatedResponse,
12
11
  ApiResponse,
13
12
  } from '../types';
14
13
 
@@ -18,161 +17,181 @@ export class CategoriesClient extends BaseClient {
18
17
  }
19
18
 
20
19
  /**
21
- * Get all categories
20
+ * Get all categories for a site
22
21
  */
23
- async getCategories(params?: {
24
- page?: number;
25
- limit?: number;
26
- parentId?: number;
27
- organizationId?: number;
28
- }): Promise<PaginatedResponse<Category>> {
29
- return this.getPaginated<Category>('/categories', params);
30
- }
22
+ async getCategories(
23
+ siteName: string,
24
+ params?: {
25
+ category_type?: 'post' | 'product';
26
+ parent_id?: string;
27
+ include_subcategories?: 'true' | 'false';
28
+ },
29
+ cachePolicy?: CachePolicy
30
+ ): Promise<ApiResponse<{ categories: Category[] }>> {
31
+ const endpoint = this.siteScopedEndpoint(siteName, '/categories', { includeSitesSegment: false });
32
+ const path = this.buildPath(endpoint);
31
33
 
32
- /**
33
- * Get category by ID
34
- */
35
- async getCategoryById(id: number): Promise<ApiResponse<Category>> {
36
- return this.getSingle<Category>(`/categories/${id}`);
34
+ return this.fetchWithCache<ApiResponse<{ categories: Category[] }>>(
35
+ endpoint,
36
+ params,
37
+ this.buildCategoryTags(siteName),
38
+ cachePolicy,
39
+ () => this.http.get<{ categories: Category[] }>(path, params)
40
+ );
37
41
  }
38
42
 
39
43
  /**
40
- * Get category by slug
44
+ * Get category by ID (validates it belongs to the site)
41
45
  */
42
- async getCategoryBySlug(slug: string): Promise<ApiResponse<Category>> {
43
- return this.getSingle<Category>(`/categories/slug/${slug}`);
46
+ async getCategoryById(siteName: string, id: number, cachePolicy?: CachePolicy): Promise<ApiResponse<{ category: Category }>> {
47
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
48
+ const path = this.buildPath(endpoint);
49
+
50
+ return this.fetchWithCache<ApiResponse<{ category: Category }>>(
51
+ endpoint,
52
+ undefined,
53
+ this.buildCategoryTags(siteName, `categories:id:${id}`),
54
+ cachePolicy,
55
+ () => this.http.get<{ category: Category }>(path)
56
+ );
44
57
  }
45
58
 
46
59
  /**
47
- * Get product category by slug (for products)
60
+ * Get product categories for a site
48
61
  */
49
- async getProductCategoryBySlug(
62
+ async getProductCategories(
50
63
  siteName: string,
51
- slug: string,
64
+ params?: {
65
+ parent_id?: string;
66
+ include_subcategories?: 'true' | 'false';
67
+ },
52
68
  cachePolicy?: CachePolicy
53
- ): Promise<ApiResponse<Category>> {
54
- const endpoint = this.siteScopedEndpoint(
55
- siteName,
56
- `/product_category/slug/${encodeURIComponent(slug)}`,
57
- { includeSitesSegment: false }
58
- );
69
+ ): Promise<ApiResponse<{ categories: Category[] }>> {
70
+ const endpoint = this.siteScopedEndpoint(siteName, '/categories/product', { includeSitesSegment: false });
59
71
  const path = this.buildPath(endpoint);
60
72
 
61
- return this.fetchWithCache<ApiResponse<Category>>(
73
+ return this.fetchWithCache<ApiResponse<{ categories: Category[] }>>(
62
74
  endpoint,
63
- undefined,
64
- this.buildCategoryTags(siteName, slug),
75
+ params,
76
+ this.buildCategoryTags(siteName, 'categories:product'),
65
77
  cachePolicy,
66
- () => this.http.get<Category>(path)
78
+ () => this.http.get<{ categories: Category[] }>(path, params)
67
79
  );
68
80
  }
69
81
 
70
82
  /**
71
- * Create new category
83
+ * Create new category for a site
72
84
  */
73
- async createCategory(data: CreateCategoryRequest): Promise<ApiResponse<Category>> {
74
- return this.create<CreateCategoryRequest, Category>('/categories', data);
75
- }
85
+ async createCategory(
86
+ siteName: string,
87
+ data: CreateCategoryRequest,
88
+ csrfToken?: string
89
+ ): Promise<ApiResponse<{ message: string; category_id: number }>> {
90
+ const endpoint = this.siteScopedEndpoint(siteName, '/categories', { includeSitesSegment: false });
91
+ const path = this.buildPath(endpoint);
76
92
 
77
- /**
78
- * Update category
79
- */
80
- async updateCategory(id: number, data: Partial<CreateCategoryRequest>): Promise<ApiResponse<Category>> {
81
- return this.update<Partial<CreateCategoryRequest>, Category>(`/categories/${id}`, data);
82
- }
93
+ const result = await this.http.post<{ message: string; category_id: number }>(path, data, { csrfToken });
83
94
 
84
- /**
85
- * Delete category
86
- */
87
- async deleteCategory(id: number): Promise<ApiResponse<{ message: string }>> {
88
- return this.delete<{ message: string }>(`/categories/${id}`);
89
- }
95
+ // Invalidate cache after creation
96
+ if (this.cache) {
97
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
98
+ }
90
99
 
91
- /**
92
- * Get category tree (hierarchical structure)
93
- */
94
- async getCategoryTree(rootId?: number): Promise<ApiResponse<Array<Category & {
95
- children: Category[];
96
- }>>> {
97
- const endpoint = rootId ? `/categories/tree/${rootId}` : '/categories/tree';
98
- return this.getSingle(endpoint);
100
+ return result;
99
101
  }
100
102
 
101
103
  /**
102
- * Get category children
104
+ * Create new product category for a site
103
105
  */
104
- async getCategoryChildren(id: number): Promise<ApiResponse<Category[]>> {
105
- return this.getSingle<Category[]>(`/categories/${id}/children`);
106
- }
106
+ async createProductCategory(
107
+ siteName: string,
108
+ data: Omit<CreateCategoryRequest, 'category_type'>,
109
+ csrfToken?: string
110
+ ): Promise<ApiResponse<{ message: string; category_id: number }>> {
111
+ const endpoint = this.siteScopedEndpoint(siteName, '/categories/product', { includeSitesSegment: false });
112
+ const path = this.buildPath(endpoint);
107
113
 
108
- /**
109
- * Get category parent
110
- */
111
- async getCategoryParent(id: number): Promise<ApiResponse<Category | null>> {
112
- return this.getSingle<Category | null>(`/categories/${id}/parent`);
113
- }
114
+ const result = await this.http.post<{ message: string; category_id: number }>(path, data, { csrfToken });
114
115
 
115
- /**
116
- * Move category to new parent
117
- */
118
- async moveCategoryToParent(id: number, parentId: number | null): Promise<ApiResponse<Category>> {
119
- return this.patch<{ parentId: number | null }, Category>(`/categories/${id}`, { parentId });
120
- }
116
+ // Invalidate cache after creation
117
+ if (this.cache) {
118
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName, 'categories:product') });
119
+ }
121
120
 
122
- /**
123
- * Get category breadcrumb path
124
- */
125
- async getCategoryBreadcrumb(id: number): Promise<ApiResponse<Array<{
126
- id: number;
127
- name: string;
128
- slug: string;
129
- }>>> {
130
- return this.getSingle(`/categories/${id}/breadcrumb`);
121
+ return result;
131
122
  }
132
123
 
133
124
  /**
134
- * Get category content/products
125
+ * Update category (validates it belongs to the site)
135
126
  */
136
- async getCategoryContent(id: number, params?: {
137
- page?: number;
138
- limit?: number;
139
- type?: 'content' | 'products' | 'all';
140
- }): Promise<ApiResponse<{
141
- content: any[];
142
- products: any[];
143
- total: number;
144
- }>> {
145
- return this.http.get(`/categories/${id}/content`, params);
127
+ async updateCategory(
128
+ siteName: string,
129
+ id: number,
130
+ data: Partial<CreateCategoryRequest>,
131
+ csrfToken?: string
132
+ ): Promise<ApiResponse<{ message: string }>> {
133
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
134
+ const path = this.buildPath(endpoint);
135
+
136
+ const result = await this.http.put<{ message: string }>(path, data, { csrfToken });
137
+
138
+ // Invalidate cache after update
139
+ if (this.cache) {
140
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName, `categories:id:${id}`) });
141
+ }
142
+
143
+ return result;
146
144
  }
147
145
 
148
146
  /**
149
- * Bulk update category order
147
+ * Delete category (validates it belongs to the site)
150
148
  */
151
- async updateCategoryOrder(updates: Array<{
152
- id: number;
153
- order: number;
154
- parentId?: number;
155
- }>): Promise<ApiResponse<{ message: string }>> {
156
- return this.create('/categories/reorder', { updates });
149
+ async deleteCategory(siteName: string, id: number, csrfToken?: string): Promise<ApiResponse<{ message: string }>> {
150
+ const endpoint = this.siteScopedEndpoint(siteName, `/categories/${id}`, { includeSitesSegment: false });
151
+ const path = this.buildPath(endpoint);
152
+
153
+ const result = await this.http.delete<{ message: string }>(path, { csrfToken });
154
+
155
+ // Invalidate cache after deletion
156
+ if (this.cache) {
157
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
158
+ }
159
+
160
+ return result;
157
161
  }
158
162
 
159
163
  /**
160
- * Search categories
164
+ * Associate pages or products with categories
161
165
  */
162
- async searchCategories(query: string, params?: {
163
- limit?: number;
164
- organizationId?: number;
165
- }): Promise<ApiResponse<Category[]>> {
166
- return this.http.get(`/categories/search`, { q: query, ...params });
166
+ async associateCategories(
167
+ siteName: string,
168
+ data: {
169
+ entity_id: number;
170
+ entity_type: 'page' | 'product';
171
+ category_ids: number[];
172
+ },
173
+ csrfToken?: string
174
+ ): Promise<ApiResponse<{ message: string }>> {
175
+ const endpoint = this.siteScopedEndpoint(siteName, '/categories/associate', { includeSitesSegment: false });
176
+ const path = this.buildPath(endpoint);
177
+
178
+ const result = await this.http.post<{ message: string }>(path, data, { csrfToken });
179
+
180
+ // Invalidate cache after association
181
+ if (this.cache) {
182
+ await this.cache.invalidate({ tags: this.buildCategoryTags(siteName) });
183
+ }
184
+
185
+ return result;
167
186
  }
168
187
 
169
- private buildCategoryTags(siteName: string, slug: string): string[] {
188
+ private buildCategoryTags(siteName: string, extraTag?: string): string[] {
170
189
  const tags = new Set<string>(['categories']);
171
190
  if (siteName) {
172
191
  tags.add(`categories:site:${siteName}`);
173
192
  }
174
- if (slug) {
175
- tags.add(`categories:product:${siteName}:${slug}`);
193
+ if (extraTag) {
194
+ tags.add(extraTag);
176
195
  }
177
196
  return Array.from(tags.values());
178
197
  }