@lonca/trendyol 0.3.0 → 0.5.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.cjs CHANGED
@@ -234,6 +234,217 @@ function extractAttributeList(data) {
234
234
  }
235
235
  return null;
236
236
  }
237
+ function toIso(epochMs) {
238
+ if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
239
+ return void 0;
240
+ }
241
+ return new Date(epochMs).toISOString();
242
+ }
243
+ function normalizeClaim(node) {
244
+ const out = {
245
+ id: node.id ?? node.claimId ?? "",
246
+ orderNumber: node.orderNumber ?? "",
247
+ raw: node
248
+ };
249
+ const orderDate = toIso(node.orderDate);
250
+ if (orderDate) out.orderDate = orderDate;
251
+ const claimDate = toIso(node.claimDate);
252
+ if (claimDate) out.claimDate = claimDate;
253
+ if (node.customerFirstName !== void 0) out.customerFirstName = node.customerFirstName;
254
+ if (node.customerLastName !== void 0) out.customerLastName = node.customerLastName;
255
+ return out;
256
+ }
257
+ var ClaimsResource = class {
258
+ constructor(transport, sellerId, limiter) {
259
+ this.transport = transport;
260
+ this.sellerId = sellerId;
261
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
262
+ }
263
+ transport;
264
+ sellerId;
265
+ limiter;
266
+ /**
267
+ * Create a return claim against an order. Use this to file a return on
268
+ * behalf of a customer (e.g. when they called your CS line). For
269
+ * customer-initiated returns coming from trendyol.com, you receive them
270
+ * via `claims.list()` — no need to call `create`.
271
+ *
272
+ * Returns whatever Trendyol returns (typically the new claim's identifier).
273
+ *
274
+ * @throws {ValidationError} when `claimItems` is empty.
275
+ */
276
+ async create(input) {
277
+ if (!Array.isArray(input?.claimItems) || input.claimItems.length === 0) {
278
+ throw new core.ValidationError({ message: "claims.create: claimItems must not be empty" });
279
+ }
280
+ return this.transport.request({
281
+ method: "POST",
282
+ path: `/integration/order/sellers/${this.sellerId}/claims/create`,
283
+ body: input,
284
+ rateLimiter: this.limiter
285
+ });
286
+ }
287
+ /**
288
+ * File a seller-side rejection ("ret talebi") against a customer claim.
289
+ *
290
+ * **Wire format: `multipart/form-data`** — the SDK builds the FormData
291
+ * internally from the typed input. `claimItemIdList` is joined with
292
+ * commas (Trendyol expects a single comma-separated string field).
293
+ * Attach supporting docs (PDF / JPEG) via `files: [Blob, ...]`.
294
+ */
295
+ async createIssue(claimId, input) {
296
+ if (!Array.isArray(input?.claimItemIdList) || input.claimItemIdList.length === 0) {
297
+ throw new core.ValidationError({
298
+ message: "claims.createIssue: claimItemIdList must not be empty"
299
+ });
300
+ }
301
+ if (typeof input.description !== "string" || input.description.length === 0) {
302
+ throw new core.ValidationError({ message: "claims.createIssue: description is required" });
303
+ }
304
+ if (input.description.length > 500) {
305
+ throw new core.ValidationError({
306
+ message: `claims.createIssue: description must be \u2264500 chars (got ${input.description.length})`
307
+ });
308
+ }
309
+ const form = new FormData();
310
+ form.append("claimIssueReasonId", String(input.claimIssueReasonId));
311
+ form.append("claimItemIdList", input.claimItemIdList.join(","));
312
+ form.append("description", input.description);
313
+ if (input.files) {
314
+ for (const file of input.files) {
315
+ form.append("files", file);
316
+ }
317
+ }
318
+ return this.transport.request({
319
+ method: "POST",
320
+ path: `/integration/order/sellers/${this.sellerId}/claims/${encodeURIComponent(claimId)}/issue`,
321
+ body: form,
322
+ rateLimiter: this.limiter
323
+ });
324
+ }
325
+ /**
326
+ * Approve specific claim line items. After approval, Trendyol moves
327
+ * those line items into the post-approval refund / return-shipping flow.
328
+ *
329
+ * @throws {ValidationError} when `claimLineItemIdList` is empty.
330
+ */
331
+ async approveLineItems(claimId, input) {
332
+ if (!Array.isArray(input?.claimLineItemIdList) || input.claimLineItemIdList.length === 0) {
333
+ throw new core.ValidationError({
334
+ message: "claims.approveLineItems: claimLineItemIdList must not be empty"
335
+ });
336
+ }
337
+ return this.transport.request({
338
+ method: "PUT",
339
+ path: `/integration/order/sellers/${this.sellerId}/claims/${encodeURIComponent(claimId)}/items/approve`,
340
+ body: input,
341
+ rateLimiter: this.limiter
342
+ });
343
+ }
344
+ /**
345
+ * List claims (page-based; SDK exposes opaque cursor convention).
346
+ *
347
+ * @example
348
+ * ```ts
349
+ * import { paginate } from '@lonca/core';
350
+ * for await (const c of paginate((p) =>
351
+ * client.claims.list({ ...p, claimItemStatus: 'WaitingInAction' }),
352
+ * )) {
353
+ * console.log(c.id, c.orderNumber, c.claimDate);
354
+ * }
355
+ * ```
356
+ */
357
+ async list(params = {}) {
358
+ const size = Math.min(params.limit ?? 50, 200);
359
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
360
+ const query = { page, size };
361
+ if (params.startDate) query.startDate = params.startDate.getTime();
362
+ if (params.endDate) query.endDate = params.endDate.getTime();
363
+ if (params.claimItemStatus) query.claimItemStatus = params.claimItemStatus;
364
+ const data = await this.transport.request({
365
+ method: "GET",
366
+ path: `/integration/order/sellers/${this.sellerId}/claims`,
367
+ query,
368
+ rateLimiter: this.limiter
369
+ });
370
+ const items = (data.content ?? []).map(normalizeClaim);
371
+ const result = { items };
372
+ const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
373
+ if (page + 1 < totalPages) {
374
+ result.nextCursor = String(page + 1);
375
+ }
376
+ return result;
377
+ }
378
+ /**
379
+ * Fetch the catalog of rejection-reason IDs the seller can use on
380
+ * `claims.createIssue()`. Cache the result — it changes rarely.
381
+ *
382
+ * Note: this endpoint is **not seller-scoped** (no `sellerId` in path).
383
+ */
384
+ async getIssueReasons() {
385
+ const data = await this.transport.request({
386
+ method: "GET",
387
+ path: `/integration/order/claim-issue-reasons`,
388
+ rateLimiter: this.limiter
389
+ });
390
+ return (data ?? []).map((r) => ({
391
+ id: typeof r.id === "number" ? r.id : 0,
392
+ name: r.name ?? ""
393
+ }));
394
+ }
395
+ /**
396
+ * Fetch the audit log for a single claim item (state transitions,
397
+ * actor, timestamp). Trendyol's response shape varies — the SDK
398
+ * surfaces each row as `{ raw }` and leaves field extraction to the
399
+ * caller until we observe a stable shape on the wire.
400
+ */
401
+ async getItemAudits(claimItemId) {
402
+ const data = await this.transport.request({
403
+ method: "GET",
404
+ path: `/integration/order/sellers/${this.sellerId}/claims/items/${encodeURIComponent(claimItemId)}/audit`,
405
+ rateLimiter: this.limiter
406
+ });
407
+ const rows = Array.isArray(data) ? data : Array.isArray(data?.content) ? data.content : [];
408
+ return rows.map((row) => ({ raw: row }));
409
+ }
410
+ };
411
+ var FinanceResource = class {
412
+ constructor(transport, sellerId, limiter) {
413
+ this.transport = transport;
414
+ this.sellerId = sellerId;
415
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 100, intervalMs: 6e4 });
416
+ }
417
+ transport;
418
+ sellerId;
419
+ limiter;
420
+ async getSettlements(params = {}) {
421
+ return this.queryPage(`/integration/sellers/${this.sellerId}/settlements`, params);
422
+ }
423
+ async getOtherFinancials(params = {}) {
424
+ return this.queryPage(`/integration/sellers/${this.sellerId}/otherfinancials`, params);
425
+ }
426
+ async queryPage(path, params) {
427
+ const size = Math.min(params.limit ?? 50, 200);
428
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
429
+ const query = { page, size };
430
+ if (params.startDate) query.startDate = params.startDate.getTime();
431
+ if (params.endDate) query.endDate = params.endDate.getTime();
432
+ if (params.transactionType) query.transactionType = params.transactionType;
433
+ const data = await this.transport.request({
434
+ method: "GET",
435
+ path,
436
+ query,
437
+ rateLimiter: this.limiter
438
+ });
439
+ const items = (data.content ?? []).map((row) => ({ raw: row }));
440
+ const result = { items };
441
+ const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
442
+ if (page + 1 < totalPages) {
443
+ result.nextCursor = String(page + 1);
444
+ }
445
+ return result;
446
+ }
447
+ };
237
448
  var MAX_ITEMS_PER_REQUEST = 1e3;
