@lonca/trendyol 0.10.0 → 0.11.1
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 +18 -33
- package/dist/{client-0omWgpd_.d.cts → client-CCA1P7sb.d.cts} +117 -18
- package/dist/{client-0omWgpd_.d.ts → client-CCA1P7sb.d.ts} +117 -18
- package/dist/index.cjs +104 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +105 -108
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +104 -107
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +105 -108
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@lonca/core');
|
|
4
|
-
var crypto = require('crypto');
|
|
5
4
|
|
|
6
5
|
// src/resources/brands.ts
|
|
7
6
|
var DEFAULT_PAGE_SIZE = 1e3;
|
|
@@ -41,7 +40,7 @@ var BrandsResource = class {
|
|
|
41
40
|
* `createProducts` and don't want to page through the full `list()`
|
|
42
41
|
* (1000 brands per page).
|
|
43
42
|
*
|
|
44
|
-
* **
|
|
43
|
+
* **Wire fact (verified STAGE 2026-05-25):** Trendyol's
|
|
45
44
|
* doc claims this is a case-sensitive *exact* match, but live behaviour
|
|
46
45
|
* is **substring + case-insensitive** — `search('Trendyol')` returns
|
|
47
46
|
* 17 hits including `TRENDYOLMILLA`, `trendyol vavist`, `Trendyol Üyelik`.
|
|
@@ -1066,6 +1065,7 @@ var LocationsResource = class {
|
|
|
1066
1065
|
};
|
|
1067
1066
|
var MAX_PAGE_SIZE = 200;
|
|
1068
1067
|
var DEFAULT_PAGE_SIZE2 = 50;
|
|
1068
|
+
var MAX_OFFSET_RECORDS = 1e4;
|
|
1069
1069
|
function toIso3(epochMs) {
|
|
1070
1070
|
if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
|
|
1071
1071
|
return void 0;
|
|
@@ -1242,6 +1242,11 @@ var OrdersResource = class {
|
|
|
1242
1242
|
async list(params = {}) {
|
|
1243
1243
|
const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
|
|
1244
1244
|
const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
|
|
1245
|
+
if ((page + 1) * size > MAX_OFFSET_RECORDS) {
|
|
1246
|
+
throw new core.ValidationError({
|
|
1247
|
+
message: `getShipmentPackages can reach at most ${MAX_OFFSET_RECORDS} records (page ${page} \xD7 size ${size} exceeds it); use listStream() for full scans`
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1245
1250
|
const query = { page, size };
|
|
1246
1251
|
if (params.status) query.status = params.status;
|
|
1247
1252
|
if (params.orderNumber) query.orderNumber = params.orderNumber;
|
|
@@ -1256,7 +1261,8 @@ var OrdersResource = class {
|
|
|
1256
1261
|
const items = (data.content ?? []).map(normalizePackage2);
|
|
1257
1262
|
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
1258
1263
|
const result = { items };
|
|
1259
|
-
|
|
1264
|
+
const nextPageWithinCap = (page + 2) * size <= MAX_OFFSET_RECORDS;
|
|
1265
|
+
if (page + 1 < totalPages && nextPageWithinCap) {
|
|
1260
1266
|
result.nextCursor = String(page + 1);
|
|
1261
1267
|
}
|
|
1262
1268
|
return result;
|
|
@@ -1695,6 +1701,7 @@ var OrdersResource = class {
|
|
|
1695
1701
|
};
|
|
1696
1702
|
var DEFAULT_PAGE_SIZE3 = 50;
|
|
1697
1703
|
var MAX_PAGE_SIZE2 = 1e3;
|
|
1704
|
+
var MAX_INVENTORY_PAGE_SIZE = 100;
|
|
1698
1705
|
var MAX_BUYBOX_BARCODES = 10;
|
|
1699
1706
|
function normalizeRef(ref) {
|
|
1700
1707
|
return {
|
|
@@ -1760,6 +1767,30 @@ function normalizeProduct(node) {
|
|
|
1760
1767
|
if (node.lastModifiedBy !== void 0) out.lastModifiedBy = node.lastModifiedBy;
|
|
1761
1768
|
return out;
|
|
1762
1769
|
}
|
|
1770
|
+
function normalizeStockPriceVariant(node) {
|
|
1771
|
+
const out = {
|
|
1772
|
+
variantId: node.variantId !== void 0 ? String(node.variantId) : "",
|
|
1773
|
+
barcode: node.barcode ?? "",
|
|
1774
|
+
raw: node
|
|
1775
|
+
};
|
|
1776
|
+
if (typeof node.salePrice === "number") out.salePrice = node.salePrice;
|
|
1777
|
+
if (typeof node.listPrice === "number") out.listPrice = node.listPrice;
|
|
1778
|
+
if (typeof node.quantity === "number") out.quantity = node.quantity;
|
|
1779
|
+
if (node.stockCode !== void 0) out.stockCode = node.stockCode;
|
|
1780
|
+
if (node.stockLastModifiedDate !== void 0 && node.stockLastModifiedDate !== null) {
|
|
1781
|
+
const iso = toIso4(node.stockLastModifiedDate);
|
|
1782
|
+
if (iso) out.stockLastModifiedAt = iso;
|
|
1783
|
+
}
|
|
1784
|
+
return out;
|
|
1785
|
+
}
|
|
1786
|
+
function normalizeStockPrice(node) {
|
|
1787
|
+
return {
|
|
1788
|
+
contentId: node.contentId !== void 0 ? String(node.contentId) : "",
|
|
1789
|
+
productMainId: node.productMainId ?? "",
|
|
1790
|
+
variants: (node.variants ?? []).map(normalizeStockPriceVariant),
|
|
1791
|
+
raw: node
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1763
1794
|
function normalizeUnapprovedRejectReason(r) {
|
|
1764
1795
|
const out = {};
|
|
1765
1796
|
if (r.rejectReason !== void 0) out.rejectReason = r.rejectReason;
|
|
@@ -1950,6 +1981,63 @@ var ProductsResource = class {
|
|
|
1950
1981
|
}
|
|
1951
1982
|
return page;
|
|
1952
1983
|
}
|
|
1984
|
+
/**
|
|
1985
|
+
* List **stock and price** for approved products — Trendyol's lightweight
|
|
1986
|
+
* `inventory-and-price` filter. A slim alternative to {@link list} when you
|
|
1987
|
+
* only need pricing + stock: the response carries `contentId`,
|
|
1988
|
+
* `productMainId`, and a `variants[]` array with `barcode`, `salePrice`,
|
|
1989
|
+
* `listPrice`, `quantity`, `stockCode`, and `stockLastModifiedAt` — nothing
|
|
1990
|
+
* else.
|
|
1991
|
+
*
|
|
1992
|
+
* Filter by `barcode`, `contentId`, `stockCode`, `productMainId`, or listing
|
|
1993
|
+
* `status`. Sort with `orderByDirection` (`SellerCreatedDate`). `size` caps
|
|
1994
|
+
* at **100** here (tighter than `list`'s 1000).
|
|
1995
|
+
*
|
|
1996
|
+
* Pagination follows the same convention as {@link list}: pass our opaque
|
|
1997
|
+
* `cursor` from the previous response and we forward it as `nextPageToken`
|
|
1998
|
+
* (required once the dataset exceeds 10,000 items).
|
|
1999
|
+
*
|
|
2000
|
+
* @example
|
|
2001
|
+
* ```ts
|
|
2002
|
+
* import { paginate } from '@lonca/core';
|
|
2003
|
+
* for await (const p of paginate((q) =>
|
|
2004
|
+
* client.products.listInventoryAndPrice({ ...q, status: 'onSale' }),
|
|
2005
|
+
* )) {
|
|
2006
|
+
* for (const v of p.variants) {
|
|
2007
|
+
* console.log(v.barcode, v.quantity, v.salePrice);
|
|
2008
|
+
* }
|
|
2009
|
+
* }
|
|
2010
|
+
* ```
|
|
2011
|
+
*/
|
|
2012
|
+
async listInventoryAndPrice(params = {}) {
|
|
2013
|
+
const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE3, MAX_INVENTORY_PAGE_SIZE);
|
|
2014
|
+
const query = { size };
|
|
2015
|
+
if (params.cursor) {
|
|
2016
|
+
query.nextPageToken = params.cursor;
|
|
2017
|
+
} else {
|
|
2018
|
+
query.page = 0;
|
|
2019
|
+
}
|
|
2020
|
+
if (params.barcode) query.barcode = params.barcode;
|
|
2021
|
+
if (params.contentId) query.contentId = params.contentId;
|
|
2022
|
+
if (params.stockCode) query.stockCode = params.stockCode;
|
|
2023
|
+
if (params.productMainId) query.productMainId = params.productMainId;
|
|
2024
|
+
if (params.status) query.status = params.status;
|
|
2025
|
+
if (params.orderByDirection) query.orderByDirection = params.orderByDirection;
|
|
2026
|
+
const headers = params.storeFrontCode ? { storeFrontCode: params.storeFrontCode } : void 0;
|
|
2027
|
+
const data = await this.transport.request({
|
|
2028
|
+
method: "GET",
|
|
2029
|
+
path: `/integration/product/sellers/${this.transport.sellerId}/products/approved/inventory-and-price`,
|
|
2030
|
+
query,
|
|
2031
|
+
headers,
|
|
2032
|
+
rateLimiter: this.filterLimiter
|
|
2033
|
+
});
|
|
2034
|
+
const items = (data.content ?? []).map(normalizeStockPrice);
|
|
2035
|
+
const page = { items };
|
|
2036
|
+
if (data.nextPageToken) {
|
|
2037
|
+
page.nextCursor = data.nextPageToken;
|
|
2038
|
+
}
|
|
2039
|
+
return page;
|
|
2040
|
+
}
|
|
1953
2041
|
/**
|
|
1954
2042
|
* Poll a batch request returned by an async write (e.g. `createProducts`,
|
|
1955
2043
|
* `updatePriceAndInventory`).
|
|
@@ -2696,14 +2784,6 @@ function extractErrorsArray(body) {
|
|
|
2696
2784
|
}
|
|
2697
2785
|
return void 0;
|
|
2698
2786
|
}
|
|
2699
|
-
function parseRetryAfter(header) {
|
|
2700
|
-
if (!header) return void 0;
|
|
2701
|
-
const seconds = Number(header);
|
|
2702
|
-
if (!Number.isNaN(seconds)) return Math.max(0, seconds * 1e3);
|
|
2703
|
-
const epoch = Date.parse(header);
|
|
2704
|
-
if (!Number.isNaN(epoch)) return Math.max(0, epoch - Date.now());
|
|
2705
|
-
return void 0;
|
|
2706
|
-
}
|
|
2707
2787
|
|
|
2708
2788
|
// src/transport.ts
|
|
2709
2789
|
var BASE_URLS = {
|
|
@@ -2714,95 +2794,26 @@ var TrendyolTransport = class {
|
|
|
2714
2794
|
constructor(config) {
|
|
2715
2795
|
this.config = config;
|
|
2716
2796
|
this.baseUrl = BASE_URLS[config.env];
|
|
2717
|
-
this.
|
|
2718
|
-
|
|
2719
|
-
|
|
2797
|
+
this.requester = core.createRequester({
|
|
2798
|
+
fetch: config.fetch ?? fetch,
|
|
2799
|
+
logger: config.logger,
|
|
2800
|
+
timeoutMs: config.timeoutMs ?? 3e4,
|
|
2801
|
+
label: "Trendyol",
|
|
2802
|
+
logPrefix: "trendyol",
|
|
2803
|
+
buildUrl: (opts) => this.buildUrl(opts.path, opts.query),
|
|
2804
|
+
buildHeaders: (correlationId) => this.buildHeaders(correlationId),
|
|
2805
|
+
mapHttpError
|
|
2806
|
+
});
|
|
2720
2807
|
}
|
|
2721
2808
|
config;
|
|
2722
2809
|
baseUrl;
|
|
2723
|
-
|
|
2724
|
-
timeoutMs;
|
|
2725
|
-
fetchImpl;
|
|
2810
|
+
requester;
|
|
2726
2811
|
/** Seller ID this transport is configured with. Resources read it for path-building. */
|
|
2727
2812
|
get sellerId() {
|
|
2728
2813
|
return this.config.sellerId;
|
|
2729
2814
|
}
|
|
2730
|
-
|
|
2731
|
-
return
|
|
2732
|
-
async (attempt) => {
|
|
2733
|
-
if (opts.rateLimiter) await opts.rateLimiter.acquire(opts.signal);
|
|
2734
|
-
const url = this.buildUrl(opts.path, opts.query);
|
|
2735
|
-
const correlationId = crypto.randomUUID();
|
|
2736
|
-
const headers = this.buildHeaders(correlationId);
|
|
2737
|
-
const init = {
|
|
2738
|
-
method: opts.method,
|
|
2739
|
-
headers,
|
|
2740
|
-
signal: this.composeSignal(opts.signal)
|
|
2741
|
-
};
|
|
2742
|
-
if (opts.body !== void 0 && opts.method !== "GET") {
|
|
2743
|
-
if (opts.body instanceof FormData) {
|
|
2744
|
-
init.body = opts.body;
|
|
2745
|
-
delete headers["Content-Type"];
|
|
2746
|
-
} else {
|
|
2747
|
-
init.body = JSON.stringify(opts.body);
|
|
2748
|
-
}
|
|
2749
|
-
}
|
|
2750
|
-
this.logger.debug("trendyol.request", {
|
|
2751
|
-
method: opts.method,
|
|
2752
|
-
url,
|
|
2753
|
-
correlationId,
|
|
2754
|
-
attempt
|
|
2755
|
-
});
|
|
2756
|
-
let response;
|
|
2757
|
-
try {
|
|
2758
|
-
response = await this.fetchImpl(url, init);
|
|
2759
|
-
} catch (err) {
|
|
2760
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
2761
|
-
throw new core.TimeoutError({
|
|
2762
|
-
message: `Trendyol request timed out after ${this.timeoutMs}ms`,
|
|
2763
|
-
cause: err
|
|
2764
|
-
});
|
|
2765
|
-
}
|
|
2766
|
-
throw new core.NetworkError({
|
|
2767
|
-
message: "Trendyol network failure",
|
|
2768
|
-
cause: err
|
|
2769
|
-
});
|
|
2770
|
-
}
|
|
2771
|
-
if (!response.ok) {
|
|
2772
|
-
const body = await safeJson(response);
|
|
2773
|
-
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
|
|
2774
|
-
const error = mapHttpError(response.status, body, retryAfterMs);
|
|
2775
|
-
this.logger.warn("trendyol.error", {
|
|
2776
|
-
method: opts.method,
|
|
2777
|
-
url,
|
|
2778
|
-
correlationId,
|
|
2779
|
-
status: response.status,
|
|
2780
|
-
code: error.code,
|
|
2781
|
-
retryable: error.retryable
|
|
2782
|
-
});
|
|
2783
|
-
throw error;
|
|
2784
|
-
}
|
|
2785
|
-
this.logger.debug("trendyol.response", {
|
|
2786
|
-
correlationId,
|
|
2787
|
-
status: response.status
|
|
2788
|
-
});
|
|
2789
|
-
if (response.status === 204) return void 0;
|
|
2790
|
-
return await safeJson(response);
|
|
2791
|
-
},
|
|
2792
|
-
{
|
|
2793
|
-
signal: opts.signal,
|
|
2794
|
-
onRetry: (err, attempt, delay2) => {
|
|
2795
|
-
if (err instanceof core.LoncaError) {
|
|
2796
|
-
this.logger.warn("trendyol.retry", {
|
|
2797
|
-
attempt,
|
|
2798
|
-
delayMs: delay2,
|
|
2799
|
-
code: err.code,
|
|
2800
|
-
status: err.status
|
|
2801
|
-
});
|
|
2802
|
-
}
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
);
|
|
2815
|
+
request(opts) {
|
|
2816
|
+
return this.requester(opts);
|
|
2806
2817
|
}
|
|
2807
2818
|
buildUrl(path, query) {
|
|
2808
2819
|
const url = new URL(path, this.baseUrl);
|
|
@@ -2825,21 +2836,7 @@ var TrendyolTransport = class {
|
|
|
2825
2836
|
Accept: "application/json"
|
|
2826
2837
|
};
|
|
2827
2838
|
}
|
|
2828
|
-
composeSignal(external) {
|
|
2829
|
-
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
2830
|
-
if (!external) return timeoutSignal;
|
|
2831
|
-
return AbortSignal.any([external, timeoutSignal]);
|
|
2832
|
-
}
|
|
2833
2839
|
};
|
|
2834
|
-
async function safeJson(response) {
|
|
2835
|
-
const text = await response.text();
|
|
2836
|
-
if (!text) return void 0;
|
|
2837
|
-
try {
|
|
2838
|
-
return JSON.parse(text);
|
|
2839
|
-
} catch {
|
|
2840
|
-
return text;
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
2840
|
|
|
2844
2841
|
// src/client.ts
|
|
2845
2842
|
function createTrendyolClient(opts) {
|