@lonca/trendyol 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -34,6 +34,29 @@ var BrandsResource = class {
34
34
  const nextCursor = page + 1 < data.totalPages ? String(page + 1) : void 0;
35
35
  return nextCursor !== void 0 ? { items, nextCursor } : { items };
36
36
  }
37
+ /**
38
+ * Search brands by name. Useful when you need a brand's numeric ID for
39
+ * `createProducts` and don't want to page through the full `list()`
40
+ * (1000 brands per page).
41
+ *
42
+ * **Discovery-first wire fact (verified STAGE 2026-05-25):** Trendyol's
43
+ * doc claims this is a case-sensitive *exact* match, but live behaviour
44
+ * is **substring + case-insensitive** — `search('Trendyol')` returns
45
+ * 17 hits including `TRENDYOLMILLA`, `trendyol vavist`, `Trendyol Üyelik`.
46
+ * Plan for ranking your results client-side if you need an exact match.
47
+ * The endpoint returns an empty array when nothing matches (no 404).
48
+ *
49
+ * @param name The brand name to search for.
50
+ */
51
+ async search(name) {
52
+ const data = await this.transport.request({
53
+ method: "GET",
54
+ path: "/integration/product/brands/by-name",
55
+ query: { name },
56
+ rateLimiter: this.limiter
57
+ });
58
+ return (data ?? []).map((b) => ({ id: String(b.id), name: b.name }));
59
+ }
37
60
  };
38
61
  function normalizeCategory(node) {
39
62
  return {
@@ -63,12 +86,16 @@ function normalizeAttribute(node) {
63
86
  }
64
87
  return out;
65
88
  }
89
+ var MAX_ATTR_VALUES_PAGE_SIZE = 1e3;
90
+ var DEFAULT_ATTR_VALUES_PAGE_SIZE = 100;
66
91
  var CategoriesResource = class {
67
- constructor(transport, limiter) {
92
+ constructor(transport, limiter, sellerId) {
68
93
  this.transport = transport;
94
+ this.sellerId = sellerId;
69
95
  this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 50, intervalMs: 6e4 });
70
96
  }
71
97
  transport;
98
+ sellerId;
72
99
  limiter;
73
100
  /**
74
101
  * Fetch the full Trendyol category tree.
@@ -110,6 +137,87 @@ var CategoriesResource = class {
110
137
  }
111
138
  return list.map(normalizeAttribute);
112
139
  }
140
+ /**
141
+ * Fetch the allowed values for a single category attribute (paginated).
142
+ *
143
+ * `getCategoryAttributes` returns attribute metadata + flags but typically
144
+ * omits the value catalog. Use this method to fetch the catalog for an
145
+ * attribute when `allowCustom` is `false` and you need to map your data
146
+ * onto Trendyol's accepted values.
147
+ *
148
+ * @param categoryId Trendyol numeric category ID; accepts `string` or `number`.
149
+ * @param attributeId Attribute ID returned by `getAttributes`.
150
+ * @param params Cursor pagination (max page size 1000; default 100).
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * import { paginate } from '@lonca/core';
155
+ * for await (const value of paginate((p) =>
156
+ * client.categories.getAttributeValues(catId, attrId, p),
157
+ * )) {
158
+ * console.log(value.id, value.name);
159
+ * }
160
+ * ```
161
+ */
162
+ async getAttributeValues(categoryId, attributeId, params = {}) {
163
+ const catId = encodeURIComponent(String(categoryId));
164
+ const attrId = encodeURIComponent(String(attributeId));
165
+ const size = Math.min(params.limit ?? DEFAULT_ATTR_VALUES_PAGE_SIZE, MAX_ATTR_VALUES_PAGE_SIZE);
166
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
167
+ const data = await this.transport.request({
168
+ method: "GET",
169
+ path: `/integration/product/categories/${catId}/attributes/${attrId}/values`,
170
+ query: { page, size },
171
+ rateLimiter: this.limiter
172
+ });
173
+ const items = (data.content ?? []).map((item) => ({
174
+ id: item.attributeValueId !== void 0 ? String(item.attributeValueId) : "",
175
+ name: item.attributeValue ?? item.attributeValueName ?? ""
176
+ }));
177
+ const result = { items };
178
+ const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
179
+ if (page + 1 < totalPages) {
180
+ result.nextCursor = String(page + 1);
181
+ }
182
+ return result;
183
+ }
184
+ /**
185
+ * Look up category info for a list of barcodes (Trendyol Export Center
186
+ * / AutoFT endpoint).
187
+ *
188
+ * **Requires Export Center enrollment.** Sellers who have not joined
189
+ * Trendyol's "İhracat Merkezi" program will get an auth error on this
190
+ * endpoint even though their regular Marketplace credentials are valid.
191
+ *
192
+ * @param barcodes 1–N barcodes to look up.
193
+ * @throws {ValidationError} when `barcodes` is empty.
194
+ * @throws Error when the client was created without a `sellerId` and this
195
+ * method is called directly (use `createTrendyolClient` to wire it).
196
+ */
197
+ async getByBarcodes(barcodes) {
198
+ if (!Array.isArray(barcodes) || barcodes.length === 0) {
199
+ throw new ValidationError({ message: "getByBarcodes: barcodes must not be empty" });
200
+ }
201
+ if (this.sellerId === void 0) {
202
+ throw new Error(
203
+ "CategoriesResource.getByBarcodes requires a sellerId; instantiate via createTrendyolClient"
204
+ );
205
+ }
206
+ const data = await this.transport.request({
207
+ method: "POST",
208
+ path: `/integration/ecgw/v1/${this.sellerId}/lookup/product-categories/by-barcodes`,
209
+ body: { barcodes },
210
+ rateLimiter: this.limiter
211
+ });
212
+ const matches = Object.entries(data.barcodeCategories ?? {}).map(([barcode, cat]) => ({
213
+ barcode,
214
+ category: {
215
+ id: cat.id !== void 0 ? String(cat.id) : "",
216
+ name: cat.displayName ?? ""
217
+ }
218
+ }));
219
+ return { matches, notFound: data.notFound ?? [] };
220
+ }
113
221
  };