238
449
  var InventoryResource = class {
239
450
  constructor(transport, sellerId) {
@@ -273,9 +484,201 @@ var InventoryResource = class {
273
484
  return { batchRequestId: data.batchRequestId ?? "" };
274
485
  }
275
486
  };
487
+ var InvoicesResource = class {
488
+ constructor(transport, sellerId, limiter) {
489
+ this.transport = transport;
490
+ this.sellerId = sellerId;
491
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 500, intervalMs: 6e4 });
492
+ }
493
+ transport;
494
+ sellerId;
495
+ limiter;
496
+ /**
497
+ * Upload an invoice file for a shipment package. **Multipart**: the
498
+ * SDK builds the FormData internally from the typed input.
499
+ *
500
+ * Max 10 MB. Accepted formats: PDF, JPEG, PNG.
501
+ */
502
+ async uploadFile(input) {
503
+ if (!input?.file) {
504
+ throw new core.ValidationError({ message: "invoices.uploadFile: file is required" });
505
+ }
506
+ if (!input.shipmentPackageId) {
507
+ throw new core.ValidationError({ message: "invoices.uploadFile: shipmentPackageId is required" });
508
+ }
509
+ const form = new FormData();
510
+ form.append("shipmentPackageId", String(input.shipmentPackageId));
511
+ form.append("file", input.file);
512
+ if (input.invoiceDateTime !== void 0) {
513
+ form.append("invoiceDateTime", String(input.invoiceDateTime));
514
+ }
515
+ if (input.invoiceNumber !== void 0) {
516
+ form.append("invoiceNumber", input.invoiceNumber);
517
+ }
518
+ return this.transport.request({
519
+ method: "POST",
520
+ path: `/integration/sellers/${this.sellerId}/seller-invoice-file`,
521
+ body: form,
522
+ rateLimiter: this.limiter
523
+ });
524
+ }
525
+ /** Register an invoice URL with Trendyol (alternative to uploading the file). */
526
+ async sendLink(input) {
527
+ if (!input?.invoiceLink) {
528
+ throw new core.ValidationError({ message: "invoices.sendLink: invoiceLink is required" });
529
+ }
530
+ return this.transport.request({
531
+ method: "POST",
532
+ path: `/integration/sellers/${this.sellerId}/seller-invoice-links`,
533
+ body: input,
534
+ rateLimiter: this.limiter
535
+ });
536
+ }
537
+ /** Remove a previously-registered invoice link. */
538
+ async deleteLink(input) {
539
+ return this.transport.request({
540
+ method: "POST",
541
+ path: `/integration/sellers/${this.sellerId}/seller-invoice-links/delete`,
542
+ body: input,
543
+ rateLimiter: this.limiter
544
+ });
545
+ }
546
+ };
547
+ var LabelsResource = class {
548
+ constructor(transport, sellerId, limiter) {
549
+ this.transport = transport;
550
+ this.sellerId = sellerId;
551
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 100, intervalMs: 6e4 });
552
+ }
553
+ transport;
554
+ sellerId;
555
+ limiter;
556
+ /**
557
+ * Request a common ZPL label for a cargo tracking number. After this
558
+ * returns, call `getCommon()` with the same `cargoTrackingNumber` to
559
+ * retrieve the generated label.
560
+ *
561
+ * @throws {ValidationError} when `format` is missing.
562
+ */
563
+ async createCommon(cargoTrackingNumber, input) {
564
+ if (!input?.format) {
565
+ throw new core.ValidationError({ message: "labels.createCommon: format is required" });
566
+ }
567
+ return this.transport.request({
568
+ method: "POST",
569
+ path: `/integration/sellers/${this.sellerId}/common-label/${encodeURIComponent(String(cargoTrackingNumber))}`,
570
+ body: input,
571
+ rateLimiter: this.limiter
572
+ });
573
+ }
574
+ /** Retrieve the previously-created common label. */
575
+ async getCommon(cargoTrackingNumber) {
576
+ const data = await this.transport.request({
577
+ method: "GET",
578
+ path: `/integration/sellers/${this.sellerId}/common-label/${encodeURIComponent(String(cargoTrackingNumber))}`,
579
+ rateLimiter: this.limiter
580
+ });
581
+ return { raw: data ?? {} };
582
+ }
583
+ };
584
+ function n(rows, extract) {
585
+ const list = Array.isArray(rows) ? rows : [];
586
+ return list.map((r) => {
587
+ const node = r;
588
+ return { ...extract(node), raw: node };
589
+ });
590
+ }
591
+ var LocationsResource = class {
592
+ constructor(transport, limiter) {
593
+ this.transport = transport;
594
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 60, intervalMs: 6e4 });
595
+ }
596
+ transport;
597
+ limiter;
598
+ /** List all supported countries (Türkiye + AZ + GULF + CEE). */
599
+ async getCountries() {
600
+ const data = await this.transport.request({
601
+ method: "GET",
602
+ path: `/integration/member/countries`,
603
+ rateLimiter: this.limiter
604
+ });
605
+ return n(data, (node) => ({
606
+ code: String(node.code ?? node.id ?? ""),
607
+ name: node.name
608
+ }));
609
+ }
610
+ // ─── Domestic (TR / AZ) ───────────────────────────────────────────────
611
+ async getTurkeyCities() {
612
+ return this.cities(`/integration/member/countries/domestic/TR/cities`);
613
+ }
614
+ async getTurkeyDistricts(cityCode) {
615
+ return this.districts(
616
+ `/integration/member/countries/domestic/TR/cities/${encodeURIComponent(String(cityCode))}/districts`
617
+ );
618
+ }
619
+ async getTurkeyNeighborhoods(cityCode, districtCode) {
620
+ return this.neighborhoods(
621
+ `/integration/member/countries/domestic/TR/cities/${encodeURIComponent(String(cityCode))}/districts/${encodeURIComponent(String(districtCode))}/neighborhoods`
622
+ );
623
+ }
624
+ async getAzerbaijanCities() {
625
+ return this.cities(`/integration/member/countries/domestic/AZ/cities`);
626
+ }
627
+ async getAzerbaijanDistricts(cityCode) {
628
+ return this.districts(
629
+ `/integration/member/countries/domestic/AZ/cities/${encodeURIComponent(String(cityCode))}/districts`
630
+ );
631
+ }
632
+ // ─── International (GULF / CEE) ───────────────────────────────────────
633
+ async getCitiesByCountry(countryCode) {
634
+ return this.cities(`/integration/member/countries/${encodeURIComponent(countryCode)}/cities`);
635
+ }
636
+ async getDistrictsByCity(countryCode, cityId) {
637
+ return this.districts(
638
+ `/integration/member/countries/${encodeURIComponent(countryCode)}/cities/${encodeURIComponent(String(cityId))}/districts`
639
+ );
640
+ }
641
+ // ─── Shared paginators ────────────────────────────────────────────────
642
+ async cities(path) {
643
+ const data = await this.transport.request({
644
+ method: "GET",
645
+ path,
646
+ rateLimiter: this.limiter
647
+ });
648
+ return n(data, (node) => ({
649
+ code: String(node.code ?? node.id ?? ""),
650
+ name: node.name,
651
+ countryCode: node.countryCode
652
+ }));
653
+ }
654
+ async districts(path) {
655
+ const data = await this.transport.request({
656
+ method: "GET",
657
+ path,
658
+ rateLimiter: this.limiter
659
+ });
660
+ return n(data, (node) => ({
661
+ code: String(node.code ?? node.id ?? ""),
662
+ name: node.name,
663
+ cityCode: node.cityCode !== void 0 ? String(node.cityCode) : void 0
664
+ }));
665
+ }
666
+ async neighborhoods(path) {
667
+ const data = await this.transport.request({
668
+ method: "GET",
669
+ path,
670
+ rateLimiter: this.limiter
671
+ });
672
+ return n(data, (node) => ({
673
+ code: String(node.code ?? node.id ?? ""),
674
+ name: node.name,
675
+ districtCode: node.districtCode !== void 0 ? String(node.districtCode) : void 0
676
+ }));
677
+ }
678
+ };
276
679
  var MAX_PAGE_SIZE = 200;
