@lonca/trendyol 0.1.0 → 0.2.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/LICENSE +21 -0
- package/dist/index.cjs +582 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +562 -1
- package/dist/index.d.ts +562 -1
- package/dist/index.js +579 -4
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TokenBucketRateLimiter, noopLogger, retry, TimeoutError, NetworkError, LoncaError, ServerError, RateLimitError, NotFoundError,
|
|
1
|
+
import { TokenBucketRateLimiter, ValidationError, noopLogger, retry, TimeoutError, NetworkError, LoncaError, ServerError, RateLimitError, NotFoundError, AuthError } from '@lonca/core';
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
|
|
4
4
|
// src/resources/brands.ts
|
|
@@ -26,7 +26,7 @@ var BrandsResource = class {
|
|
|
26
26
|
const size = params.limit ?? DEFAULT_PAGE_SIZE;
|
|
27
27
|
const data = await this.transport.request({
|
|
28
28
|
method: "GET",
|
|
29
|
-
path: "/
|
|
29
|
+
path: "/integration/product/brands",
|
|
30
30
|
query: { page, size },
|
|
31
31
|
rateLimiter: this.limiter
|
|
32
32
|
});
|
|
@@ -35,6 +35,576 @@ var BrandsResource = class {
|
|
|
35
35
|
return nextCursor !== void 0 ? { items, nextCursor } : { items };
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
+
function normalizeCategory(node) {
|
|
39
|
+
return {
|
|
40
|
+
id: String(node.id),
|
|
41
|
+
name: node.name,
|
|
42
|
+
parentId: node.parentId === null || node.parentId === void 0 ? null : String(node.parentId),
|
|
43
|
+
subCategories: (node.subCategories ?? []).map(normalizeCategory)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function normalizeAttribute(node) {
|
|
47
|
+
const attr = node.attribute ?? { id: 0, name: "" };
|
|
48
|
+
const rawValues = node.attributeValues ?? [];
|
|
49
|
+
const out = {
|
|
50
|
+
id: String(attr.id),
|
|
51
|
+
name: attr.name,
|
|
52
|
+
required: !!node.required,
|
|
53
|
+
allowCustom: !!node.allowCustom,
|
|
54
|
+
varianter: !!node.varianter,
|
|
55
|
+
slicer: !!node.slicer,
|
|
56
|
+
values: rawValues.map((v) => ({ id: String(v.id), name: v.name }))
|
|
57
|
+
};
|
|
58
|
+
if (node.categoryId !== void 0) {
|
|
59
|
+
out.categoryId = String(node.categoryId);
|
|
60
|
+
}
|
|
61
|
+
if (node.allowMultipleAttributeValues !== void 0) {
|
|
62
|
+
out.allowMultipleAttributeValues = node.allowMultipleAttributeValues;
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
var CategoriesResource = class {
|
|
67
|
+
constructor(transport, limiter) {
|
|
68
|
+
this.transport = transport;
|
|
69
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 50, intervalMs: 6e4 });
|
|
70
|
+
}
|
|
71
|
+
transport;
|
|
72
|
+
limiter;
|
|
73
|
+
/**
|
|
74
|
+
* Fetch the full Trendyol category tree.
|
|
75
|
+
*
|
|
76
|
+
* Trendyol returns the entire tree in one response — there is no pagination.
|
|
77
|
+
* Cache the result aggressively in your application; the tree changes rarely.
|
|
78
|
+
*/
|
|
79
|
+
async list() {
|
|
80
|
+
const data = await this.transport.request({
|
|
81
|
+
method: "GET",
|
|
82
|
+
path: "/integration/product/product-categories",
|
|
83
|
+
rateLimiter: this.limiter
|
|
84
|
+
});
|
|
85
|
+
const list = Array.isArray(data) ? data : data.categories;
|
|
86
|
+
return list.map(normalizeCategory);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Fetch the attributes (required and optional) for a single category.
|
|
90
|
+
*
|
|
91
|
+
* Call this before `createProduct V2` so you know which attributes are
|
|
92
|
+
* mandatory — the API rejects products that omit any `required` attribute.
|
|
93
|
+
*
|
|
94
|
+
* @param categoryId Trendyol numeric category ID; accepts `string` or `number`.
|
|
95
|
+
*/
|
|
96
|
+
async getAttributes(categoryId) {
|
|
97
|
+
const id = encodeURIComponent(String(categoryId));
|
|
98
|
+
const data = await this.transport.request({
|
|
99
|
+
method: "GET",
|
|
100
|
+
path: `/integration/product/categories/${id}/attributes`,
|
|
101
|
+
rateLimiter: this.limiter
|
|
102
|
+
});
|
|
103
|
+
const list = extractAttributeList(data);
|
|
104
|
+
if (!list) {
|
|
105
|
+
console.error(
|
|
106
|
+
"[@lonca/trendyol] getCategoryAttributes: unexpected response shape:",
|
|
107
|
+
JSON.stringify(data).slice(0, 800)
|
|
108
|
+
);
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
return list.map(normalizeAttribute);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function extractAttributeList(data) {
|
|
115
|
+
if (Array.isArray(data)) return data;
|
|
116
|
+
if (data && typeof data === "object") {
|
|
117
|
+
const obj = data;
|
|
118
|
+
if (Array.isArray(obj.categoryAttributes)) {
|
|
119
|
+
return obj.categoryAttributes;
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(obj.attributes)) {
|
|
122
|
+
return obj.attributes;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
var MAX_ITEMS_PER_REQUEST = 1e3;
|
|
128
|
+
var InventoryResource = class {
|
|
129
|
+
constructor(transport, sellerId) {
|
|
130
|
+
this.transport = transport;
|
|
131
|
+
this.sellerId = sellerId;
|
|
132
|
+
}
|
|
133
|
+
transport;
|
|
134
|
+
sellerId;
|
|
135
|
+
/**
|
|
136
|
+
* Update price and/or stock for one or more SKUs (by barcode).
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const { batchRequestId } = await client.inventory.update([
|
|
141
|
+
* { barcode: 'ABC123', quantity: 42, salePrice: 199.9, listPrice: 249.9 },
|
|
142
|
+
* { barcode: 'XYZ789', quantity: 0 },
|
|
143
|
+
* ]);
|
|
144
|
+
* const status = await client.products.getBatchStatus(batchRequestId);
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* @throws {ValidationError} when `items` is empty or longer than 1000.
|
|
148
|
+
*/
|
|
149
|
+
async update(items) {
|
|
150
|
+
if (items.length === 0) {
|
|
151
|
+
throw new ValidationError({ message: "updatePriceAndInventory: items must not be empty" });
|
|
152
|
+
}
|
|
153
|
+
if (items.length > MAX_ITEMS_PER_REQUEST) {
|
|
154
|
+
throw new ValidationError({
|
|
155
|
+
message: `updatePriceAndInventory: max ${MAX_ITEMS_PER_REQUEST} items per request, got ${items.length}`
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const data = await this.transport.request({
|
|
159
|
+
method: "POST",
|
|
160
|
+
path: `/integration/inventory/sellers/${this.sellerId}/products/price-and-inventory`,
|
|
161
|
+
body: { items }
|
|
162
|
+
});
|
|
163
|
+
return { batchRequestId: data.batchRequestId ?? "" };
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var MAX_PAGE_SIZE = 200;
|
|
167
|
+
var DEFAULT_PAGE_SIZE2 = 50;
|
|
168
|
+
function toIso(epochMs) {
|
|
169
|
+
if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
return new Date(epochMs).toISOString();
|
|
173
|
+
}
|
|
174
|
+
function normalizeAddress(node) {
|
|
175
|
+
if (!node) return void 0;
|
|
176
|
+
const out = {};
|
|
177
|
+
if (node.id !== void 0) out.id = String(node.id);
|
|
178
|
+
if (node.firstName !== void 0) out.firstName = node.firstName;
|
|
179
|
+
if (node.lastName !== void 0) out.lastName = node.lastName;
|
|
180
|
+
if (node.fullName !== void 0) out.fullName = node.fullName;
|
|
181
|
+
if (node.company !== void 0) out.company = node.company;
|
|
182
|
+
if (node.address1 !== void 0) out.address1 = node.address1;
|
|
183
|
+
if (node.address2 !== void 0) out.address2 = node.address2;
|
|
184
|
+
if (node.fullAddress !== void 0) out.fullAddress = node.fullAddress;
|
|
185
|
+
if (node.shortAddress !== void 0) out.shortAddress = node.shortAddress;
|
|
186
|
+
if (node.city !== void 0) out.city = node.city;
|
|
187
|
+
if (node.cityCode !== void 0) out.cityCode = node.cityCode;
|
|
188
|
+
if (node.district !== void 0) out.district = node.district;
|
|
189
|
+
if (node.districtId !== void 0) out.districtId = node.districtId;
|
|
190
|
+
if (node.neighborhoodId !== void 0) out.neighborhoodId = node.neighborhoodId;
|
|
191
|
+
if (node.countyId !== void 0) out.countyId = node.countyId;
|
|
192
|
+
if (node.countyName !== void 0) out.countyName = node.countyName;
|
|
193
|
+
if (node.stateName !== void 0) out.stateName = node.stateName;
|
|
194
|
+
if (node.postalCode !== void 0) out.postalCode = node.postalCode;
|
|
195
|
+
if (node.countryCode !== void 0) out.countryCode = node.countryCode;
|
|
196
|
+
if (node.phone !== void 0) out.phone = node.phone;
|
|
197
|
+
if (node.addressLines !== void 0) out.addressLines = node.addressLines;
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
function normalizeLine(node) {
|
|
201
|
+
const out = {
|
|
202
|
+
id: node.lineId !== void 0 ? String(node.lineId) : "",
|
|
203
|
+
quantity: typeof node.quantity === "number" ? node.quantity : 0,
|
|
204
|
+
productName: node.productName ?? "",
|
|
205
|
+
barcode: node.barcode ?? "",
|
|
206
|
+
lineUnitPrice: typeof node.lineUnitPrice === "number" ? node.lineUnitPrice : 0,
|
|
207
|
+
lineGrossAmount: typeof node.lineGrossAmount === "number" ? node.lineGrossAmount : 0,
|
|
208
|
+
raw: node
|
|
209
|
+
};
|
|
210
|
+
if (node.productSize !== void 0) out.productSize = node.productSize;
|
|
211
|
+
if (node.productColor !== void 0) out.productColor = node.productColor;
|
|
212
|
+
if (node.stockCode !== void 0) out.stockCode = node.stockCode;
|
|
213
|
+
if (node.contentId !== void 0) out.contentId = String(node.contentId);
|
|
214
|
+
if (node.sellerId !== void 0) out.sellerId = String(node.sellerId);
|
|
215
|
+
if (node.productCategoryId !== void 0) out.productCategoryId = String(node.productCategoryId);
|
|
216
|
+
if (node.salesCampaignId !== void 0) out.salesCampaignId = String(node.salesCampaignId);
|
|
217
|
+
if (node.currencyCode !== void 0) out.currencyCode = node.currencyCode;
|
|
218
|
+
if (node.lineSellerDiscount !== void 0) out.lineSellerDiscount = node.lineSellerDiscount;
|
|
219
|
+
if (node.lineTyDiscount !== void 0) out.lineTyDiscount = node.lineTyDiscount;
|
|
220
|
+
if (node.lineTotalDiscount !== void 0) out.lineTotalDiscount = node.lineTotalDiscount;
|
|
221
|
+
if (node.vatRate !== void 0) out.vatRate = node.vatRate;
|
|
222
|
+
if (node.commission !== void 0) out.commission = node.commission;
|
|
223
|
+
if (node.orderLineItemStatusName !== void 0) {
|
|
224
|
+
out.orderLineItemStatusName = node.orderLineItemStatusName;
|
|
225
|
+
}
|
|
226
|
+
if (node.businessUnit !== void 0) out.businessUnit = node.businessUnit;
|
|
227
|
+
if (Array.isArray(node.fastDeliveryOptions)) out.fastDeliveryOptions = node.fastDeliveryOptions;
|
|
228
|
+
if (Array.isArray(node.discountDetails)) {
|
|
229
|
+
out.discountDetails = node.discountDetails;
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
function normalizeHistory(node) {
|
|
234
|
+
const out = { raw: node };
|
|
235
|
+
if (node.status !== void 0) out.status = node.status;
|
|
236
|
+
const createdAt = toIso(node.createdDate);
|
|
237
|
+
if (createdAt) out.createdAt = createdAt;
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
function normalizeCustomer(node) {
|
|
241
|
+
const out = {
|
|
242
|
+
firstName: node.customerFirstName ?? "",
|
|
243
|
+
lastName: node.customerLastName ?? ""
|
|
244
|
+
};
|
|
245
|
+
if (node.customerId !== void 0) out.id = String(node.customerId);
|
|
246
|
+
if (node.customerEmail !== void 0) out.email = node.customerEmail;
|
|
247
|
+
if (node.taxNumber !== void 0 && node.taxNumber !== null) out.taxNumber = node.taxNumber;
|
|
248
|
+
if (node.identityNumber !== void 0) out.identityNumber = node.identityNumber;
|
|
249
|
+
return out;
|
|
250
|
+
}
|
|
251
|
+
function normalizePackage(node) {
|
|
252
|
+
const out = {
|
|
253
|
+
id: node.shipmentPackageId !== void 0 ? String(node.shipmentPackageId) : "",
|
|
254
|
+
orderNumber: node.orderNumber ?? "",
|
|
255
|
+
status: node.status ?? "",
|
|
256
|
+
customer: normalizeCustomer(node),
|
|
257
|
+
orderDate: toIso(node.orderDate) ?? "",
|
|
258
|
+
lastModifiedDate: toIso(node.lastModifiedDate) ?? "",
|
|
259
|
+
currencyCode: node.currencyCode ?? "",
|
|
260
|
+
packageTotalPrice: typeof node.packageTotalPrice === "number" ? node.packageTotalPrice : 0,
|
|
261
|
+
packageGrossAmount: typeof node.packageGrossAmount === "number" ? node.packageGrossAmount : 0,
|
|
262
|
+
packageSellerDiscount: typeof node.packageSellerDiscount === "number" ? node.packageSellerDiscount : 0,
|
|
263
|
+
packageTyDiscount: typeof node.packageTyDiscount === "number" ? node.packageTyDiscount : 0,
|
|
264
|
+
packageTotalDiscount: typeof node.packageTotalDiscount === "number" ? node.packageTotalDiscount : 0,
|
|
265
|
+
lines: (node.lines ?? []).map(normalizeLine),
|
|
266
|
+
packageHistories: (node.packageHistories ?? []).map(normalizeHistory),
|
|
267
|
+
raw: node
|
|
268
|
+
};
|
|
269
|
+
if (node.shipmentNumber !== void 0) out.shipmentNumber = String(node.shipmentNumber);
|
|
270
|
+
if (node.originPackageIds !== void 0 && node.originPackageIds !== null) {
|
|
271
|
+
out.originPackageIds = node.originPackageIds.map((id) => String(id));
|
|
272
|
+
}
|
|
273
|
+
if (node.warehouseId !== void 0) out.warehouseId = String(node.warehouseId);
|
|
274
|
+
if (node.supplierId !== void 0) out.supplierId = String(node.supplierId);
|
|
275
|
+
if (node.shipmentPackageStatus !== void 0) {
|
|
276
|
+
out.shipmentPackageStatus = node.shipmentPackageStatus;
|
|
277
|
+
}
|
|
278
|
+
const agreed = toIso(node.agreedDeliveryDate);
|
|
279
|
+
if (agreed) out.agreedDeliveryDate = agreed;
|
|
280
|
+
const estStart = toIso(node.estimatedDeliveryStartDate);
|
|
281
|
+
if (estStart) out.estimatedDeliveryStartDate = estStart;
|
|
282
|
+
const estEnd = toIso(node.estimatedDeliveryEndDate);
|
|
283
|
+
if (estEnd) out.estimatedDeliveryEndDate = estEnd;
|
|
284
|
+
const origin = toIso(node.originShipmentDate);
|
|
285
|
+
if (origin) out.originShipmentDate = origin;
|
|
286
|
+
const invoice = normalizeAddress(node.invoiceAddress);
|
|
287
|
+
if (invoice) out.invoiceAddress = invoice;
|
|
288
|
+
const shipment = normalizeAddress(node.shipmentAddress);
|
|
289
|
+
if (shipment) out.shipmentAddress = shipment;
|
|
290
|
+
if (node.deliveryAddressType !== void 0) out.deliveryAddressType = node.deliveryAddressType;
|
|
291
|
+
if (node.cargoTrackingNumber !== void 0) {
|
|
292
|
+
out.cargoTrackingNumber = String(node.cargoTrackingNumber);
|
|
293
|
+
}
|
|
294
|
+
if (node.cargoProviderName !== void 0) out.cargoProviderName = node.cargoProviderName;
|
|
295
|
+
if (node.cargoProviderId !== void 0) out.cargoProviderId = String(node.cargoProviderId);
|
|
296
|
+
if (node.cargoSenderNumber !== void 0) out.cargoSenderNumber = node.cargoSenderNumber;
|
|
297
|
+
if (node.deliveryType !== void 0) out.deliveryType = node.deliveryType;
|
|
298
|
+
if (node.whoPays !== void 0) out.whoPays = node.whoPays;
|
|
299
|
+
if (node.timeSlotId !== void 0) out.timeSlotId = node.timeSlotId;
|
|
300
|
+
if (typeof node.fastDelivery === "boolean") out.fastDelivery = node.fastDelivery;
|
|
301
|
+
if (node.fastDeliveryType !== void 0) out.fastDeliveryType = node.fastDeliveryType;
|
|
302
|
+
if (typeof node.deliveredByService === "boolean")
|
|
303
|
+
out.deliveredByService = node.deliveredByService;
|
|
304
|
+
if (typeof node.commercial === "boolean") out.commercial = node.commercial;
|
|
305
|
+
if (typeof node.micro === "boolean") out.micro = node.micro;
|
|
306
|
+
if (typeof node.giftBoxRequested === "boolean") out.giftBoxRequested = node.giftBoxRequested;
|
|
307
|
+
if (typeof node["3pByTrendyol"] === "boolean") {
|
|
308
|
+
out.threePByTrendyol = node["3pByTrendyol"];
|
|
309
|
+
}
|
|
310
|
+
if (typeof node.containsDangerousProduct === "boolean") {
|
|
311
|
+
out.containsDangerousProduct = node.containsDangerousProduct;
|
|
312
|
+
}
|
|
313
|
+
if (typeof node.isCod === "boolean") out.isCod = node.isCod;
|
|
314
|
+
if (typeof node.is4P === "boolean") out.is4P = node.is4P;
|
|
315
|
+
if (node.invoiceLink !== void 0) out.invoiceLink = node.invoiceLink;
|
|
316
|
+
if (node.createdBy !== void 0) out.createdBy = node.createdBy;
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
var OrdersResource = class {
|
|
320
|
+
constructor(transport, sellerId, limiter) {
|
|
321
|
+
this.transport = transport;
|
|
322
|
+
this.sellerId = sellerId;
|
|
323
|
+
this.limiter = limiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
|
|
324
|
+
}
|
|
325
|
+
transport;
|
|
326
|
+
sellerId;
|
|
327
|
+
limiter;
|
|
328
|
+
/**
|
|
329
|
+
* List shipment packages for the seller.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```ts
|
|
333
|
+
* import { paginate } from '@lonca/core';
|
|
334
|
+
* for await (const pkg of paginate((p) => client.orders.list({ ...p, status: 'Created' }))) {
|
|
335
|
+
* console.log(pkg.id, pkg.status, pkg.customer.firstName);
|
|
336
|
+
* }
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
async list(params = {}) {
|
|
340
|
+
const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
|
|
341
|
+
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
342
|
+
const query = { page, size };
|
|
343
|
+
if (params.status) query.status = params.status;
|
|
344
|
+
if (params.orderNumber) query.orderNumber = params.orderNumber;
|
|
345
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
346
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
347
|
+
const data = await this.transport.request({
|
|
348
|
+
method: "GET",
|
|
349
|
+
path: `/integration/order/sellers/${this.sellerId}/orders`,
|
|
350
|
+
query,
|
|
351
|
+
rateLimiter: this.limiter
|
|
352
|
+
});
|
|
353
|
+
const items = (data.content ?? []).map(normalizePackage);
|
|
354
|
+
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
355
|
+
const result = { items };
|
|
356
|
+
if (page + 1 < totalPages) {
|
|
357
|
+
result.nextCursor = String(page + 1);
|
|
358
|
+
}
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
var DEFAULT_PAGE_SIZE3 = 50;
|
|
363
|
+
var MAX_PAGE_SIZE2 = 1e3;
|
|
364
|
+
function normalizeRef(ref) {
|
|
365
|
+
return {
|
|
366
|
+
id: ref?.id !== void 0 ? String(ref.id) : "",
|
|
367
|
+
name: ref?.name ?? ""
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function normalizeAttribute2(attr) {
|
|
371
|
+
const out = {
|
|
372
|
+
attributeId: attr.attributeId !== void 0 ? String(attr.attributeId) : ""
|
|
373
|
+
};
|
|
374
|
+
if (attr.attributeName !== void 0) out.attributeName = attr.attributeName;
|
|
375
|
+
if (attr.attributeValueId !== void 0) {
|
|
376
|
+
out.attributeValueId = String(attr.attributeValueId);
|
|
377
|
+
}
|
|
378
|
+
if (attr.attributeValue !== void 0) out.attributeValue = attr.attributeValue;
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
function normalizeImages(images) {
|
|
382
|
+
if (!images) return [];
|
|
383
|
+
return images.map((img) => typeof img === "string" ? img : img?.url).filter((u) => typeof u === "string" && u.length > 0);
|
|
384
|
+
}
|
|
385
|
+
function normalizeStock(stock) {
|
|
386
|
+
if (typeof stock === "number") return stock;
|
|
387
|
+
if (stock && typeof stock === "object" && typeof stock.quantity === "number") {
|
|
388
|
+
return stock.quantity;
|
|
389
|
+
}
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
function normalizeVariant(variant) {
|
|
393
|
+
const out = {
|
|
394
|
+
variantId: variant.variantId !== void 0 ? String(variant.variantId) : "",
|
|
395
|
+
barcode: variant.barcode ?? "",
|
|
396
|
+
attributes: (variant.attributes ?? []).map(normalizeAttribute2),
|
|
397
|
+
raw: variant
|
|
398
|
+
};
|
|
399
|
+
if (typeof variant.commission === "number") out.commission = variant.commission;
|
|
400
|
+
if (variant.productUrl !== void 0) out.productUrl = variant.productUrl;
|
|
401
|
+
if (typeof variant.onSale === "boolean") out.onSale = variant.onSale;
|
|
402
|
+
const stock = normalizeStock(variant.stock);
|
|
403
|
+
if (stock !== void 0) out.stock = stock;
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
function toIso2(epochMs) {
|
|
407
|
+
if (typeof epochMs !== "number" || !Number.isFinite(epochMs)) return void 0;
|
|
408
|
+
return new Date(epochMs).toISOString();
|
|
409
|
+
}
|
|
410
|
+
function normalizeProduct(node) {
|
|
411
|
+
const out = {
|
|
412
|
+
contentId: node.contentId !== void 0 ? String(node.contentId) : "",
|
|
413
|
+
productMainId: node.productMainId ?? "",
|
|
414
|
+
title: node.title ?? "",
|
|
415
|
+
brand: normalizeRef(node.brand),
|
|
416
|
+
category: normalizeRef(node.category),
|
|
417
|
+
images: normalizeImages(node.images),
|
|
418
|
+
attributes: (node.attributes ?? []).map(normalizeAttribute2),
|
|
419
|
+
variants: (node.variants ?? []).map(normalizeVariant),
|
|
420
|
+
createdAt: toIso2(node.creationDate) ?? "",
|
|
421
|
+
updatedAt: toIso2(node.lastModifiedDate) ?? "",
|
|
422
|
+
raw: node
|
|
423
|
+
};
|
|
424
|
+
if (node.description !== void 0) out.description = node.description;
|
|
425
|
+
if (node.lastModifiedBy !== void 0) out.lastModifiedBy = node.lastModifiedBy;
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
function normalizeBatchResult(data) {
|
|
429
|
+
const items = (data.items ?? []).map((item) => {
|
|
430
|
+
const result = {};
|
|
431
|
+
if (item.requestItem !== void 0) result.requestItem = item.requestItem;
|
|
432
|
+
if (item.status !== void 0) result.status = item.status;
|
|
433
|
+
if (item.failureReasons !== void 0) result.failureReasons = item.failureReasons;
|
|
434
|
+
return result;
|
|
435
|
+
});
|
|
436
|
+
const out = {
|
|
437
|
+
batchRequestId: typeof data.batchRequestId === "string" ? data.batchRequestId : "",
|
|
438
|
+
status: data.status ?? "PROCESSING",
|
|
439
|
+
items,
|
|
440
|
+
raw: data
|
|
441
|
+
};
|
|
442
|
+
if (typeof data.itemCount === "number") out.itemCount = data.itemCount;
|
|
443
|
+
if (typeof data.failedItemCount === "number") out.failedItemCount = data.failedItemCount;
|
|
444
|
+
const createdAt = toIso2(data.creationDate);
|
|
445
|
+
if (createdAt) out.createdAt = createdAt;
|
|
446
|
+
const lastModifiedAt = toIso2(data.lastModification);
|
|
447
|
+
if (lastModifiedAt) out.lastModifiedAt = lastModifiedAt;
|
|
448
|
+
if (data.sourceType !== void 0) out.sourceType = data.sourceType;
|
|
449
|
+
if (data.batchRequestType !== void 0) out.batchRequestType = data.batchRequestType;
|
|
450
|
+
if (data.notes !== void 0) out.notes = data.notes;
|
|
451
|
+
if (data.objectKey !== void 0) out.objectKey = data.objectKey;
|
|
452
|
+
if (data.storeFrontCode !== void 0) out.storeFrontCode = data.storeFrontCode;
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
455
|
+
var ProductsResource = class {
|
|
456
|
+
constructor(transport, sellerId, options = {}) {
|
|
457
|
+
this.transport = transport;
|
|
458
|
+
this.sellerId = sellerId;
|
|
459
|
+
this.filterLimiter = options.filterLimiter ?? new TokenBucketRateLimiter({ capacity: 2e3, intervalMs: 6e4 });
|
|
460
|
+
this.batchLimiter = options.batchLimiter ?? new TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
|
|
461
|
+
}
|
|
462
|
+
transport;
|
|
463
|
+
sellerId;
|
|
464
|
+
filterLimiter;
|
|
465
|
+
batchLimiter;
|
|
466
|
+
/**
|
|
467
|
+
* List approved products. Use `paginate()` from `@lonca/core` to iterate
|
|
468
|
+
* lazily across pages.
|
|
469
|
+
*
|
|
470
|
+
* Trendyol exposes both page-based and `nextPageToken`-based pagination
|
|
471
|
+
* (the latter required when the dataset exceeds 10,000 items). The SDK
|
|
472
|
+
* picks the right strategy automatically — pass our opaque `cursor` from
|
|
473
|
+
* the previous response and we forward it as `nextPageToken`.
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```ts
|
|
477
|
+
* import { paginate } from '@lonca/core';
|
|
478
|
+
* for await (const product of paginate((p) => client.products.list(p))) {
|
|
479
|
+
* for (const variant of product.variants) {
|
|
480
|
+
* console.log(variant.barcode, product.title);
|
|
481
|
+
* }
|
|
482
|
+
* }
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
async list(params = {}) {
|
|
486
|
+
const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE3, MAX_PAGE_SIZE2);
|
|
487
|
+
const query = { size };
|
|
488
|
+
if (params.cursor) {
|
|
489
|
+
query.nextPageToken = params.cursor;
|
|
490
|
+
} else {
|
|
491
|
+
query.page = 0;
|
|
492
|
+
}
|
|
493
|
+
if (params.barcode) query.barcode = params.barcode;
|
|
494
|
+
if (params.startDate) query.startDate = params.startDate.getTime();
|
|
495
|
+
if (params.endDate) query.endDate = params.endDate.getTime();
|
|
496
|
+
const data = await this.transport.request({
|
|
497
|
+
method: "GET",
|
|
498
|
+
path: `/integration/product/sellers/${this.sellerId}/products/approved`,
|
|
499
|
+
query,
|
|
500
|
+
rateLimiter: this.filterLimiter
|
|
501
|
+
});
|
|
502
|
+
const items = (data.content ?? []).map(normalizeProduct);
|
|
503
|
+
const page = { items };
|
|
504
|
+
if (data.nextPageToken) {
|
|
505
|
+
page.nextCursor = data.nextPageToken;
|
|
506
|
+
}
|
|
507
|
+
return page;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Poll a batch request returned by an async write (e.g. `createProducts`,
|
|
511
|
+
* `updatePriceAndInventory`).
|
|
512
|
+
*
|
|
513
|
+
* Trendyol retains batch results for **4 hours** after the originating
|
|
514
|
+
* request — poll within that window.
|
|
515
|
+
*
|
|
516
|
+
* @param batchRequestId The opaque ID returned by the originating call.
|
|
517
|
+
*/
|
|
518
|
+
async getBatchStatus(batchRequestId) {
|
|
519
|
+
const id = encodeURIComponent(batchRequestId);
|
|
520
|
+
const data = await this.transport.request({
|
|
521
|
+
method: "GET",
|
|
522
|
+
path: `/integration/product/sellers/${this.sellerId}/products/batch-requests/${id}`,
|
|
523
|
+
rateLimiter: this.batchLimiter
|
|
524
|
+
});
|
|
525
|
+
return normalizeBatchResult(data);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
529
|
+
"SHIPMENT",
|
|
530
|
+
"RETURNING",
|
|
531
|
+
"INVOICE",
|
|
532
|
+
"WAREHOUSE"
|
|
533
|
+
]);
|
|
534
|
+
function normalizeAddressType(raw) {
|
|
535
|
+
if (raw && VALID_TYPES.has(raw)) {
|
|
536
|
+
return raw;
|
|
537
|
+
}
|
|
538
|
+
const stripped = raw?.replace(/_ADDRESS$/, "");
|
|
539
|
+
if (stripped && VALID_TYPES.has(stripped)) {
|
|
540
|
+
return stripped;
|
|
541
|
+
}
|
|
542
|
+
return "SHIPMENT";
|
|
543
|
+
}
|
|
544
|
+
function normalizeAddress2(node) {
|
|
545
|
+
return {
|
|
546
|
+
id: String(node.id),
|
|
547
|
+
name: node.name,
|
|
548
|
+
addressType: normalizeAddressType(node.addressType),
|
|
549
|
+
isShipmentAddress: node.isShipmentAddress ?? false,
|
|
550
|
+
isReturningAddress: node.isReturningAddress ?? false,
|
|
551
|
+
isInvoiceAddress: node.isInvoiceAddress ?? false,
|
|
552
|
+
isDefault: node.isDefault ?? false,
|
|
553
|
+
address: node.address,
|
|
554
|
+
city: node.city,
|
|
555
|
+
district: node.district,
|
|
556
|
+
postCode: node.postCode,
|
|
557
|
+
fullName: node.fullName
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
var DEFAULT_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
561
|
+
var SuppliersResource = class {
|
|
562
|
+
constructor(transport, sellerId, options = {}) {
|
|
563
|
+
this.transport = transport;
|
|
564
|
+
this.sellerId = sellerId;
|
|
565
|
+
this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
566
|
+
this.limiter = new TokenBucketRateLimiter({
|
|
567
|
+
capacity: 1,
|
|
568
|
+
intervalMs: DEFAULT_CACHE_TTL_MS
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
transport;
|
|
572
|
+
sellerId;
|
|
573
|
+
cache = null;
|
|
574
|
+
cacheTtlMs;
|
|
575
|
+
limiter;
|
|
576
|
+
inflight = null;
|
|
577
|
+
/**
|
|
578
|
+
* List the seller's registered addresses (shipment, returning, invoice, warehouse).
|
|
579
|
+
*
|
|
580
|
+
* Returns the cached value if it is still fresh. Concurrent calls share a
|
|
581
|
+
* single in-flight request.
|
|
582
|
+
*/
|
|
583
|
+
async getAddresses(options = {}) {
|
|
584
|
+
if (!options.forceRefresh && this.cache && this.cache.expiresAt > Date.now()) {
|
|
585
|
+
return this.cache.value;
|
|
586
|
+
}
|
|
587
|
+
if (this.inflight) return this.inflight;
|
|
588
|
+
this.inflight = this.fetchFresh().finally(() => {
|
|
589
|
+
this.inflight = null;
|
|
590
|
+
});
|
|
591
|
+
return this.inflight;
|
|
592
|
+
}
|
|
593
|
+
/** Drop the cache; the next `getAddresses()` call hits the API. */
|
|
594
|
+
invalidateCache() {
|
|
595
|
+
this.cache = null;
|
|
596
|
+
}
|
|
597
|
+
async fetchFresh() {
|
|
598
|
+
const data = await this.transport.request({
|
|
599
|
+
method: "GET",
|
|
600
|
+
path: `/integration/sellers/${this.sellerId}/addresses`,
|
|
601
|
+
rateLimiter: this.limiter
|
|
602
|
+
});
|
|
603
|
+
const items = data.supplierAddresses.map(normalizeAddress2);
|
|
604
|
+
this.cache = { value: items, expiresAt: Date.now() + this.cacheTtlMs };
|
|
605
|
+
return items;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
38
608
|
|
|
39
609
|
// src/auth.ts
|
|
40
610
|
function buildAuthHeader(apiKey, apiSecret) {
|
|
@@ -237,10 +807,15 @@ function createTrendyolClient(opts) {
|
|
|
237
807
|
timeoutMs: opts.timeoutMs
|
|
238
808
|
});
|
|
239
809
|
return {
|
|
240
|
-
brands: new BrandsResource(transport)
|
|
810
|
+
brands: new BrandsResource(transport),
|
|
811
|
+
categories: new CategoriesResource(transport),
|
|
812
|
+
suppliers: new SuppliersResource(transport, opts.sellerId),
|
|
813
|
+
products: new ProductsResource(transport, opts.sellerId),
|
|
814
|
+
inventory: new InventoryResource(transport, opts.sellerId),
|
|
815
|
+
orders: new OrdersResource(transport, opts.sellerId)
|
|
241
816
|
};
|
|
242
817
|
}
|
|
243
818
|
|
|
244
|
-
export { BrandsResource, createTrendyolClient };
|
|
819
|
+
export { BrandsResource, CategoriesResource, InventoryResource, OrdersResource, ProductsResource, SuppliersResource, createTrendyolClient };
|
|
245
820
|
//# sourceMappingURL=index.js.map
|
|
246
821
|
//# sourceMappingURL=index.js.map
|