114
222
  function extractAttributeList(data) {
115
223
  if (Array.isArray(data)) return data;
@@ -361,6 +469,7 @@ var OrdersResource = class {
361
469
  };
362
470
  var DEFAULT_PAGE_SIZE3 = 50;
363
471
  var MAX_PAGE_SIZE2 = 1e3;
472
+ var MAX_BUYBOX_BARCODES = 10;
364
473
  function normalizeRef(ref) {
365
474
  return {
366
475
  id: ref?.id !== void 0 ? String(ref.id) : "",
@@ -425,6 +534,82 @@ function normalizeProduct(node) {
425
534
  if (node.lastModifiedBy !== void 0) out.lastModifiedBy = node.lastModifiedBy;
426
535
  return out;
427
536
  }
537
+ function normalizeUnapprovedRejectReason(r) {
538
+ const out = {};
539
+ if (r.rejectReason !== void 0) out.rejectReason = r.rejectReason;
540
+ if (r.rejectReasonDetail !== void 0) out.rejectReasonDetail = r.rejectReasonDetail;
541
+ return out;
542
+ }
543
+ function normalizeUnapproved(node) {
544
+ const imagesSource = node.images ?? node.media;
545
+ const out = {
546
+ productMainId: node.productMainId ?? "",
547
+ brand: normalizeRef(node.brand),
548
+ category: normalizeRef(node.category),
549
+ barcode: node.barcode ?? "",
550
+ title: node.title ?? "",
551
+ images: normalizeImages(imagesSource),
552
+ attributes: (node.attributes ?? []).map(normalizeAttribute2),
553
+ rejectReasonDetails: (node.rejectReasonDetails ?? []).map(normalizeUnapprovedRejectReason),
554
+ raw: node
555
+ };
556
+ if (node.supplierId !== void 0) out.supplierId = String(node.supplierId);
557
+ if (node.status !== void 0) out.status = node.status;
558
+ if (node.description !== void 0) out.description = node.description;
559
+ if (typeof node.quantity === "number") out.quantity = node.quantity;
560
+ if (typeof node.listPrice === "number") out.listPrice = node.listPrice;
561
+ if (typeof node.salePrice === "number") out.salePrice = node.salePrice;
562
+ if (typeof node.vatRate === "number") out.vatRate = node.vatRate;
563
+ if (node.dimensionalWeight !== void 0 && node.dimensionalWeight !== null) {
564
+ out.dimensionalWeight = node.dimensionalWeight;
565
+ }
566
+ if (node.stockCode !== void 0) out.stockCode = node.stockCode;
567
+ if (node.origin !== void 0) out.origin = node.origin;
568
+ if (node.locationBasedDelivery !== void 0) {
569
+ out.locationBasedDelivery = node.locationBasedDelivery;
570
+ }
571
+ if (node.lotNumber !== void 0) out.lotNumber = node.lotNumber;
572
+ if (node.specialConsumptionTax !== void 0) {
573
+ out.specialConsumptionTax = node.specialConsumptionTax;
574
+ }
575
+ if (node.sgrPrice !== void 0) out.sgrPrice = node.sgrPrice;
576
+ const createdAt = toIso2(node.createDateTime);
577
+ if (createdAt) out.createdAt = createdAt;
578
+ const updatedAt = toIso2(node.lastUpdateDate);
579
+ if (updatedAt) out.updatedAt = updatedAt;
580
+ const priceChanged = toIso2(node.lastPriceChangeDate);
581
+ if (priceChanged) out.lastPriceChangedAt = priceChanged;
582
+ const stockChanged = toIso2(node.lastStockChangeDate);
583
+ if (stockChanged) out.lastStockChangedAt = stockChanged;
584
+ return out;
585
+ }
586
+ function normalizeBuyboxInfo(node) {
587
+ const out = {
588
+ barcode: node.barcode ?? "",
589
+ raw: node
590
+ };
591
+ if (typeof node.buyboxOrder === "number") out.buyboxOrder = node.buyboxOrder;
592
+ if (typeof node.buyboxPrice === "number") out.buyboxPrice = node.buyboxPrice;
593
+ if (typeof node.hasMultipleSeller === "boolean") {
594
+ out.hasMultipleSeller = node.hasMultipleSeller;
595
+ }
596
+ if (node.secondBuyboxPrice !== void 0) out.secondBuyboxPrice = node.secondBuyboxPrice;
597
+ if (node.thirdBuyboxPrice !== void 0) out.thirdBuyboxPrice = node.thirdBuyboxPrice;
598
+ return out;
599
+ }
600
+ function normalizeProductBase(data) {
601
+ const out = {
602
+ barcode: data.barcode ?? "",
603
+ approved: !!data.approved,
604
+ archived: !!data.archived,
605
+ raw: data
606
+ };
607
+ const approvedAt = toIso2(data.approvedDate);
608
+ if (approvedAt) out.approvedAt = approvedAt;
609
+ if (data.listingId !== void 0) out.listingId = data.listingId;
610
+ if (data.contentId !== void 0) out.contentId = String(data.contentId);
611
+ return out;
612
+ }
428
613
  function normalizeBatchResult(data) {
429
614
  const items = (data.items ?? []).map((item) => {
430
615
  const result = {};
@@ -452,17 +637,52 @@ function normalizeBatchResult(data) {
452
637
  if (data.storeFrontCode !== void 0) out.storeFrontCode = data.storeFrontCode;
453
638
  return out;
454
639
  }
640
+ var MAX_WRITE_ITEMS = 1e3;
641
+ var DELETE_RATE_PER_MIN = 100;
455
642
  var ProductsResource = class {
456
643
  constructor(transport, sellerId, options = {}) {
457
644
  this.transport = transport;
458
645
  this.sellerId = sellerId;
459
646
  this.filterLimiter = options.filterLimiter ?? new TokenBucketRateLimiter({ capacity: 2e3, intervalMs: 6e4 });
460
647
  this.batchLimiter = options.batchLimiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
648
+ this.buyboxLimiter = options.buyboxLimiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
649
+ this.writeLimiter = options.writeLimiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
650
+ this.deleteLimiter = options.deleteLimiter ?? new TokenBucketRateLimiter({ capacity: DELETE_RATE_PER_MIN, intervalMs: 6e4 });
461
651
  }
462
652
  transport;
463
653
  sellerId;
464
654
  filterLimiter;
465
655
  batchLimiter;
656
+ buyboxLimiter;
657
+ writeLimiter;
658
+ deleteLimiter;
659
+ validateBarcodes(barcodes, methodLabel) {
660
+ if (!Array.isArray(barcodes) || barcodes.length === 0) {
661
+ throw new ValidationError({ message: `${methodLabel}: barcodes must not be empty` });
662
+ }
663
+ if (barcodes.length > MAX_WRITE_ITEMS) {
664
+ throw new ValidationError({
665
+ message: `${methodLabel}: max ${MAX_WRITE_ITEMS} barcodes per call (got ${barcodes.length})`
666
+ });
667
+ }
668
+ }
669
+ async submitWrite(endpoint, items, methodLabel) {
670
+ if (!Array.isArray(items) || items.length === 0) {
671
+ throw new ValidationError({ message: `${methodLabel}: items must not be empty` });
672
+ }
673
+ if (items.length > MAX_WRITE_ITEMS) {
674
+ throw new ValidationError({
675
+ message: `${methodLabel}: max ${MAX_WRITE_ITEMS} items per call (got ${items.length})`
676
+ });
677
+ }
678
+ const data = await this.transport.request({
679
+ method: "POST",
680
+ path: `/integration/product/sellers/${this.sellerId}${endpoint}`,
681
+ body: { items },
682
+ rateLimiter: this.writeLimiter
683
+ });
684
+ return { batchRequestId: data?.batchRequestId ?? "" };
685
+ }
466
686
  /**
467
687
  * List approved products. Use `paginate()` from `@lonca/core` to iterate
468
688
  * lazily across pages.
@@ -524,6 +744,235 @@ var ProductsResource = class {
524
744
  });
525
745
  return normalizeBatchResult(data);
526
746
  }
747
+ /**
748
+ * List **unapproved** (draft / rejected / pending-review) products.
749
+ *
750
+ * Wire shape is intentionally flatter than the approved-product shape:
751
+ * each barcode is one top-level item with `barcode`, `quantity`, `salePrice`
752
+ * etc. at the root. Rejected drafts carry `rejectReasonDetails` so you can
753
+ * surface why Trendyol's content team turned them down.
754
+ *
755
+ * Pagination follows the same convention as `list()`: `cursor` from the
756
+ * previous response forwards as `nextPageToken`.
757
+ *
758
+ * @example
759
+ * ```ts
760
+ * const page = await client.products.listUnapproved({ limit: 50 });
761
+ * for (const draft of page.items) {
762
+ * if (draft.status === 'rejected') {
763
+ * console.warn(draft.barcode, draft.rejectReasonDetails);
764
+ * }
765
+ * }
766
+ * ```
767
+ */
768
+ async listUnapproved(params = {}) {
769
+ const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE3, MAX_PAGE_SIZE2);
770
+ const query = { size };
771
+ if (params.cursor) {
772
+ query.nextPageToken = params.cursor;
773
+ } else {
774
+ query.page = 0;
775
+ }
776
+ if (params.barcode) query.barcode = params.barcode;
777
+ if (params.startDate) query.startDate = params.startDate.getTime();
778
+ if (params.endDate) query.endDate = params.endDate.getTime();
779
+ if (params.dateQueryType) query.dateQueryType = params.dateQueryType;
780
+ if (params.supplierId !== void 0) query.supplierId = params.supplierId;
781
+ const data = await this.transport.request({
782
+ method: "GET",
783
+ path: `/integration/product/sellers/${this.sellerId}/products/unapproved`,
784
+ query,
785
+ rateLimiter: this.filterLimiter
786
+ });
787
+ const items = (data.content ?? []).map(normalizeUnapproved);
788
+ const page = { items };
789
+ if (data.nextPageToken) {
790
+ page.nextCursor = data.nextPageToken;
791
+ }
792
+ return page;
793
+ }
794
+ /**
795
+ * Fetch the basic lifecycle status of a single product by barcode.
796
+ *
797
+ * Cheap and useful as a polling primitive after `createProducts`: poll
798
+ * this endpoint until `approved` flips to `true` (or use
799
+ * `client.products.getBatchStatus()` to track the originating batch).
800
+ *
801
+ * @param barcode The product barcode to look up.
802
+ */
803
+ async getBase(barcode) {
804
+ const code = encodeURIComponent(barcode);
805
+ const data = await this.transport.request({
806
+ method: "GET",
807
+ path: `/integration/product/sellers/${this.sellerId}/product/${code}`,
808
+ rateLimiter: this.filterLimiter
809
+ });
810
+ return normalizeProductBase(data);
811
+ }
812
+ /**
813
+ * Fetch buybox information for up to 10 barcodes in one call.
814
+ *
815
+ * Returns rank (`buyboxOrder === 1` means you hold the buybox), the
816
+ * current buybox price, and — beyond the spec — the second and third
817
+ * competing prices when other sellers are present.
818
+ *
819
+ * @param barcodes 1–10 product barcodes.
820
+ * @throws {ValidationError} when `barcodes` is empty or longer than 10.
821
+ */
822
+ async getBuyboxInfo(barcodes) {
823
+ if (!Array.isArray(barcodes) || barcodes.length === 0) {
824
+ throw new ValidationError({
825
+ message: "getBuyboxInfo: barcodes must be a non-empty array"
826
+ });
827
+ }
828
+ if (barcodes.length > MAX_BUYBOX_BARCODES) {
829
+ throw new ValidationError({
830
+ message: `getBuyboxInfo: max ${MAX_BUYBOX_BARCODES} barcodes per call (got ${barcodes.length})`
831
+ });
832
+ }
833
+ const data = await this.transport.request({
834
+ method: "POST",
835
+ path: `/integration/product/sellers/${this.sellerId}/products/buybox-information`,
836
+ body: { barcodes },
837
+ rateLimiter: this.buyboxLimiter
838
+ });
839
+ const list = data.buyboxInfo ?? data.content ?? [];
840
+ return list.map(normalizeBuyboxInfo);
841
+ }
842
+ /**
843
+ * Create products (V2). Async batch — returns a `batchRequestId` you can
844
+ * poll with `getBatchStatus`. Max 1000 items per call.
845
+ *
846
+ * Trendyol requires the full V2 attribute payload — fetch via
847
+ * `categories.getAttributes` (and `categories.getAttributeValues` for
848
+ * values when `allowCustom === false`). Shipment / returning warehouse
849
+ * IDs come from `suppliers.getAddresses`.
850
+ *
851
+ * @throws {ValidationError} when `items` is empty or longer than 1000.
852
+ */
853
+ async create(items) {
854
+ return this.submitWrite("/v2/products", items, "createProducts");
855
+ }
856
+ /**
857
+ * Update **content** of approved products (title, description, images,
858
+ * attributes). Identified by `contentId`. Partial update is supported
859
+ * except for attributes — if you update ANY attribute, send ALL of them.
860
+ *
861
+ * @throws {ValidationError} when `items` is empty or longer than 1000.
862
+ */
863
+ async updateContent(items) {
864
+ return this.submitWrite("/products/content-bulk-update", items, "updateContent");
865
+ }
866
+ /**
867
+ * Update **variant** fields of approved products (stockCode, vatRate,
868
+ * dimensionalWeight, warehouse IDs, location-based delivery, lot). Identified
869
+ * by `barcode`. The barcode itself cannot be changed via this endpoint.
870
+ *
871
+ * @throws {ValidationError} when `items` is empty or longer than 1000.
872
+ */
873
+ async updateVariants(items) {
874
+ return this.submitWrite("/products/variant-bulk-update", items, "updateVariants");
875
+ }
876
+ /**
877
+ * Update **unapproved** (draft) products. Identified by `barcode`. All
878
+ * other fields are optional partial updates. Use this to fix drafts that
879
+ * Trendyol rejected — `client.products.listUnapproved` surfaces the
880
+ * `rejectReasonDetails` you need to act on.
881
+ *
882
+ * **Gotcha (verified live STAGE 2026-05-25):** Trendyol's V2 spec claims
883
+ * only `barcode` is required, but the endpoint returns HTTP 500
884
+ * (`TrendyolSystemException` / `TypeError`) when too many optional fields
885
+ * are omitted. In practice, send at least `title`, `description`,
886
+ * `productMainId`, `brandId`, `categoryId`, `stockCode`,
887
+ * `dimensionalWeight`, `vatRate`, `images[]`, and `attributes[]` (an
888
+ * empty array is OK for the latter). The SDK forwards your payload
889
+ * as-is; trim fields only if you have verified the server accepts it.
890
+ *
891
+ * @throws {ValidationError} when `items` is empty or longer than 1000.
892
+ */
893
+ async updateUnapproved(items) {
894
+ return this.submitWrite("/products/unapproved-bulk-update", items, "updateUnapproved");
895
+ }
896
+ /**
897
+ * Update product **delivery information** (deliveryDuration,
898
+ * fastDeliveryType). Identified by `barcode`.
899
+ *
900
+ * @throws {ValidationError} when `items` is empty or longer than 1000.
901
+ */
902
+ async updateDeliveryInfo(items) {
903
+ return this.submitWrite("/products/delivery-info-bulk-update", items, "updateDeliveryInfo");
904
+ }
905
+ /**
906
+ * Delete products by barcode. Trendyol allows deletion of unapproved
907
+ * products and approved products that have been archived for more than a
908
+ * day (and have not been sales-stopped by Trendyol).
909
+ *
910
+ * Async batch — returns `{ batchRequestId }` to poll via `getBatchStatus`.
911
+ * Separately rate-limited at **100 req/min** (much tighter than create/update).
912
+ *
913
+ * @param barcodes 1–1000 barcodes.
914
+ * @throws {ValidationError} when `barcodes` is empty or longer than 1000.
915
+ */
916
+ async delete(barcodes) {
917
+ this.validateBarcodes(barcodes, "delete");
918
+ const data = await this.transport.request({
919
+ method: "DELETE",
920
+ path: `/integration/product/sellers/${this.sellerId}/products`,
921
+ body: { items: barcodes.map((barcode) => ({ barcode })) },
922
+ rateLimiter: this.deleteLimiter
923
+ });
924
+ return { batchRequestId: data?.batchRequestId ?? "" };
925
+ }
926
+ /**
927
+ * Archive products by barcode (Trendyol's `archived=true` state).
928
+ * Archived products are not visible to customers; pair with `delete`
929
+ * after the 24-hour archive cool-down to remove them entirely.
930
+ *
931
+ * Async batch — returns `{ batchRequestId }`.
932
+ *
933
+ * @throws {ValidationError} when `barcodes` is empty or longer than 1000.
934
+ */
935
+ async archive(barcodes) {
936
+ return this.setArchivedState(barcodes, true, "archive");
937
+ }
938
+ /**
939
+ * Unarchive products by barcode (Trendyol's `archived=false` state).
940
+ * Restores visibility for previously-archived products.
941
+ *
942
+ * @throws {ValidationError} when `barcodes` is empty or longer than 1000.
943
+ */
944
+ async unarchive(barcodes) {
945
+ return this.setArchivedState(barcodes, false, "unarchive");
946
+ }
947
+ async setArchivedState(barcodes, archived, methodLabel) {
948
+ this.validateBarcodes(barcodes, methodLabel);
949
+ const data = await this.transport.request({
950
+ method: "PUT",
951
+ path: `/integration/product/sellers/${this.sellerId}/products/archive-state`,
952
+ body: { items: barcodes.map((barcode) => ({ barcode, archived })) },
953
+ rateLimiter: this.writeLimiter
954
+ });
955
+ return { batchRequestId: data?.batchRequestId ?? "" };
956
+ }
957
+ /**
958
+ * Unlock products whose sale was paused by Trendyol due to pricing
959
+ * issues (under/over-pricing, critical price error, supplier issues).
960
+ * Restores selling status for the listed barcodes.
961
+ *
962
+ * Async batch — returns `{ batchRequestId }`.
963
+ *
964
+ * @throws {ValidationError} when `barcodes` is empty or longer than 1000.
965
+ */
966
+ async unlock(barcodes) {
967
+ this.validateBarcodes(barcodes, "unlock");
968
+ const data = await this.transport.request({
969
+ method: "PUT",
970
+ path: `/integration/product/sellers/${this.sellerId}/products/unlock`,
971
+ body: { items: barcodes.map((barcode) => ({ barcode })) },
972
+ rateLimiter: this.writeLimiter
973
+ });
974
+ return { batchRequestId: data?.batchRequestId ?? "" };
975
+ }
527
976
  };
528
977
  var VALID_TYPES = /* @__PURE__ */ new Set([
529
978
  "SHIPMENT",
@@ -808,7 +1257,7 @@ function createTrendyolClient(opts) {
808
1257
  });
809
1258
  return {
810
1259
  brands: new BrandsResource(transport),
811
- categories: new CategoriesResource(transport),
1260
+ categories: new CategoriesResource(transport, void 0, opts.sellerId),
812
1261
  suppliers: new SuppliersResource(transport, opts.sellerId),
813
1262
  products: new ProductsResource(transport, opts.sellerId),
814
1263
  inventory: new InventoryResource(transport, opts.sellerId),