277
680
  var DEFAULT_PAGE_SIZE2 = 50;
278
- function toIso(epochMs) {
681
+ function toIso2(epochMs) {
279
682
  if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
280
683
  return void 0;
281
684
  }
@@ -343,7 +746,7 @@ function normalizeLine(node) {
343
746
  function normalizeHistory(node) {
344
747
  const out = { raw: node };
345
748
  if (node.status !== void 0) out.status = node.status;
346
- const createdAt = toIso(node.createdDate);
749
+ const createdAt = toIso2(node.createdDate);
347
750
  if (createdAt) out.createdAt = createdAt;
348
751
  return out;
349
752
  }
@@ -359,13 +762,14 @@ function normalizeCustomer(node) {
359
762
  return out;
360
763
  }
361
764
  function normalizePackage(node) {
765
+ const pkgId = node.shipmentPackageId ?? node.id;
362
766
  const out = {
363
- id: node.shipmentPackageId !== void 0 ? String(node.shipmentPackageId) : "",
767
+ id: pkgId !== void 0 ? String(pkgId) : "",
364
768
  orderNumber: node.orderNumber ?? "",
365
769
  status: node.status ?? "",
366
770
  customer: normalizeCustomer(node),
367
- orderDate: toIso(node.orderDate) ?? "",
368
- lastModifiedDate: toIso(node.lastModifiedDate) ?? "",
771
+ orderDate: toIso2(node.orderDate) ?? "",
772
+ lastModifiedDate: toIso2(node.lastModifiedDate) ?? "",
369
773
  currencyCode: node.currencyCode ?? "",
370
774
  packageTotalPrice: typeof node.packageTotalPrice === "number" ? node.packageTotalPrice : 0,
371
775
  packageGrossAmount: typeof node.packageGrossAmount === "number" ? node.packageGrossAmount : 0,
@@ -385,13 +789,13 @@ function normalizePackage(node) {
385
789
  if (node.shipmentPackageStatus !== void 0) {
386
790
  out.shipmentPackageStatus = node.shipmentPackageStatus;
387
791
  }
388
- const agreed = toIso(node.agreedDeliveryDate);
792
+ const agreed = toIso2(node.agreedDeliveryDate);
389
793
  if (agreed) out.agreedDeliveryDate = agreed;
390
- const estStart = toIso(node.estimatedDeliveryStartDate);
794
+ const estStart = toIso2(node.estimatedDeliveryStartDate);
391
795
  if (estStart) out.estimatedDeliveryStartDate = estStart;
392
- const estEnd = toIso(node.estimatedDeliveryEndDate);
796
+ const estEnd = toIso2(node.estimatedDeliveryEndDate);
393
797
  if (estEnd) out.estimatedDeliveryEndDate = estEnd;
394
- const origin = toIso(node.originShipmentDate);
798
+ const origin = toIso2(node.originShipmentDate);
395
799
  if (origin) out.originShipmentDate = origin;
396
800
  const invoice = normalizeAddress(node.invoiceAddress);
397
801
  if (invoice) out.invoiceAddress = invoice;
@@ -417,50 +821,383 @@ function normalizePackage(node) {
417
821
  if (typeof node["3pByTrendyol"] === "boolean") {
418
822
  out.threePByTrendyol = node["3pByTrendyol"];
419
823
  }
420
- if (typeof node.containsDangerousProduct === "boolean") {
421
- out.containsDangerousProduct = node.containsDangerousProduct;
824
+ if (typeof node.containsDangerousProduct === "boolean") {
825
+ out.containsDangerousProduct = node.containsDangerousProduct;
826
+ }
827
+ if (typeof node.isCod === "boolean") out.isCod = node.isCod;
828
+ if (typeof node.is4P === "boolean") out.is4P = node.is4P;
829
+ if (node.invoiceLink !== void 0) out.invoiceLink = node.invoiceLink;
830
+ if (node.createdBy !== void 0) out.createdBy = node.createdBy;
831
+ return out;
832
+ }
833
+ var OrdersResource = class {
834
+ constructor(transport, sellerId, limiter) {
835
+ this.transport = transport;
836
+ this.sellerId = sellerId;
837
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
838
+ }
839
+ transport;
840
+ sellerId;
841
+ limiter;
842
+ /**
843
+ * List shipment packages for the seller.
844
+ *
845
+ * @example
846
+ * ```ts
847
+ * import { paginate } from '@lonca/core';
848
+ * for await (const pkg of paginate((p) => client.orders.list({ ...p, status: 'Created' }))) {
849
+ * console.log(pkg.id, pkg.status, pkg.customer.firstName);
850
+ * }
851
+ * ```
852
+ */
853
+ async list(params = {}) {
854
+ const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
855
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
856
+ const query = { page, size };
857
+ if (params.status) query.status = params.status;
858
+ if (params.orderNumber) query.orderNumber = params.orderNumber;
859
+ if (params.startDate) query.startDate = params.startDate.getTime();
860
+ if (params.endDate) query.endDate = params.endDate.getTime();
861
+ const data = await this.transport.request({
862
+ method: "GET",
863
+ path: `/integration/order/sellers/${this.sellerId}/orders`,
864
+ query,
865
+ rateLimiter: this.limiter
866
+ });
867
+ const items = (data.content ?? []).map(normalizePackage);
868
+ const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
869
+ const result = { items };
870
+ if (page + 1 < totalPages) {
871
+ result.nextCursor = String(page + 1);
872
+ }
873
+ return result;
874
+ }
875
+ /**
876
+ * Push a shipment-package status update.
877
+ *
878
+ * Trendyol restricts the seller-side push to two transitions:
879
+ * - `Picking` — order picked up from the shelf / being prepared
880
+ * - `Invoiced` — invoice issued, ready for cargo handoff
881
+ *
882
+ * Other transitions (`Shipped`, `Delivered`, etc.) are driven by Trendyol
883
+ * or the cargo provider — call `processAlternativeDelivery` or
884
+ * `manualDeliverByPackageId` if you ship outside Trendyol's cargo
885
+ * network.
886
+ *
887
+ * Returns void; Trendyol responds with 200 + empty body on success.
888
+ */
889
+ async updatePackageStatus(packageId, input) {
890
+ await this.transport.request({
891
+ method: "PUT",
892
+ path: this.packagePath(packageId),
893
+ body: input,
894
+ rateLimiter: this.limiter
895
+ });
896
+ }
897
+ /**
898
+ * Notify Trendyol that one or more line items cannot be supplied
899
+ * ("Tedarik Edememe Bildirimi"). Marks the listed line IDs as
900
+ * `UnSupplied`. Trendyol cancels those quantities and notifies the
901
+ * customer.
902
+ *
903
+ * `reasonId` is a numeric code Trendyol publishes separately — consult
904
+ * the seller panel or the "Tedarik Edememe" docs for current values.
905
+ *
906
+ * Returns void; Trendyol responds with 200 + empty body on success.
907
+ */
908
+ async cancelPackageItem(packageId, input) {
909
+ if (!Array.isArray(input?.lines) || input.lines.length === 0) {
910
+ throw new core.ValidationError({ message: "cancelPackageItem: lines must not be empty" });
911
+ }
912
+ await this.transport.request({
913
+ method: "PUT",
914
+ path: `${this.packagePath(packageId)}/items/unsupplied`,
915
+ body: input,
916
+ rateLimiter: this.limiter
917
+ });
918
+ }
919
+ /**
920
+ * Extend the agreed delivery date for a shipment package by 1, 2, or 3 days.
921
+ * Trendyol enforces the [1, 3] range server-side; the SDK validates client-side
922
+ * to fail fast.
923
+ *
924
+ * Returns void; Trendyol responds with 200 + empty body on success.
925
+ */
926
+ async extendDeliveryDate(packageId, extendedDayCount) {
927
+ if (![1, 2, 3].includes(extendedDayCount)) {
928
+ throw new core.ValidationError({
929
+ message: `extendDeliveryDate: extendedDayCount must be 1, 2, or 3 (got ${extendedDayCount})`
930
+ });
931
+ }
932
+ await this.transport.request({
933
+ method: "PUT",
934
+ path: `${this.packagePath(packageId)}/extended-agreed-delivery-date`,
935
+ body: { extendedDayCount },
936
+ rateLimiter: this.limiter
937
+ });
938
+ }
939
+ /**
940
+ * Notify Trendyol of an alternative delivery channel — used when the
941
+ * seller is shipping via a non-Trendyol cargo provider. Trendyol then
942
+ * either SMSes the customer the tracking link (when `isPhoneNumber` is
943
+ * `true`) or stores the tracking URL on the package directly.
944
+ *
945
+ * Returns void; Trendyol responds with 200 + empty body on success.
946
+ */
947
+ async processAlternativeDelivery(packageId, input) {
948
+ await this.transport.request({
949
+ method: "PUT",
950
+ path: `${this.packagePath(packageId)}/alternative-delivery`,
951
+ body: input,
952
+ rateLimiter: this.limiter
953
+ });
954
+ }
955
+ /**
956
+ * Split a shipment package by moving a set of line IDs into a new
957
+ * package. The original package keeps the remaining lines.
958
+ *
959
+ * @param packageId The package to split.
960
+ * @param orderLineIds Line IDs to move into the new package (1+).
961
+ * @throws {ValidationError} when `orderLineIds` is empty.
962
+ */
963
+ async splitPackage(packageId, orderLineIds) {
964
+ if (!Array.isArray(orderLineIds) || orderLineIds.length === 0) {
965
+ throw new core.ValidationError({ message: "splitPackage: orderLineIds must not be empty" });
966
+ }
967
+ await this.transport.request({
968
+ method: "POST",
969
+ path: `${this.packagePath(packageId)}/split`,
970
+ body: { orderLineIds },
971
+ rateLimiter: this.limiter
972
+ });
973
+ }
974
+ /**
975
+ * Split a shipment package by quantity. Each `QuantitySplit` entry
976
+ * carves a single line into multiple packages — e.g. `{ orderLineId: 100,
977
+ * quantities: [2, 2, 1] }` splits 5 units of line 100 into three packages
978
+ * of 2 + 2 + 1.
979
+ *
980
+ * @throws {ValidationError} when `quantitySplit` is empty.
981
+ */
982
+ async splitPackageByQuantity(packageId, quantitySplit) {
983
+ if (!Array.isArray(quantitySplit) || quantitySplit.length === 0) {
984
+ throw new core.ValidationError({
985
+ message: "splitPackageByQuantity: quantitySplit must not be empty"
986
+ });
987
+ }
988
+ await this.transport.request({
989
+ method: "POST",
990
+ path: `${this.packagePath(packageId)}/quantity-split`,
991
+ body: { quantitySplit },
992
+ rateLimiter: this.limiter
993
+ });
994
+ }
995
+ /**
996
+ * Split a shipment package into multiple new packages by grouping line
997
+ * IDs. Each `SplitGroup` becomes one new package containing the listed
998
+ * line IDs.
999
+ *
1000
+ * @throws {ValidationError} when `splitGroups` is empty.
1001
+ */
1002
+ async multiSplitPackage(packageId, splitGroups) {
1003
+ if (!Array.isArray(splitGroups) || splitGroups.length === 0) {
1004
+ throw new core.ValidationError({ message: "multiSplitPackage: splitGroups must not be empty" });
1005
+ }
1006
+ await this.transport.request({
1007
+ method: "POST",
1008
+ path: `${this.packagePath(packageId)}/multi-split`,
1009
+ body: { splitGroups },
1010
+ rateLimiter: this.limiter
1011
+ });
1012
+ }
1013
+ /**
1014
+ * Split a shipment package into multiple new packages, each containing a
1015
+ * mix of line items at specific quantities. This is the most expressive
1016
+ * split — use it when you need fine-grained control over which line IDs
1017
+ * and how many of each end up in each new package.
1018
+ *
1019
+ * @throws {ValidationError} when `splitPackages` is empty.
1020
+ */
1021
+ async splitMultiPackagesByQuantity(packageId, splitPackages) {
1022
+ if (!Array.isArray(splitPackages) || splitPackages.length === 0) {
1023
+ throw new core.ValidationError({
1024
+ message: "splitMultiPackagesByQuantity: splitPackages must not be empty"
1025
+ });
1026
+ }
1027
+ await this.transport.request({
1028
+ method: "POST",
1029
+ path: `${this.packagePath(packageId)}/split-packages`,
1030
+ body: { splitPackages },
1031
+ rateLimiter: this.limiter
1032
+ });
1033
+ }
1034
+ /**
1035
+ * Change the cargo provider on an existing shipment package. Use one of
1036
+ * Trendyol's documented marketplace cargo codes (`'YKMP'`, `'ARASMP'`,
1037
+ * `'SURATMP'`, etc.) — see `TrendyolCargoProvider` for the full list.
1038
+ */
1039
+ async changeCargoProvider(packageId, cargoProvider) {
1040
+ await this.transport.request({
1041
+ method: "PUT",
1042
+ path: `${this.packagePath(packageId)}/cargo-providers`,
1043
+ body: { cargoProvider },
1044
+ rateLimiter: this.limiter
1045
+ });
1046
+ }
1047
+ /**
1048
+ * Mark a shipment package as manually delivered via its package ID.
1049
+ * Used when the seller delivered the order outside Trendyol's cargo
1050
+ * network and needs to flip the package to `Delivered` after handover.
1051
+ *
1052
+ * No request body; Trendyol responds with 200 + empty body on success.
1053
+ */
1054
+ async manualDeliverByPackageId(packageId) {
1055
+ await this.transport.request({
1056
+ method: "PUT",
1057
+ path: `${this.packagePath(packageId)}/manual-invoice-delivery`,
1058
+ rateLimiter: this.limiter
1059
+ });
1060
+ }
1061
+ /**
1062
+ * Manual-deliver variant that takes the cargo tracking number instead
1063
+ * of the package ID. Useful when you only have the tracking number on
1064
+ * hand (e.g., from a cargo provider webhook).
1065
+ *
1066
+ * Note the path structure: tracking number sits at a sibling location,
1067
+ * not under `/shipment-packages/{id}/...`.
1068
+ */
1069
+ async manualDeliverByTrackingNumber(cargoTrackingNumber) {
1070
+ await this.transport.request({
1071
+ method: "PUT",
1072
+ path: `/integration/order/sellers/${this.sellerId}/shipment-packages/manual-invoice-delivery-by-tracking-number/${encodeURIComponent(String(cargoTrackingNumber))}`,
1073
+ rateLimiter: this.limiter
1074
+ });
1075
+ }
1076
+ /**
1077
+ * Mark a package as delivered through an authorized service ("yetkili
1078
+ * servis"). For appliance / installation-required products that are
1079
+ * delivered + installed by a third-party service partner.
1080
+ */
1081
+ async markDeliveredByService(packageId) {
1082
+ await this.transport.request({
1083
+ method: "PUT",
1084
+ path: `${this.packagePath(packageId)}/delivered-by-service`,
1085
+ rateLimiter: this.limiter
1086
+ });
1087
+ }
1088
+ /**
1089
+ * Update box / packaging metadata on a shipment package (desi value
1090
+ * and/or number of boxes). Either field can be sent alone.
1091
+ */
1092
+ async updateBoxInfo(packageId, input) {
1093
+ await this.transport.request({
1094
+ method: "PUT",
1095
+ path: `${this.packagePath(packageId)}/box-info`,
1096
+ body: input,
1097
+ rateLimiter: this.limiter
1098
+ });
422
1099
  }
423
- if (typeof node.isCod === "boolean") out.isCod = node.isCod;
424
- if (typeof node.is4P === "boolean") out.is4P = node.is4P;
425
- if (node.invoiceLink !== void 0) out.invoiceLink = node.invoiceLink;
426
- if (node.createdBy !== void 0) out.createdBy = node.createdBy;
427
- return out;
428
- }
429
- var OrdersResource = class {
430
- constructor(transport, sellerId, limiter) {
431
- this.transport = transport;
432
- this.sellerId = sellerId;
433
- this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 1e3, intervalMs: 6e4 });
1100
+ /**
1101
+ * Update labor costs for one or more order lines.
1102
+ *
1103
+ * **Wire note:** Trendyol's request body is a raw array (no envelope),
1104
+ * not `{ items: [...] }`. The SDK forwards `items` verbatim.
1105
+ *
1106
+ * @throws {ValidationError} when `items` is empty.
1107
+ */
1108
+ async updateLaborCosts(packageId, items) {
1109
+ if (!Array.isArray(items) || items.length === 0) {
1110
+ throw new core.ValidationError({ message: "updateLaborCosts: items must not be empty" });
1111
+ }
1112
+ await this.transport.request({
1113
+ method: "PUT",
1114
+ path: `${this.packagePath(packageId)}/labor-costs`,
1115
+ body: items,
1116
+ rateLimiter: this.limiter
1117
+ });
434
1118
  }
435
- transport;
436
- sellerId;
437
- limiter;
438
1119
  /**
439
- * List shipment packages for the seller.
1120
+ * Reassign a shipment package to a different warehouse. `warehouseId`
1121
+ * comes from `client.suppliers.getAddresses()` (filter by `isShipmentAddress`).
1122
+ */
1123
+ async updateWarehouse(packageId, warehouseId) {
1124
+ await this.transport.request({
1125
+ method: "PUT",
1126
+ path: `${this.packagePath(packageId)}/warehouse`,
1127
+ body: { warehouseId },
1128
+ rateLimiter: this.limiter
1129
+ });
1130
+ }
1131
+ /**
1132
+ * Stream variant of `list()`. Trendyol's `getShipmentPackagesStream`
1133
+ * returns the same `ShipmentPackage` shape but paginates with an opaque
1134
+ * cursor — useful when the dataset is large and page-based pagination
1135
+ * would hit the 10 000-record cap.
440
1136
  *
441
1137
  * @example
442
1138
  * ```ts
443
1139
  * import { paginate } from '@lonca/core';
444
- * for await (const pkg of paginate((p) => client.orders.list({ ...p, status: 'Created' }))) {
445
- * console.log(pkg.id, pkg.status, pkg.customer.firstName);
1140
+ * for await (const pkg of paginate((p) =>
1141
+ * client.orders.listStream({ ...p, packageItemStatuses: 'Created,Picking' }),
1142
+ * )) {
1143
+ * console.log(pkg.id, pkg.status);
446
1144
  * }
447
1145
  * ```
448
1146
  */
449
- async list(params = {}) {
1147
+ async listStream(params = {}) {
450
1148
  const size = Math.min(params.limit ?? DEFAULT_PAGE_SIZE2, MAX_PAGE_SIZE);
451
- const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
452
- const query = { page, size };
453
- if (params.status) query.status = params.status;
454
- if (params.orderNumber) query.orderNumber = params.orderNumber;
455
- if (params.startDate) query.startDate = params.startDate.getTime();
456
- if (params.endDate) query.endDate = params.endDate.getTime();
1149
+ const query = { size };
1150
+ if (params.cursor) query.nextCursor = params.cursor;
1151
+ if (params.packageItemStatuses) query.packageItemStatuses = params.packageItemStatuses;
1152
+ if (params.lastModifiedStartDate) {
1153
+ query.lastModifiedStartDate = params.lastModifiedStartDate.getTime();
1154
+ }
1155
+ if (params.lastModifiedEndDate) {
1156
+ query.lastModifiedEndDate = params.lastModifiedEndDate.getTime();
1157
+ }
457
1158
  const data = await this.transport.request({
458
1159
  method: "GET",
459
- path: `/integration/order/sellers/${this.sellerId}/orders`,
1160
+ path: `/integration/order/sellers/${this.sellerId}/orders/stream`,
460
1161
  query,
461
1162
  rateLimiter: this.limiter
462
1163
  });
463
1164
  const items = (data.content ?? []).map(normalizePackage);
1165
+ const result = { items };
1166
+ if (data.hasMore && data.nextCursor) {
1167
+ result.nextCursor = data.nextCursor;
1168
+ }
1169
+ return result;
1170
+ }
1171
+ /**
1172
+ * Fetch the per-parcel cargo-fee breakdown for a single cargo invoice
1173
+ * (Trendyol's `getCargoInvoiceItems`). Useful for reconciling Trendyol's
1174
+ * cargo deductions against your shipped packages.
1175
+ *
1176
+ * `invoiceSerialNumber` is sourced from the Current Account Statement
1177
+ * ("Cari Hesap Ekstresi") with `transactionType=DeductionInvoices`.
1178
+ *
1179
+ * Page-based pagination internally (cursor encodes the page index).
1180
+ */
1181
+ async getCargoInvoiceItems(invoiceSerialNumber, params = {}) {
1182
+ const size = Math.min(params.limit ?? 500, 500);
1183
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
1184
+ const data = await this.transport.request({
1185
+ method: "GET",
1186
+ path: `/integration/finance/che/sellers/${this.sellerId}/cargo-invoice/${encodeURIComponent(invoiceSerialNumber)}/items`,
1187
+ query: { page, size },
1188
+ rateLimiter: this.limiter
1189
+ });
1190
+ const items = (data.content ?? []).map((row) => {
1191
+ const out = { raw: row };
1192
+ if (row.shipmentPackageType !== void 0) out.shipmentPackageType = row.shipmentPackageType;
1193
+ if (row.parcelUniqueId !== void 0) {
1194
+ out.parcelUniqueId = typeof row.parcelUniqueId === "string" ? row.parcelUniqueId : String(row.parcelUniqueId);
1195
+ }
1196
+ if (row.orderNumber !== void 0) out.orderNumber = row.orderNumber;
1197
+ if (typeof row.amount === "number") out.amount = row.amount;
1198
+ if (typeof row.desi === "number") out.desi = row.desi;
1199
+ return out;
1200
+ });
464
1201
  const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
465
1202
  const result = { items };
466
1203
  if (page + 1 < totalPages) {
@@ -468,6 +1205,104 @@ var OrdersResource = class {
468
1205
  }
469
1206
  return result;
470
1207
  }
1208
+ /**
1209
+ * Notify Trendyol that a shipped package was returned to you (manual
1210
+ * return flow, e.g. customer dropped it at your address or you got the
1211
+ * package back without going through Trendyol's return cargo).
1212
+ *
1213
+ * No body; Trendyol responds with 200 + empty body on success.
1214
+ */
1215
+ async manualReturnByPackageId(packageId) {
1216
+ await this.transport.request({
1217
+ method: "PUT",
1218
+ path: `${this.packagePath(packageId)}/manual-return`,
1219
+ rateLimiter: this.limiter
1220
+ });
1221
+ }
1222
+ /**
1223
+ * Manual-return variant that takes the cargo tracking number instead of
1224
+ * the package ID. Useful when the cargo provider's webhook only carries
1225
+ * the tracking number.
1226
+ *
1227
+ * Sibling path (not under `/{packageId}/...`).
1228
+ */
1229
+ async manualReturnByTrackingNumber(cargoTrackingNumber) {
1230
+ await this.transport.request({
1231
+ method: "PUT",
1232
+ path: `/integration/order/sellers/${this.sellerId}/shipment-packages/manual-return-by-tracking-number/${encodeURIComponent(String(cargoTrackingNumber))}`,
1233
+ rateLimiter: this.limiter
1234
+ });
1235
+ }
1236
+ /**
1237
+ * Fetch Trendyol Express compensation tickets (claims filed when a
1238
+ * shipment is lost or damaged in transit). Page-based pagination
1239
+ * internally; the SDK exposes the cursor convention.
1240
+ *
1241
+ * Note the different base path — `/integration/tex/compensation/...`,
1242
+ * not the regular `/integration/order/...`.
1243
+ *
1244
+ * @example
1245
+ * ```ts
1246
+ * import { paginate } from '@lonca/core';
1247
+ * const tickets = await client.orders.getCompensationTickets({
1248
+ * startDate: new Date('2026-01-01'),
1249
+ * endDate: new Date('2026-02-01'),
1250
+ * });
1251
+ * for (const t of tickets.items) {
1252
+ * console.log(t.orderNumber, t.currentState, t.stateMessage);
1253
+ * }
1254
+ * ```
1255
+ */
1256
+ async getCompensationTickets(params = {}) {
1257
+ const size = Math.min(params.limit ?? 200, 200);
1258
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
1259
+ const query = { page, size };
1260
+ if (params.startDate) query.startDate = params.startDate.getTime();
1261
+ if (params.endDate) query.endDate = params.endDate.getTime();
1262
+ const res = await this.transport.request({
1263
+ method: "GET",
1264
+ path: `/integration/tex/compensation/sellers/${this.sellerId}/tickets`,
1265
+ query,
1266
+ rateLimiter: this.limiter
1267
+ });
1268
+ const tickets = Array.isArray(res.data) ? res.data : res.data?.items ?? res.data?.content ?? res.content ?? [];
1269
+ const items = tickets.map((t) => {
1270
+ const itemDetails = (t.itemDetails ?? []).map((d) => {
1271
+ const out2 = {};
1272
+ if (typeof d.itemAmount === "number") out2.itemAmount = d.itemAmount;
1273
+ if (d.itemCode !== void 0) out2.itemCode = d.itemCode;
1274
+ if (typeof d.itemCount === "number") out2.itemCount = d.itemCount;
1275
+ if (d.itemName !== void 0) out2.itemName = d.itemName;
1276
+ return out2;
1277
+ });
1278
+ const out = {
1279
+ itemDetails,
1280
+ raw: t
1281
+ };
1282
+ if (t.cargoProvider !== void 0) out.cargoProvider = t.cargoProvider;
1283
+ if (t.compensateReason !== void 0) out.compensateReason = t.compensateReason;
1284
+ const createdAt = toIso2(t.createDate);
1285
+ if (createdAt) out.createdAt = createdAt;
1286
+ if (t.currentState !== void 0) {
1287
+ out.currentState = t.currentState;
1288
+ }
1289
+ if (t.deliveryNumber !== void 0) out.deliveryNumber = t.deliveryNumber;
1290
+ if (t.orderNumber !== void 0) out.orderNumber = t.orderNumber;
1291
+ if (t.requestedBy !== void 0) out.requestedBy = t.requestedBy;
1292
+ if (t.stateMessage !== void 0) out.stateMessage = t.stateMessage;
1293
+ if (t.totalItemsAmount !== void 0) out.totalItemsAmount = t.totalItemsAmount;
1294
+ return out;
1295
+ });
1296
+ const result = { items };
1297
+ const total = typeof res.totalCount === "number" ? res.totalCount : 0;
1298
+ if ((page + 1) * size < total) {
1299
+ result.nextCursor = String(page + 1);
1300
+ }
1301
+ return result;
1302
+ }
1303
+ packagePath(packageId) {
1304
+ return `/integration/order/sellers/${this.sellerId}/shipment-packages/${encodeURIComponent(String(packageId))}`;
1305
+ }
471
1306
  };
472
1307
  var DEFAULT_PAGE_SIZE3 = 50;
473
1308
  var MAX_PAGE_SIZE2 = 1e3;
@@ -514,7 +1349,7 @@ function normalizeVariant(variant) {
514
1349
  if (stock !== void 0) out.stock = stock;
515
1350
  return out;
516
1351
  }
517
- function toIso2(epochMs) {
1352
+ function toIso3(epochMs) {
518
1353
  if (typeof epochMs !== "number" || !Number.isFinite(epochMs)) return void 0;
519
1354
  return new Date(epochMs).toISOString();
520
1355
  }
@@ -528,8 +1363,8 @@ function normalizeProduct(node) {
528
1363
  images: normalizeImages(node.images),
529
1364
  attributes: (node.attributes ?? []).map(normalizeAttribute2),
530
1365
  variants: (node.variants ?? []).map(normalizeVariant),
531
- createdAt: toIso2(node.creationDate) ?? "",
532
- updatedAt: toIso2(node.lastModifiedDate) ?? "",
1366
+ createdAt: toIso3(node.creationDate) ?? "",
1367
+ updatedAt: toIso3(node.lastModifiedDate) ?? "",
533
1368
  raw: node
534
1369
  };
535
1370
  if (node.description !== void 0) out.description = node.description;
@@ -575,13 +1410,13 @@ function normalizeUnapproved(node) {
575
1410
  out.specialConsumptionTax = node.specialConsumptionTax;
576
1411
  }
577
1412
  if (node.sgrPrice !== void 0) out.sgrPrice = node.sgrPrice;
578
- const createdAt = toIso2(node.createDateTime);
1413
+ const createdAt = toIso3(node.createDateTime);
579
1414
  if (createdAt) out.createdAt = createdAt;
580
- const updatedAt = toIso2(node.lastUpdateDate);
1415
+ const updatedAt = toIso3(node.lastUpdateDate);
581
1416
  if (updatedAt) out.updatedAt = updatedAt;
582
- const priceChanged = toIso2(node.lastPriceChangeDate);
1417
+ const priceChanged = toIso3(node.lastPriceChangeDate);
583
1418
  if (priceChanged) out.lastPriceChangedAt = priceChanged;
584
- const stockChanged = toIso2(node.lastStockChangeDate);
1419
+ const stockChanged = toIso3(node.lastStockChangeDate);
585
1420
  if (stockChanged) out.lastStockChangedAt = stockChanged;
586
1421
  return out;
587
1422
  }
@@ -606,7 +1441,7 @@ function normalizeProductBase(data) {
606
1441
  archived: !!data.archived,
607
1442
  raw: data
608
1443
  };
609
- const approvedAt = toIso2(data.approvedDate);
1444
+ const approvedAt = toIso3(data.approvedDate);
610
1445
  if (approvedAt) out.approvedAt = approvedAt;
611
1446
  if (data.listingId !== void 0) out.listingId = data.listingId;
612
1447
  if (data.contentId !== void 0) out.contentId = String(data.contentId);
@@ -628,9 +1463,9 @@ function normalizeBatchResult(data) {
628
1463
  };
629
1464
  if (typeof data.itemCount === "number") out.itemCount = data.itemCount;
630
1465
  if (typeof data.failedItemCount === "number") out.failedItemCount = data.failedItemCount;
631
- const createdAt = toIso2(data.creationDate);
1466
+ const createdAt = toIso3(data.creationDate);
632
1467
  if (createdAt) out.createdAt = createdAt;
633
- const lastModifiedAt = toIso2(data.lastModification);
1468
+ const lastModifiedAt = toIso3(data.lastModification);
634
1469
  if (lastModifiedAt) out.lastModifiedAt = lastModifiedAt;
635
1470
  if (data.sourceType !== void 0) out.sourceType = data.sourceType;
636
1471
  if (data.batchRequestType !== void 0) out.batchRequestType = data.batchRequestType;
@@ -976,6 +1811,120 @@ var ProductsResource = class {
976
1811
  return { batchRequestId: data?.batchRequestId ?? "" };
977
1812
  }
978
1813
  };
1814
+ function toIso4(epochMs) {
1815
+ if (typeof epochMs !== "number" || !Number.isFinite(epochMs) || epochMs === 0) {
1816
+ return void 0;
1817
+ }
1818
+ return new Date(epochMs).toISOString();
1819
+ }
1820
+ function normalizeAnswer(a) {
1821
+ if (!a) return void 0;
1822
+ const out = {};
1823
+ if (a.text !== void 0) out.text = a.text;
1824
+ const createdAt = toIso4(a.creationDate);
1825
+ if (createdAt) out.createdAt = createdAt;
1826
+ if (a.status !== void 0) out.status = a.status;
1827
+ return out;
1828
+ }
1829
+ function normalizeQuestion(node) {
1830
+ const out = {
1831
+ id: node.id !== void 0 ? String(node.id) : "",
1832
+ raw: node
1833
+ };
1834
+ if (node.text !== void 0) out.text = node.text;
1835
+ if (node.customerId !== void 0) out.customerId = String(node.customerId);
1836
+ if (node.userName !== void 0) out.userName = node.userName;
1837
+ if (typeof node.showUserName === "boolean") out.showUserName = node.showUserName;
1838
+ if (node.status !== void 0) out.status = node.status;
1839
+ if (typeof node.public === "boolean") out.public = node.public;
1840
+ if (node.productMainId !== void 0) out.productMainId = node.productMainId;
1841
+ if (node.productName !== void 0) out.productName = node.productName;
1842
+ if (node.imageUrl !== void 0) out.imageUrl = node.imageUrl;
1843
+ if (node.webUrl !== void 0) out.webUrl = node.webUrl;
1844
+ const createdAt = toIso4(node.creationDate);
1845
+ if (createdAt) out.createdAt = createdAt;
1846
+ if (node.answeredDateMessage !== void 0) out.answeredDateMessage = node.answeredDateMessage;
1847
+ const answer = normalizeAnswer(node.answer);
1848
+ if (answer) out.answer = answer;
1849
+ const rejectedAnswer = normalizeAnswer(node.rejectedAnswer);
1850
+ if (rejectedAnswer) out.rejectedAnswer = rejectedAnswer;
1851
+ const rejectedAt = toIso4(node.rejectedDate);
1852
+ if (rejectedAt) out.rejectedAt = rejectedAt;
1853
+ if (node.reason !== void 0) out.reason = node.reason;
1854
+ if (node.reportReason !== void 0) out.reportReason = node.reportReason;
1855
+ const reportedAt = toIso4(node.reportedDate);
1856
+ if (reportedAt) out.reportedAt = reportedAt;
1857
+ return out;
1858
+ }
1859
+ var QuestionsResource = class {
1860
+ constructor(transport, sellerId, limiter) {
1861
+ this.transport = transport;
1862
+ this.sellerId = sellerId;
1863
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 500, intervalMs: 6e4 });
1864
+ }
1865
+ transport;
1866
+ sellerId;
1867
+ limiter;
1868
+ /** Fetch a single question by its numeric ID. */
1869
+ async get(questionId) {
1870
+ const data = await this.transport.request({
1871
+ method: "GET",
1872
+ path: `/integration/qna/sellers/${this.sellerId}/questions/${encodeURIComponent(String(questionId))}`,
1873
+ rateLimiter: this.limiter
1874
+ });
1875
+ return normalizeQuestion(data);
1876
+ }
1877
+ /**
1878
+ * Filter questions by barcode / date range / status. Page-based
1879
+ * pagination internally; SDK exposes the opaque-cursor convention.
1880
+ */
1881
+ async list(params = {}) {
1882
+ const size = Math.min(params.limit ?? 50, 200);
1883
+ const page = params.cursor ? Number.parseInt(params.cursor, 10) : 0;
1884
+ const query = { page, size };
1885
+ if (params.barcode) query.barcode = params.barcode;
1886
+ if (params.startDate) query.startDate = params.startDate.getTime();
1887
+ if (params.endDate) query.endDate = params.endDate.getTime();
1888
+ if (params.status) query.status = params.status;
1889
+ const data = await this.transport.request({
1890
+ method: "GET",
1891
+ path: `/integration/qna/sellers/${this.sellerId}/questions/filter`,
1892
+ query,
1893
+ rateLimiter: this.limiter
1894
+ });
1895
+ const items = (data.content ?? []).map(normalizeQuestion);
1896
+ const result = { items };
1897
+ const totalPages = typeof data.totalPages === "number" ? data.totalPages : 0;
1898
+ if (page + 1 < totalPages) {
1899
+ result.nextCursor = String(page + 1);
1900
+ }
1901
+ return result;
1902
+ }
1903
+ /**
1904
+ * Reply to a question. Trendyol enforces 10–2000 characters on the
1905
+ * answer text; the SDK pre-validates client-side.
1906
+ *
1907
+ * @throws {ValidationError} when `text` is outside the 10–2000 char range.
1908
+ */
1909
+ async answer(questionId, text) {
1910
+ if (typeof text !== "string" || text.length < 10) {
1911
+ throw new core.ValidationError({
1912
+ message: `questions.answer: text must be at least 10 chars (got ${text?.length ?? 0})`
1913
+ });
1914
+ }
1915
+ if (text.length > 2e3) {
1916
+ throw new core.ValidationError({
1917
+ message: `questions.answer: text must be at most 2000 chars (got ${text.length})`
1918
+ });
1919
+ }
1920
+ return this.transport.request({
1921
+ method: "POST",
1922
+ path: `/integration/qna/sellers/${this.sellerId}/questions/${encodeURIComponent(String(questionId))}/answers`,
1923
+ body: { text },
1924
+ rateLimiter: this.limiter
1925
+ });
1926
+ }
1927
+ };
979
1928
  var VALID_TYPES = /* @__PURE__ */ new Set([
980
1929
  "SHIPMENT",
981
1930
  "RETURNING",
@@ -1056,6 +2005,170 @@ var SuppliersResource = class {
1056
2005
  return items;
1057
2006
  }
1058
2007
  };
2008
+ var TestOrdersResource = class {
2009
+ constructor(transport, sellerId, limiter) {
2010
+ this.transport = transport;
2011
+ this.sellerId = sellerId;
2012
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 60, intervalMs: 6e4 });
2013
+ }
2014
+ transport;
2015
+ sellerId;
2016
+ limiter;
2017
+ /**
2018
+ * Create a test order with the given customer / addresses / lines. The
2019
+ * SDK forwards the typed payload verbatim — drill into Trendyol's
2020
+ * `createTestOrder` doc for inner field rules.
2021
+ *
2022
+ * @throws {ValidationError} when required top-level fields are missing.
2023
+ */
2024
+ async create(input) {
2025
+ for (const k of ["customer", "invoiceAddress", "shippingAddress", "seller", "lines"]) {
2026
+ if (!input?.[k]) {
2027
+ throw new core.ValidationError({ message: `testOrders.create: ${k} is required` });
2028
+ }
2029
+ }
2030
+ return this.transport.request({
2031
+ method: "POST",
2032
+ path: `/integration/test/order/orders/core`,
2033
+ body: input,
2034
+ rateLimiter: this.limiter
2035
+ });
2036
+ }
2037
+ /** Push a test shipment package to the given status. */
2038
+ async updateStatus(packageId, status) {
2039
+ return this.transport.request({
2040
+ method: "PUT",
2041
+ path: `/integration/test/order/sellers/${this.sellerId}/shipment-packages/${encodeURIComponent(String(packageId))}/status`,
2042
+ body: { status },
2043
+ rateLimiter: this.limiter
2044
+ });
2045
+ }
2046
+ /** Move test claims to the `WaitingInAction` state. */
2047
+ async setClaimsWaitingInAction() {
2048
+ return this.transport.request({
2049
+ method: "PUT",
2050
+ path: `/integration/test/order/sellers/${this.sellerId}/claims/waiting-in-action`,
2051
+ rateLimiter: this.limiter
2052
+ });
2053
+ }
2054
+ };
2055
+ function normalizeWebhook(node) {
2056
+ const out = {
2057
+ id: node.id !== void 0 ? String(node.id) : "",
2058
+ raw: node
2059
+ };
2060
+ if (node.url !== void 0) out.url = node.url;
2061
+ if (node.authenticationType !== void 0) out.authenticationType = node.authenticationType;
2062
+ if (node.username !== void 0) out.username = node.username;
2063
+ if (node.apiKey !== void 0) out.apiKey = node.apiKey;
2064
+ if (node.subscribedStatuses !== void 0) out.subscribedStatuses = node.subscribedStatuses;
2065
+ if (typeof node.active === "boolean") out.active = node.active;
2066
+ else if (typeof node.isActive === "boolean") out.active = node.isActive;
2067
+ else if (typeof node.status === "string") out.active = node.status.toUpperCase() === "ACTIVE";
2068
+ return out;
2069
+ }
2070
+ var WebhooksResource = class {
2071
+ constructor(transport, sellerId, limiter) {
2072
+ this.transport = transport;
2073
+ this.sellerId = sellerId;
2074
+ this.limiter = limiter ?? new core.TokenBucketRateLimiter({ capacity: 50, intervalMs: 6e4 });
2075
+ }
2076
+ transport;
2077
+ sellerId;
2078
+ limiter;
2079
+ /**
2080
+ * Create a new webhook subscription. Trendyol caps subscriptions at 15
2081
+ * per seller — the SDK does NOT pre-check (you'd need to call `list()`
2082
+ * first), but Trendyol returns 400 when the cap is exceeded.
2083
+ *
2084
+ * @throws {ValidationError} when `url` or `authenticationType` is missing.
2085
+ */
2086
+ async create(input) {
2087
+ this.validateInput(input, "create");
2088
+ return this.transport.request({
2089
+ method: "POST",
2090
+ path: `/integration/sellers/${this.sellerId}/webhooks`,
2091
+ body: input,
2092
+ rateLimiter: this.limiter
2093
+ });
2094
+ }
2095
+ /** List all registered webhook subscriptions. */
2096
+ async list() {
2097
+ const data = await this.transport.request({
2098
+ method: "GET",
2099
+ path: `/integration/sellers/${this.sellerId}/webhooks`,
2100
+ rateLimiter: this.limiter
2101
+ });
2102
+ const rows = Array.isArray(data) ? data : Array.isArray(data?.webhooks) ? data.webhooks : Array.isArray(data?.content) ? data.content : [];
2103
+ return rows.map((r) => normalizeWebhook(r));
2104
+ }
2105
+ /**
2106
+ * Update a webhook subscription. Same input shape as `create`; replaces
2107
+ * the whole subscription (Trendyol does NOT partially update).
2108
+ */
2109
+ async update(webhookId, input) {
2110
+ this.validateInput(input, "update");
2111
+ return this.transport.request({
2112
+ method: "PUT",
2113
+ path: this.webhookPath(webhookId),
2114
+ body: input,
2115
+ rateLimiter: this.limiter
2116
+ });
2117
+ }
2118
+ /** Permanently delete a webhook subscription. */
2119
+ async delete(webhookId) {
2120
+ return this.transport.request({
2121
+ method: "DELETE",
2122
+ path: this.webhookPath(webhookId),
2123
+ rateLimiter: this.limiter
2124
+ });
2125
+ }
2126
+ /** Re-activate a previously-deactivated webhook subscription. */
2127
+ async activate(webhookId) {
2128
+ return this.transport.request({
2129
+ method: "PUT",
2130
+ path: `${this.webhookPath(webhookId)}/activate`,
2131
+ rateLimiter: this.limiter
2132
+ });
2133
+ }
2134
+ /**
2135
+ * Deactivate a webhook subscription. Trendyol automatically deactivates
2136
+ * a subscription after persistent delivery failures (and sends 2 emails);
2137
+ * use `activate()` to bring it back online once your endpoint is healthy.
2138
+ */
2139
+ async deactivate(webhookId) {
2140
+ return this.transport.request({
2141
+ method: "PUT",
2142
+ path: `${this.webhookPath(webhookId)}/deactivate`,
2143
+ rateLimiter: this.limiter
2144
+ });
2145
+ }
2146
+ validateInput(input, method) {
2147
+ if (!input?.url || typeof input.url !== "string") {
2148
+ throw new core.ValidationError({ message: `webhooks.${method}: url is required` });
2149
+ }
2150
+ if (input.authenticationType !== "BASIC_AUTHENTICATION" && input.authenticationType !== "API_KEY") {
2151
+ throw new core.ValidationError({
2152
+ message: `webhooks.${method}: authenticationType must be 'BASIC_AUTHENTICATION' or 'API_KEY'`
2153
+ });
2154
+ }
2155
+ if (input.authenticationType === "BASIC_AUTHENTICATION") {
2156
+ if (!input.username || !input.password) {
2157
+ throw new core.ValidationError({
2158
+ message: `webhooks.${method}: BASIC_AUTHENTICATION requires username + password`
2159
+ });
2160
+ }
2161
+ } else if (!input.apiKey) {
2162
+ throw new core.ValidationError({
2163
+ message: `webhooks.${method}: API_KEY requires apiKey`
2164
+ });
2165
+ }
2166
+ if (input.subscribedStatuses && input.subscribedStatuses.length === 0) ;
2167
+ }
2168
+ webhookPath(webhookId) {
2169
+ return `/integration/sellers/${this.sellerId}/webhooks/${encodeURIComponent(String(webhookId))}`;
2170
+ }
2171
+ };
1059
2172
 
1060
2173
  // src/auth.ts
1061
2174
  function buildAuthHeader(apiKey, apiSecret) {
@@ -1148,7 +2261,12 @@ var TrendyolTransport = class {
1148
2261
  signal: this.composeSignal(opts.signal)
1149
2262
  };
1150
2263
  if (opts.body !== void 0 && opts.method !== "GET") {
1151
- init.body = JSON.stringify(opts.body);
2264
+ if (opts.body instanceof FormData) {
2265
+ init.body = opts.body;
2266
+ delete headers["Content-Type"];
2267
+ } else {
2268
+ init.body = JSON.stringify(opts.body);
2269
+ }
1152
2270
  }
1153
2271
  this.logger.debug("trendyol.request", {
1154
2272
  method: opts.method,
@@ -1263,16 +2381,32 @@ function createTrendyolClient(opts) {
1263
2381
  suppliers: new SuppliersResource(transport, opts.sellerId),
1264
2382
  products: new ProductsResource(transport, opts.sellerId),
1265
2383
  inventory: new InventoryResource(transport, opts.sellerId),
1266
- orders: new OrdersResource(transport, opts.sellerId)
2384
+ orders: new OrdersResource(transport, opts.sellerId),
2385
+ claims: new ClaimsResource(transport, opts.sellerId),
2386
+ webhooks: new WebhooksResource(transport, opts.sellerId),
2387
+ questions: new QuestionsResource(transport, opts.sellerId),
2388
+ invoices: new InvoicesResource(transport, opts.sellerId),
2389
+ finance: new FinanceResource(transport, opts.sellerId),
2390
+ labels: new LabelsResource(transport, opts.sellerId),
2391
+ testOrders: new TestOrdersResource(transport, opts.sellerId),
2392
+ locations: new LocationsResource(transport)
1267
2393
  };
1268
2394
  }
1269
2395
 
1270
2396
  exports.BrandsResource = BrandsResource;
1271
2397
  exports.CategoriesResource = CategoriesResource;
2398
+ exports.ClaimsResource = ClaimsResource;
2399
+ exports.FinanceResource = FinanceResource;
1272
2400
  exports.InventoryResource = InventoryResource;
2401
+ exports.InvoicesResource = InvoicesResource;
2402
+ exports.LabelsResource = LabelsResource;
2403
+ exports.LocationsResource = LocationsResource;
1273
2404
  exports.OrdersResource = OrdersResource;
1274
2405
  exports.ProductsResource = ProductsResource;
2406
+ exports.QuestionsResource = QuestionsResource;
1275
2407
  exports.SuppliersResource = SuppliersResource;
2408
+ exports.TestOrdersResource = TestOrdersResource;
2409
+ exports.WebhooksResource = WebhooksResource;
1276
2410
  exports.createTrendyolClient = createTrendyolClient;
1277
2411
  //# sourceMappingURL=index.cjs.map
1278
2412
  //# sourceMappingURL=index.cjs.map