@lonca/trendyol 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -26
- package/dist/client-0omWgpd_.d.cts +2526 -0
- package/dist/client-0omWgpd_.d.ts +2526 -0
- package/dist/index.cjs +505 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2183
- package/dist/index.d.ts +24 -2183
- package/dist/index.js +501 -20
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +2919 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +46 -0
- package/dist/testing.d.ts +46 -0
- package/dist/testing.js +2917 -0
- package/dist/testing.js.map +1 -0
- package/package.json +8 -3
package/dist/index.cjs
CHANGED
|
@@ -397,6 +397,269 @@ var ClaimsResource = class {
|
|
|
397
397
|
return rows.map((row) => ({ raw: row }));
|
|
398
398
|
}
|
|
399
399
|
};
|
|
400
|
+
var MAX_BATCH_ITEMS = 5e3;
|
|
401
|
+
var ExportCenterResource = class {
|
|
402
|
+
constructor(transport, limiter) {
|
|
403
|
+
this.transport = transport;
|
|
404
|
+
this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
|
|
405
|
+
}
|
|
406
|
+
transport;
|
|
407
|
+
limiter;
|
|
408
|
+
// ─── Products ────────────────────────────────────────────────────────────
|
|
409
|
+
/**
|
|
410
|
+
* List Export Center-approved products. Uses Trendyol's `pageKey`
|
|
411
|
+
* pagination — the first call leaves `pageKey` empty; subsequent
|
|
412
|
+
* calls pass the `x-paging-key` value from the previous response.
|
|
413
|
+
*/
|
|
414
|
+
async listProducts(params = {}) {
|
|
415
|
+
const query = {};
|
|
416
|
+
if (params.barcodes && params.barcodes.length > 0) query.barcodes = params.barcodes.join(",");
|
|
417
|
+
if (params.pageKey) query.pageKey = params.pageKey;
|
|
418
|
+
if (params.size !== void 0) query.size = params.size;
|
|
419
|
+
const data = await this.transport.request({
|
|
420
|
+
method: "GET",
|
|
421
|
+
path: `/integration/ecgw/v2/${this.transport.sellerId}/products`,
|
|
422
|
+
query: Object.keys(query).length ? query : void 0,
|
|
423
|
+
rateLimiter: this.limiter
|
|
424
|
+
});
|
|
425
|
+
const rows = pickArray(data);
|
|
426
|
+
return rows.map((r) => ({ raw: r ?? {} }));
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Create Export Center products. Maximum 5000 per call. Returns a
|
|
430
|
+
* `batchId` you can poll via `getBatchStatus(batchId)`.
|
|
431
|
+
*
|
|
432
|
+
* @throws {ValidationError} when `products` is empty / oversized.
|
|
433
|
+
*/
|
|
434
|
+
async createProducts(products) {
|
|
435
|
+
this.assertBatch(products, "createProducts");
|
|
436
|
+
const data = await this.transport.request({
|
|
437
|
+
method: "POST",
|
|
438
|
+
path: `/integration/ecgw/v2/${this.transport.sellerId}/products`,
|
|
439
|
+
body: { products },
|
|
440
|
+
rateLimiter: this.limiter
|
|
441
|
+
});
|
|
442
|
+
return normalizeBatchResponse(data);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Update Export Center prices. **Trendyol allows one price update per
|
|
446
|
+
* barcode per day.** Returns a `batchId`.
|
|
447
|
+
*
|
|
448
|
+
* @throws {ValidationError} when `priceInfos` is empty / oversized.
|
|
449
|
+
*/
|
|
450
|
+
async updatePrices(priceInfos) {
|
|
451
|
+
this.assertBatch(priceInfos, "updatePrices");
|
|
452
|
+
const data = await this.transport.request({
|
|
453
|
+
method: "POST",
|
|
454
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/prices`,
|
|
455
|
+
body: { priceInfos },
|
|
456
|
+
rateLimiter: this.limiter
|
|
457
|
+
});
|
|
458
|
+
return normalizeBatchResponse(data);
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Update Export Center stocks. Returns a `batchId`. Sellers using
|
|
462
|
+
* Trendyol's shared inventory cannot use this endpoint (per portal docs).
|
|
463
|
+
*
|
|
464
|
+
* @throws {ValidationError} when `items` is empty / oversized.
|
|
465
|
+
*/
|
|
466
|
+
async updateStocks(items) {
|
|
467
|
+
this.assertBatch(items, "updateStocks");
|
|
468
|
+
const data = await this.transport.request({
|
|
469
|
+
method: "POST",
|
|
470
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/stocks`,
|
|
471
|
+
body: { items },
|
|
472
|
+
rateLimiter: this.limiter
|
|
473
|
+
});
|
|
474
|
+
return normalizeBatchResponse(data);
|
|
475
|
+
}
|
|
476
|
+
// ─── Batch status ────────────────────────────────────────────────────────
|
|
477
|
+
/**
|
|
478
|
+
* Look up the status of a previously-submitted batch. Trendyol retains
|
|
479
|
+
* batch records for **24 hours** only — older `batchId`s return `404`.
|
|
480
|
+
*/
|
|
481
|
+
async getBatchStatus(batchId) {
|
|
482
|
+
if (!batchId) {
|
|
483
|
+
throw new core.ValidationError({ message: "exportCenter.getBatchStatus: batchId is required" });
|
|
484
|
+
}
|
|
485
|
+
const data = await this.transport.request({
|
|
486
|
+
method: "GET",
|
|
487
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/check-status`,
|
|
488
|
+
query: { batchId },
|
|
489
|
+
rateLimiter: this.limiter
|
|
490
|
+
});
|
|
491
|
+
return normalizeBatchStatus(data);
|
|
492
|
+
}
|
|
493
|
+
// ─── Packages ────────────────────────────────────────────────────────────
|
|
494
|
+
/** List daily Export Center packages (V2 — query-based filters). */
|
|
495
|
+
async listPackagesV2(params = {}) {
|
|
496
|
+
const data = await this.transport.request({
|
|
497
|
+
method: "GET",
|
|
498
|
+
path: `/integration/ecgw/v2/${this.transport.sellerId}/packages`,
|
|
499
|
+
query: {
|
|
500
|
+
trackingNumber: params.trackingNumber,
|
|
501
|
+
status: params.status,
|
|
502
|
+
creationStartDate: params.creationStartDate,
|
|
503
|
+
creationEndDate: params.creationEndDate,
|
|
504
|
+
size: params.size,
|
|
505
|
+
boutiqueId: params.boutiqueId
|
|
506
|
+
},
|
|
507
|
+
rateLimiter: this.limiter
|
|
508
|
+
});
|
|
509
|
+
return pickArray(data).map(normalizePackage);
|
|
510
|
+
}
|
|
511
|
+
/** List Export Center packages (V3 — consolidated, page-based). */
|
|
512
|
+
async listPackagesV3(params = {}) {
|
|
513
|
+
const data = await this.transport.request({
|
|
514
|
+
method: "GET",
|
|
515
|
+
path: `/integration/ecgw/v3/${this.transport.sellerId}/packages`,
|
|
516
|
+
query: {
|
|
517
|
+
status: params.status,
|
|
518
|
+
page: params.offset !== void 0 ? params.offset : void 0,
|
|
519
|
+
size: params.limit,
|
|
520
|
+
creationStartDate: params.creationStartDate,
|
|
521
|
+
creationEndDate: params.creationEndDate
|
|
522
|
+
},
|
|
523
|
+
rateLimiter: this.limiter
|
|
524
|
+
});
|
|
525
|
+
return pickArray(data).map(normalizePackage);
|
|
526
|
+
}
|
|
527
|
+
/** Get the line items inside an Export Center package. */
|
|
528
|
+
async getPackageItems(params) {
|
|
529
|
+
if (!params || !params.packageId) {
|
|
530
|
+
throw new core.ValidationError({
|
|
531
|
+
message: "exportCenter.getPackageItems: packageId is required"
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
const data = await this.transport.request({
|
|
535
|
+
method: "GET",
|
|
536
|
+
path: `/integration/ecgw/v2/${this.transport.sellerId}/packages/items`,
|
|
537
|
+
query: {
|
|
538
|
+
packageId: params.packageId,
|
|
539
|
+
status: params.status,
|
|
540
|
+
page: params.offset,
|
|
541
|
+
size: params.limit
|
|
542
|
+
},
|
|
543
|
+
rateLimiter: this.limiter
|
|
544
|
+
});
|
|
545
|
+
return pickArray(data).map((r) => ({ raw: r ?? {} }));
|
|
546
|
+
}
|
|
547
|
+
// ─── Lookup ──────────────────────────────────────────────────────────────
|
|
548
|
+
/** Get the required attributes for an Export Center category. */
|
|
549
|
+
async getCategoryAttributes(categoryId) {
|
|
550
|
+
if (categoryId === void 0 || categoryId === null || categoryId === "") {
|
|
551
|
+
throw new core.ValidationError({
|
|
552
|
+
message: "exportCenter.getCategoryAttributes: categoryId is required"
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
const data = await this.transport.request({
|
|
556
|
+
method: "GET",
|
|
557
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/lookup/product-categories/${encodeURIComponent(String(categoryId))}/attributes`,
|
|
558
|
+
rateLimiter: this.limiter
|
|
559
|
+
});
|
|
560
|
+
return pickArray(data).map((r) => {
|
|
561
|
+
const row = r ?? {};
|
|
562
|
+
const out = { raw: row };
|
|
563
|
+
if (typeof row.attributeId === "number" || typeof row.attributeId === "string") {
|
|
564
|
+
out.attributeId = row.attributeId;
|
|
565
|
+
}
|
|
566
|
+
if (typeof row.attributeName === "string") out.attributeName = row.attributeName;
|
|
567
|
+
if (typeof row.required === "boolean") out.required = row.required;
|
|
568
|
+
if (Array.isArray(row.values)) out.values = row.values;
|
|
569
|
+
return out;
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
/** Get the care-instruction lookup values used by `createProducts`. */
|
|
573
|
+
async getCareInstructions() {
|
|
574
|
+
const data = await this.transport.request({
|
|
575
|
+
method: "GET",
|
|
576
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/lookup/care-instructions`,
|
|
577
|
+
rateLimiter: this.limiter
|
|
578
|
+
});
|
|
579
|
+
return pickArray(data).map(normalizeNamedRef);
|
|
580
|
+
}
|
|
581
|
+
/** Get the material-composition lookup values used by `createProducts`. */
|
|
582
|
+
async getCompositions() {
|
|
583
|
+
const data = await this.transport.request({
|
|
584
|
+
method: "GET",
|
|
585
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/lookup/compositions`,
|
|
586
|
+
rateLimiter: this.limiter
|
|
587
|
+
});
|
|
588
|
+
return pickArray(data).map(normalizeNamedRef);
|
|
589
|
+
}
|
|
590
|
+
/** Get the country-of-origin lookup values used by `createProducts`. */
|
|
591
|
+
async getOrigins() {
|
|
592
|
+
const data = await this.transport.request({
|
|
593
|
+
method: "GET",
|
|
594
|
+
path: `/integration/ecgw/v1/${this.transport.sellerId}/lookup/origins`,
|
|
595
|
+
rateLimiter: this.limiter
|
|
596
|
+
});
|
|
597
|
+
return pickArray(data).map((r) => {
|
|
598
|
+
const row = r ?? {};
|
|
599
|
+
const out = { raw: row };
|
|
600
|
+
if (typeof row.id === "number" || typeof row.id === "string") out.id = row.id;
|
|
601
|
+
if (typeof row.name === "string") out.name = row.name;
|
|
602
|
+
if (typeof row.countryCode === "string") out.countryCode = row.countryCode;
|
|
603
|
+
return out;
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
607
|
+
assertBatch(items, methodLabel) {
|
|
608
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
609
|
+
throw new core.ValidationError({
|
|
610
|
+
message: `exportCenter.${methodLabel}: items must not be empty`
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
if (items.length > MAX_BATCH_ITEMS) {
|
|
614
|
+
throw new core.ValidationError({
|
|
615
|
+
message: `exportCenter.${methodLabel}: max ${MAX_BATCH_ITEMS} items per call (got ${items.length})`
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
function pickArray(data) {
|
|
621
|
+
if (Array.isArray(data)) return data;
|
|
622
|
+
if (data && typeof data === "object") {
|
|
623
|
+
const obj = data;
|
|
624
|
+
if (Array.isArray(obj.content)) return obj.content;
|
|
625
|
+
if (Array.isArray(obj.items)) return obj.items;
|
|
626
|
+
if (Array.isArray(obj.data)) return obj.data;
|
|
627
|
+
if (Array.isArray(obj.products)) return obj.products;
|
|
628
|
+
if (Array.isArray(obj.packages)) return obj.packages;
|
|
629
|
+
}
|
|
630
|
+
return [];
|
|
631
|
+
}
|
|
632
|
+
function normalizeBatchResponse(data) {
|
|
633
|
+
const obj = data && typeof data === "object" ? data : {};
|
|
634
|
+
return {
|
|
635
|
+
batchId: String(obj.batchId ?? obj.batchRequestId ?? obj.id ?? ""),
|
|
636
|
+
raw: obj
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
function normalizeBatchStatus(data) {
|
|
640
|
+
const obj = data && typeof data === "object" ? data : {};
|
|
641
|
+
const out = { raw: obj };
|
|
642
|
+
if (typeof obj.batchId === "string") out.batchId = obj.batchId;
|
|
643
|
+
if (typeof obj.status === "string") out.status = obj.status;
|
|
644
|
+
if (typeof obj.itemCount === "number") out.itemCount = obj.itemCount;
|
|
645
|
+
if (typeof obj.failedItemCount === "number") out.failedItemCount = obj.failedItemCount;
|
|
646
|
+
if (Array.isArray(obj.items)) out.items = obj.items;
|
|
647
|
+
return out;
|
|
648
|
+
}
|
|
649
|
+
function normalizePackage(row) {
|
|
650
|
+
const r = row ?? {};
|
|
651
|
+
const out = { raw: r };
|
|
652
|
+
if (typeof r.packageNumber === "string") out.packageNumber = r.packageNumber;
|
|
653
|
+
if (typeof r.status === "string") out.status = r.status;
|
|
654
|
+
return out;
|
|
655
|
+
}
|
|
656
|
+
function normalizeNamedRef(row) {
|
|
657
|
+
const r = row ?? {};
|
|
658
|
+
const out = { raw: r };
|
|
659
|
+
if (typeof r.id === "number" || typeof r.id === "string") out.id = r.id;
|
|
660
|
+
if (typeof r.name === "string") out.name = r.name;
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
400
663
|
function toIso2(epochMs) {
|
|
401
664
|
if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
|
|
402
665
|
return void 0;
|
|
@@ -442,11 +705,14 @@ var FinanceResource = class {
|
|
|
442
705
|
transport;
|
|
443
706
|
limiter;
|
|
444
707
|
async getSettlements(params = {}) {
|
|
445
|
-
return this.queryPage(
|
|
708
|
+
return this.queryPage(
|
|
709
|
+
`/integration/finance/che/sellers/${this.transport.sellerId}/settlements`,
|
|
710
|
+
params
|
|
711
|
+
);
|
|
446
712
|
}
|
|
447
713
|
async getOtherFinancials(params = {}) {
|
|
448
714
|
return this.queryPage(
|
|
449
|
-
`/integration/sellers/${this.transport.sellerId}/otherfinancials`,
|
|
715
|
+
`/integration/finance/che/sellers/${this.transport.sellerId}/otherfinancials`,
|
|
450
716
|
params
|
|
451
717
|
);
|
|
452
718
|
}
|
|
@@ -473,11 +739,37 @@ var FinanceResource = class {
|
|
|
473
739
|
}
|
|
474
740
|
};
|
|
475
741
|
var MAX_ITEMS_PER_REQUEST = 1e3;
|
|
742
|
+
async function pollBatchStatus(getStatus, batchRequestId, opts = {}) {
|
|
743
|
+
const pollIntervalMs = opts.pollIntervalMs ?? 2e3;
|
|
744
|
+
const timeoutMs = opts.timeoutMs ?? 12e4;
|
|
745
|
+
const deadline = Date.now() + timeoutMs;
|
|
746
|
+
for (; ; ) {
|
|
747
|
+
opts.signal?.throwIfAborted();
|
|
748
|
+
const result = await getStatus(batchRequestId);
|
|
749
|
+
if (result.status === "COMPLETED" || result.status === "FAILED") {
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
if (Date.now() >= deadline) {
|
|
753
|
+
throw new core.TimeoutError({
|
|
754
|
+
message: `Trendyol batch ${batchRequestId} did not settle within ${timeoutMs}ms (last status: ${result.status})`,
|
|
755
|
+
data: { batchRequestId, lastStatus: result.status, lastResult: result }
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
await delay(pollIntervalMs, opts.signal);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
476
761
|
var InventoryResource = class {
|
|
477
|
-
|
|
762
|
+
/**
|
|
763
|
+
* @param transport Trendyol transport.
|
|
764
|
+
* @param getBatchStatus Batch-status poller (wired by `createTrendyolClient`
|
|
765
|
+
* to `products.getBatchStatus`). Required only for `updateAndWait`.
|
|
766
|
+
*/
|
|
767
|
+
constructor(transport, getBatchStatus) {
|
|
478
768
|
this.transport = transport;
|
|
769
|
+
this.getBatchStatus = getBatchStatus;
|
|
479
770
|
}
|
|
480
771
|
transport;
|
|
772
|
+
getBatchStatus;
|
|
481
773
|
/**
|
|
482
774
|
* Update price and/or stock for one or more SKUs (by barcode).
|
|
483
775
|
*
|
|
@@ -491,6 +783,9 @@ var InventoryResource = class {
|
|
|
491
783
|
* ```
|
|
492
784
|
*
|
|
493
785
|
* @throws {ValidationError} when `items` is empty or longer than 1000.
|
|
786
|
+
* @throws {ServerError} when Trendyol accepts the request but returns no
|
|
787
|
+
* `batchRequestId` (an unpollable response — surfaced loudly instead of
|
|
788
|
+
* handing back an empty id).
|
|
494
789
|
*/
|
|
495
790
|
async update(items) {
|
|
496
791
|
if (items.length === 0) {
|
|
@@ -506,9 +801,70 @@ var InventoryResource = class {
|
|
|
506
801
|
path: `/integration/inventory/sellers/${this.transport.sellerId}/products/price-and-inventory`,
|
|
507
802
|
body: { items }
|
|
508
803
|
});
|
|
509
|
-
|
|
804
|
+
if (!data.batchRequestId) {
|
|
805
|
+
throw new core.ServerError({
|
|
806
|
+
message: "Trendyol updatePriceAndInventory returned no batchRequestId",
|
|
807
|
+
data: { response: data }
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
return { batchRequestId: data.batchRequestId };
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Submit price/stock updates and wait for them to settle.
|
|
814
|
+
*
|
|
815
|
+
* Splits `items` into chunks of ≤1000, submits each via {@link update}, and
|
|
816
|
+
* polls each `batchRequestId` to a terminal state. Returns one
|
|
817
|
+
* `BatchRequestResult` per chunk (read `failedItemCount` / `items[]` for
|
|
818
|
+
* per-barcode outcomes). Compose `@lonca/core`'s `retry` around this for
|
|
819
|
+
* transient-error resilience.
|
|
820
|
+
*
|
|
821
|
+
* @throws {ValidationError} when `items` is empty.
|
|
822
|
+
* @throws {TimeoutError} when any chunk does not settle within `timeoutMs`;
|
|
823
|
+
* `error.data.batchRequestId` identifies the stuck chunk.
|
|
824
|
+
*/
|
|
825
|
+
async updateAndWait(items, opts = {}) {
|
|
826
|
+
if (items.length === 0) {
|
|
827
|
+
throw new core.ValidationError({ message: "updateAndWait: items must not be empty" });
|
|
828
|
+
}
|
|
829
|
+
const getStatus = this.getBatchStatus;
|
|
830
|
+
if (!getStatus) {
|
|
831
|
+
throw new Error(
|
|
832
|
+
"updateAndWait requires a batch-status poller; obtain the client via createTrendyolClient()"
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
const results = [];
|
|
836
|
+
for (const batch of chunk(items, MAX_ITEMS_PER_REQUEST)) {
|
|
837
|
+
const { batchRequestId } = await this.update(batch);
|
|
838
|
+
results.push(await pollBatchStatus(getStatus, batchRequestId, opts));
|
|
839
|
+
}
|
|
840
|
+
return results;
|
|
510
841
|
}
|
|
511
842
|
};
|
|
843
|
+
function chunk(items, size) {
|
|
844
|
+
if (items.length <= size) return [items];
|
|
845
|
+
const chunks = [];
|
|
846
|
+
for (let i = 0; i < items.length; i += size) {
|
|
847
|
+
chunks.push(items.slice(i, i + size));
|
|
848
|
+
}
|
|
849
|
+
return chunks;
|
|
850
|
+
}
|
|
851
|
+
function delay(ms, signal) {
|
|
852
|
+
return new Promise((resolve, reject) => {
|
|
853
|
+
if (signal?.aborted) {
|
|
854
|
+
reject(signal.reason ?? new Error("Aborted"));
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
const onAbort = () => {
|
|
858
|
+
clearTimeout(timer);
|
|
859
|
+
reject(signal?.reason ?? new Error("Aborted"));
|
|
860
|
+
};
|
|
861
|
+
const timer = setTimeout(() => {
|
|
862
|
+
signal?.removeEventListener("abort", onAbort);
|
|
863
|
+
resolve();
|
|
864
|
+
}, ms);
|
|
865
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
866
|
+
});
|
|
867
|
+
}
|
|
512
868
|
var InvoicesResource = class {
|
|
513
869
|
constructor(transport, limiter) {
|
|
514
870
|
this.transport = transport;
|
|
@@ -794,9 +1150,9 @@ function normalizeCustomer(node) {
|
|
|
794
1150
|
return out;
|
|
795
1151
|
}
|
|
796
1152
|
function normalizeShipmentPackage(rawNode) {
|
|
797
|
-
return
|
|
1153
|
+
return normalizePackage2(rawNode ?? {});
|
|
798
1154
|
}
|
|
799
|
-
function
|
|
1155
|
+
function normalizePackage2(node) {
|
|
800
1156
|
const pkgId = node.shipmentPackageId ?? node.id;
|
|
801
1157
|
const out = {
|
|
802
1158
|
id: pkgId !== void 0 ? String(pkgId) : "",
|
|
@@ -897,7 +1253,7 @@ var OrdersResource = class {
|
|
|
897
1253
|
query,
|
|
898
1254
|
rateLimiter: this.limiter
|
|
899
1255
|
});
|
|
900
|
-
const items = (data.content ?? []).map(
|
|
1256
|
+
const items = (data.content ?? []).map(normalizePackage2);
|
|
901
1257
|
const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
|
|
902
1258
|
const result = { items };
|
|
903
1259
|
if (page + 1 < totalPages) {
|
|
@@ -1194,7 +1550,7 @@ var OrdersResource = class {
|
|
|
1194
1550
|
query,
|
|
1195
1551
|
rateLimiter: this.limiter
|
|
1196
1552
|
});
|
|
1197
|
-
const items = (data.content ?? []).map(
|
|
1553
|
+
const items = (data.content ?? []).map(normalizePackage2);
|
|
1198
1554
|
const result = { items };
|
|
1199
1555
|
if (data.hasMore && data.nextCursor) {
|
|
1200
1556
|
result.nextCursor = data.nextCursor;
|
|
@@ -2077,6 +2433,55 @@ var TestOrdersResource = class {
|
|
|
2077
2433
|
});
|
|
2078
2434
|
}
|
|
2079
2435
|
};
|
|
2436
|
+
var VideosResource = class {
|
|
2437
|
+
constructor(transport, options = {}) {
|
|
2438
|
+
this.transport = transport;
|
|
2439
|
+
this.createLimiter = options.createLimiter ?? new core.TokenBucketRateLimiter({ capacity: 200, intervalMs: 6e4 });
|
|
2440
|
+
this.listLimiter = options.listLimiter ?? new core.TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
|
|
2441
|
+
}
|
|
2442
|
+
transport;
|
|
2443
|
+
createLimiter;
|
|
2444
|
+
listLimiter;
|
|
2445
|
+
/**
|
|
2446
|
+
* Queue a video for upload. Trendyol downloads from the URL in the
|
|
2447
|
+
* body asynchronously; poll `list()` (filtered by id) for status.
|
|
2448
|
+
*
|
|
2449
|
+
* @throws {ValidationError} when `input` is empty / not an object.
|
|
2450
|
+
*/
|
|
2451
|
+
async create(input) {
|
|
2452
|
+
if (!input || typeof input !== "object") {
|
|
2453
|
+
throw new core.ValidationError({ message: "videos.create: input is required" });
|
|
2454
|
+
}
|
|
2455
|
+
return this.transport.request({
|
|
2456
|
+
method: "POST",
|
|
2457
|
+
path: `/integration/video/sellers/${this.transport.sellerId}/videos`,
|
|
2458
|
+
body: input,
|
|
2459
|
+
rateLimiter: this.createLimiter
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
/** List the seller's integration videos (optionally filtered by id / status). */
|
|
2463
|
+
async list(params = {}) {
|
|
2464
|
+
const data = await this.transport.request({
|
|
2465
|
+
method: "GET",
|
|
2466
|
+
path: `/integration/video/sellers/${this.transport.sellerId}/videos`,
|
|
2467
|
+
query: {
|
|
2468
|
+
id: params.id,
|
|
2469
|
+
sellerIntegrationStatus: params.sellerIntegrationStatus,
|
|
2470
|
+
page: params.offset,
|
|
2471
|
+
size: params.limit
|
|
2472
|
+
},
|
|
2473
|
+
rateLimiter: this.listLimiter
|
|
2474
|
+
});
|
|
2475
|
+
const rows = Array.isArray(data) ? data : Array.isArray(data?.content) ? data.content : Array.isArray(data?.items) ? data.items : [];
|
|
2476
|
+
return rows.map((r) => {
|
|
2477
|
+
const row = r ?? {};
|
|
2478
|
+
const out = { raw: row };
|
|
2479
|
+
if (typeof row.id === "string") out.id = row.id;
|
|
2480
|
+
if (typeof row.status === "string") out.status = row.status;
|
|
2481
|
+
return out;
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
};
|
|
2080
2485
|
function normalizeWebhook(node) {
|
|
2081
2486
|
const out = {
|
|
2082
2487
|
id: node.id !== void 0 ? String(node.id) : "",
|
|
@@ -2193,6 +2598,16 @@ var WebhooksResource = class {
|
|
|
2193
2598
|
}
|
|
2194
2599
|
};
|
|
2195
2600
|
|
|
2601
|
+
// src/capabilities.ts
|
|
2602
|
+
var trendyolCapabilities = {
|
|
2603
|
+
/** Trendyol has no time-bounded / scheduled pricing (`pricings[]`). */
|
|
2604
|
+
scheduledPricing: false,
|
|
2605
|
+
/** `inventory.update` accepts stock-only items (`quantity` with no price). */
|
|
2606
|
+
stockOnlyBatch: true,
|
|
2607
|
+
/** Products expose `updatedAt`, so last-write-wins guards are supported. */
|
|
2608
|
+
listingUpdatedAt: true
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2196
2611
|
// src/auth.ts
|
|
2197
2612
|
function buildAuthHeader(apiKey, apiSecret) {
|
|
2198
2613
|
const token = Buffer.from(`${apiKey}:${apiSecret}`, "utf8").toString("base64");
|
|
@@ -2203,27 +2618,31 @@ function buildUserAgent(sellerId, integratorName) {
|
|
|
2203
2618
|
}
|
|
2204
2619
|
function mapHttpError(status, body, retryAfterMs) {
|
|
2205
2620
|
const data = { body };
|
|
2621
|
+
const issues = normalizeErrorIssues(body);
|
|
2206
2622
|
switch (status) {
|
|
2207
2623
|
case 401:
|
|
2208
2624
|
return new core.AuthError({
|
|
2209
2625
|
message: "Trendyol authentication failed (check apiKey / apiSecret)",
|
|
2210
2626
|
status,
|
|
2211
|
-
data
|
|
2627
|
+
data,
|
|
2628
|
+
issues
|
|
2212
2629
|
});
|
|
2213
2630
|
case 403:
|
|
2214
2631
|
return new core.ValidationError({
|
|
2215
2632
|
message: "Trendyol forbidden (check User-Agent header or endpoint path)",
|
|
2216
2633
|
status,
|
|
2217
|
-
data
|
|
2634
|
+
data,
|
|
2635
|
+
issues
|
|
2218
2636
|
});
|
|
2219
2637
|
case 404:
|
|
2220
|
-
return new core.NotFoundError({ message: "Trendyol resource not found", status, data });
|
|
2638
|
+
return new core.NotFoundError({ message: "Trendyol resource not found", status, data, issues });
|
|
2221
2639
|
case 429:
|
|
2222
2640
|
return new core.RateLimitError({
|
|
2223
2641
|
message: "Trendyol rate limit exceeded",
|
|
2224
2642
|
status,
|
|
2225
2643
|
retryAfterMs,
|
|
2226
|
-
data
|
|
2644
|
+
data,
|
|
2645
|
+
issues
|
|
2227
2646
|
});
|
|
2228
2647
|
case 500:
|
|
2229
2648
|
case 502:
|
|
@@ -2232,7 +2651,8 @@ function mapHttpError(status, body, retryAfterMs) {
|
|
|
2232
2651
|
return new core.ServerError({
|
|
2233
2652
|
message: `Trendyol server error (${status})`,
|
|
2234
2653
|
status,
|
|
2235
|
-
data
|
|
2654
|
+
data,
|
|
2655
|
+
issues
|
|
2236
2656
|
});
|
|
2237
2657
|
default:
|
|
2238
2658
|
return new core.LoncaError({
|
|
@@ -2240,10 +2660,42 @@ function mapHttpError(status, body, retryAfterMs) {
|
|
|
2240
2660
|
message: `Trendyol unexpected response (${status})`,
|
|
2241
2661
|
status,
|
|
2242
2662
|
data,
|
|
2663
|
+
issues,
|
|
2243
2664
|
retryable: false
|
|
2244
2665
|
});
|
|
2245
2666
|
}
|
|
2246
2667
|
}
|
|
2668
|
+
function normalizeErrorIssues(body) {
|
|
2669
|
+
const errors = extractErrorsArray(body);
|
|
2670
|
+
if (!errors) return [];
|
|
2671
|
+
const issues = [];
|
|
2672
|
+
for (const entry of errors) {
|
|
2673
|
+
if (typeof entry === "string") {
|
|
2674
|
+
issues.push({ message: entry });
|
|
2675
|
+
continue;
|
|
2676
|
+
}
|
|
2677
|
+
if (entry && typeof entry === "object") {
|
|
2678
|
+
const e = entry;
|
|
2679
|
+
if (typeof e.message !== "string") continue;
|
|
2680
|
+
const issue = { message: e.message };
|
|
2681
|
+
if (typeof e.field === "string") issue.field = e.field;
|
|
2682
|
+
if (typeof e.code === "string") issue.code = e.code;
|
|
2683
|
+
issues.push(issue);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
return issues;
|
|
2687
|
+
}
|
|
2688
|
+
function extractErrorsArray(body) {
|
|
2689
|
+
if (!body || typeof body !== "object") return void 0;
|
|
2690
|
+
const b = body;
|
|
2691
|
+
if (Array.isArray(b.errors)) return b.errors;
|
|
2692
|
+
const nested = b.body;
|
|
2693
|
+
if (nested && typeof nested === "object") {
|
|
2694
|
+
const n2 = nested;
|
|
2695
|
+
if (Array.isArray(n2.errors)) return n2.errors;
|
|
2696
|
+
}
|
|
2697
|
+
return void 0;
|
|
2698
|
+
}
|
|
2247
2699
|
function parseRetryAfter(header) {
|
|
2248
2700
|
if (!header) return void 0;
|
|
2249
2701
|
const seconds = Number(header);
|
|
@@ -2339,11 +2791,11 @@ var TrendyolTransport = class {
|
|
|
2339
2791
|
},
|
|
2340
2792
|
{
|
|
2341
2793
|
signal: opts.signal,
|
|
2342
|
-
onRetry: (err, attempt,
|
|
2794
|
+
onRetry: (err, attempt, delay2) => {
|
|
2343
2795
|
if (err instanceof core.LoncaError) {
|
|
2344
2796
|
this.logger.warn("trendyol.retry", {
|
|
2345
2797
|
attempt,
|
|
2346
|
-
delayMs:
|
|
2798
|
+
delayMs: delay2,
|
|
2347
2799
|
code: err.code,
|
|
2348
2800
|
status: err.status
|
|
2349
2801
|
});
|
|
@@ -2401,12 +2853,16 @@ function createTrendyolClient(opts) {
|
|
|
2401
2853
|
logger: opts.logger,
|
|
2402
2854
|
timeoutMs: opts.timeoutMs
|
|
2403
2855
|
});
|
|
2856
|
+
return buildClient(transport);
|
|
2857
|
+
}
|
|
2858
|
+
function buildClient(transport) {
|
|
2859
|
+
const products = new ProductsResource(transport);
|
|
2404
2860
|
return {
|
|
2405
2861
|
brands: new BrandsResource(transport),
|
|
2406
2862
|
categories: new CategoriesResource(transport),
|
|
2407
2863
|
suppliers: new SuppliersResource(transport),
|
|
2408
|
-
products
|
|
2409
|
-
inventory: new InventoryResource(transport),
|
|
2864
|
+
products,
|
|
2865
|
+
inventory: new InventoryResource(transport, (id) => products.getBatchStatus(id)),
|
|
2410
2866
|
orders: new OrdersResource(transport),
|
|
2411
2867
|
claims: new ClaimsResource(transport),
|
|
2412
2868
|
webhooks: new WebhooksResource(transport),
|
|
@@ -2415,7 +2871,10 @@ function createTrendyolClient(opts) {
|
|
|
2415
2871
|
finance: new FinanceResource(transport),
|
|
2416
2872
|
labels: new LabelsResource(transport),
|
|
2417
2873
|
testOrders: new TestOrdersResource(transport),
|
|
2418
|
-
locations: new LocationsResource(transport)
|
|
2874
|
+
locations: new LocationsResource(transport),
|
|
2875
|
+
exportCenter: new ExportCenterResource(transport),
|
|
2876
|
+
videos: new VideosResource(transport),
|
|
2877
|
+
capabilities: trendyolCapabilities
|
|
2419
2878
|
};
|
|
2420
2879
|
}
|
|
2421
2880
|
function parseWebhookEvent(rawBody) {
|
|
@@ -2452,10 +2911,33 @@ function parseWebhookEvent(rawBody) {
|
|
|
2452
2911
|
raw: body
|
|
2453
2912
|
};
|
|
2454
2913
|
}
|
|
2914
|
+
var statusMap = {
|
|
2915
|
+
Created: "created",
|
|
2916
|
+
Awaiting: "created",
|
|
2917
|
+
// new order awaiting seller acceptance
|
|
2918
|
+
Verified: "created",
|
|
2919
|
+
// accepted/verified, before picking
|
|
2920
|
+
Picking: "picking",
|
|
2921
|
+
UnPacked: "picking",
|
|
2922
|
+
// back in fulfillment after being unpacked
|
|
2923
|
+
Invoiced: "invoiced",
|
|
2924
|
+
Shipped: "shipped",
|
|
2925
|
+
AtCollectionPoint: "shipped",
|
|
2926
|
+
// in transit, awaiting pickup
|
|
2927
|
+
UnDelivered: "shipped",
|
|
2928
|
+
// delivery attempt failed; still in carrier's hands
|
|
2929
|
+
Delivered: "delivered",
|
|
2930
|
+
Cancelled: "cancelled",
|
|
2931
|
+
UnSupplied: "cancelled",
|
|
2932
|
+
// seller couldn't supply → effectively cancelled
|
|
2933
|
+
Returned: "returned"
|
|
2934
|
+
};
|
|
2935
|
+
var normalizeStatus = core.createStatusNormalizer(statusMap);
|
|
2455
2936
|
|
|
2456
2937
|
exports.BrandsResource = BrandsResource;
|
|
2457
2938
|
exports.CategoriesResource = CategoriesResource;
|
|
2458
2939
|
exports.ClaimsResource = ClaimsResource;
|
|
2940
|
+
exports.ExportCenterResource = ExportCenterResource;
|
|
2459
2941
|
exports.FinanceResource = FinanceResource;
|
|
2460
2942
|
exports.InventoryResource = InventoryResource;
|
|
2461
2943
|
exports.InvoicesResource = InvoicesResource;
|
|
@@ -2466,9 +2948,14 @@ exports.ProductsResource = ProductsResource;
|
|
|
2466
2948
|
exports.QuestionsResource = QuestionsResource;
|
|
2467
2949
|
exports.SuppliersResource = SuppliersResource;
|
|
2468
2950
|
exports.TestOrdersResource = TestOrdersResource;
|
|
2951
|
+
exports.VideosResource = VideosResource;
|
|
2469
2952
|
exports.WebhooksResource = WebhooksResource;
|
|
2470
2953
|
exports.createTrendyolClient = createTrendyolClient;
|
|
2471
2954
|
exports.normalizeShipmentPackage = normalizeShipmentPackage;
|
|
2955
|
+
exports.normalizeStatus = normalizeStatus;
|
|
2472
2956
|
exports.parseWebhookEvent = parseWebhookEvent;
|
|
2957
|
+
exports.pollBatchStatus = pollBatchStatus;
|
|
2958
|
+
exports.statusMap = statusMap;
|
|
2959
|
+
exports.trendyolCapabilities = trendyolCapabilities;
|
|
2473
2960
|
//# sourceMappingURL=index.cjs.map
|
|
2474
2961
|
//# sourceMappingURL=index.cjs.map
|