@lonca/trendyol 0.4.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
  }
@@ -365,8 +768,8 @@ function normalizePackage(node) {
365
768
  orderNumber: node.orderNumber ?? "",
366
769
  status: node.status ?? "",
367
770
  customer: normalizeCustomer(node),
368
- orderDate: toIso(node.orderDate) ?? "",
369
- lastModifiedDate: toIso(node.lastModifiedDate) ?? "",
771
+ orderDate: toIso2(node.orderDate) ?? "",
772
+ lastModifiedDate: toIso2(node.lastModifiedDate) ?? "",
370
773
  currencyCode: node.currencyCode ?? "",
371
774
  packageTotalPrice: typeof node.packageTotalPrice === "number" ? node.packageTotalPrice : 0,
372
775
  packageGrossAmount: typeof node.packageGrossAmount === "number" ? node.packageGrossAmount : 0,
@@ -386,13 +789,13 @@ function normalizePackage(node) {
386
789
  if (node.shipmentPackageStatus !== void 0) {
387
790
  out.shipmentPackageStatus = node.shipmentPackageStatus;
388
791
  }
389
- const agreed = toIso(node.agreedDeliveryDate);
792
+ const agreed = toIso2(node.agreedDeliveryDate);
390
793
  if (agreed) out.agreedDeliveryDate = agreed;
391
- const estStart = toIso(node.estimatedDeliveryStartDate);
794
+ const estStart = toIso2(node.estimatedDeliveryStartDate);
392
795
  if (estStart) out.estimatedDeliveryStartDate = estStart;
393
- const estEnd = toIso(node.estimatedDeliveryEndDate);
796
+ const estEnd = toIso2(node.estimatedDeliveryEndDate);
394
797
  if (estEnd) out.estimatedDeliveryEndDate = estEnd;
395
- const origin = toIso(node.originShipmentDate);
798
+ const origin = toIso2(node.originShipmentDate);
396
799
  if (origin) out.originShipmentDate = origin;
397
800
  const invoice = normalizeAddress(node.invoiceAddress);
398
801
  if (invoice) out.invoiceAddress = invoice;
@@ -802,6 +1205,101 @@ var OrdersResource = class {
802
1205
  }
803
1206
  return result;
804
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
+ }
805
1303
  packagePath(packageId) {
806
1304
  return `/integration/order/sellers/${this.sellerId}/shipment-packages/${encodeURIComponent(String(packageId))}`;
807
1305
  }
@@ -851,7 +1349,7 @@ function normalizeVariant(variant) {
851
1349
  if (stock !== void 0) out.stock = stock;
852
1350
  return out;
853
1351
  }
854
- function toIso2(epochMs) {
1352
+ function toIso3(epochMs) {
855
1353
  if (typeof epochMs !== "number" || !Number.isFinite(epochMs)) return void 0;
856
1354
  return new Date(epochMs).toISOString();
857
1355
  }
@@ -865,8 +1363,8 @@ function normalizeProduct(node) {
865
1363
  images: normalizeImages(node.images),
866
1364
  attributes: (node.attributes ?? []).map(normalizeAttribute2),
867
1365
  variants: (node.variants ?? []).map(normalizeVariant),
868
- createdAt: toIso2(node.creationDate) ?? "",
869
- updatedAt: toIso2(node.lastModifiedDate) ?? "",
1366
+ createdAt: toIso3(node.creationDate) ?? "",
1367
+ updatedAt: toIso3(node.lastModifiedDate) ?? "",
870
1368
  raw: node
871
1369
  };
872
1370
  if (node.description !== void 0) out.description = node.description;
@@ -912,13 +1410,13 @@ function normalizeUnapproved(node) {
912
1410
  out.specialConsumptionTax = node.specialConsumptionTax;
913
1411
  }
914
1412
  if (node.sgrPrice !== void 0) out.sgrPrice = node.sgrPrice;
915
- const createdAt = toIso2(node.createDateTime);
1413
+ const createdAt = toIso3(node.createDateTime);
916
1414
  if (createdAt) out.createdAt = createdAt;
917
- const updatedAt = toIso2(node.lastUpdateDate);
1415
+ const updatedAt = toIso3(node.lastUpdateDate);
918
1416
  if (updatedAt) out.updatedAt = updatedAt;
919
- const priceChanged = toIso2(node.lastPriceChangeDate);
1417
+ const priceChanged = toIso3(node.lastPriceChangeDate);
920
1418
  if (priceChanged) out.lastPriceChangedAt = priceChanged;
921
- const stockChanged = toIso2(node.lastStockChangeDate);
1419
+ const stockChanged = toIso3(node.lastStockChangeDate);
922
1420
  if (stockChanged) out.lastStockChangedAt = stockChanged;
923
1421
  return out;
924
1422
  }
@@ -943,7 +1441,7 @@ function normalizeProductBase(data) {
943
1441
  archived: !!data.archived,
944
1442
  raw: data
945
1443
  };
946
- const approvedAt = toIso2(data.approvedDate);
1444
+ const approvedAt = toIso3(data.approvedDate);
947
1445
  if (approvedAt) out.approvedAt = approvedAt;
948
1446
  if (data.listingId !== void 0) out.listingId = data.listingId;
949
1447
  if (data.contentId !== void 0) out.contentId = String(data.contentId);
@@ -965,9 +1463,9 @@ function normalizeBatchResult(data) {
965
1463
  };
966
1464
  if (typeof data.itemCount === "number") out.itemCount = data.itemCount;
967
1465
  if (typeof data.failedItemCount === "number") out.failedItemCount = data.failedItemCount;
968
- const createdAt = toIso2(data.creationDate);
1466
+ const createdAt = toIso3(data.creationDate);
969
1467
  if (createdAt) out.createdAt = createdAt;
970
- const lastModifiedAt = toIso2(data.lastModification);
1468
+ const lastModifiedAt = toIso3(data.lastModification);
971
1469
  if (lastModifiedAt) out.lastModifiedAt = lastModifiedAt;
972
1470
  if (data.sourceType !== void 0) out.sourceType = data.sourceType;
973
1471
  if (data.batchRequestType !== void 0) out.batchRequestType = data.batchRequestType;
@@ -1313,6 +1811,120 @@ var ProductsResource = class {
1313
1811
  return { batchRequestId: data?.batchRequestId ?? "" };
1314
1812
  }
1315
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
+ };
1316
1928
  var VALID_TYPES = /* @__PURE__ */ new Set([
1317
1929
  "SHIPMENT",
1318
1930
  "RETURNING",
@@ -1393,6 +2005,170 @@ var SuppliersResource = class {
1393
2005
  return items;
1394
2006
  }
1395
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
+ };
1396
2172
 
1397
2173
  // src/auth.ts
1398
2174
  function buildAuthHeader(apiKey, apiSecret) {
@@ -1485,7 +2261,12 @@ var TrendyolTransport = class {
1485
2261
  signal: this.composeSignal(opts.signal)
1486
2262
  };
1487
2263
  if (opts.body !== void 0 && opts.method !== "GET") {
1488
- 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
+ }
1489
2270
  }
1490
2271
  this.logger.debug("trendyol.request", {
1491
2272
  method: opts.method,
@@ -1600,16 +2381,32 @@ function createTrendyolClient(opts) {
1600
2381
  suppliers: new SuppliersResource(transport, opts.sellerId),
1601
2382
  products: new ProductsResource(transport, opts.sellerId),
1602
2383
  inventory: new InventoryResource(transport, opts.sellerId),
1603
- 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)
1604
2393
  };
1605
2394
  }
1606
2395
 
1607
2396
  exports.BrandsResource = BrandsResource;
1608
2397
  exports.CategoriesResource = CategoriesResource;
2398
+ exports.ClaimsResource = ClaimsResource;
2399
+ exports.FinanceResource = FinanceResource;
1609
2400
  exports.InventoryResource = InventoryResource;
2401
+ exports.InvoicesResource = InvoicesResource;
2402
+ exports.LabelsResource = LabelsResource;
2403
+ exports.LocationsResource = LocationsResource;
1610
2404
  exports.OrdersResource = OrdersResource;
1611
2405
  exports.ProductsResource = ProductsResource;
2406
+ exports.QuestionsResource = QuestionsResource;
1612
2407
  exports.SuppliersResource = SuppliersResource;
2408
+ exports.TestOrdersResource = TestOrdersResource;
2409
+ exports.WebhooksResource = WebhooksResource;
1613
2410
  exports.createTrendyolClient = createTrendyolClient;
1614
2411
  //# sourceMappingURL=index.cjs.map
1615
2412
  //# sourceMappingURL=index.cjs.map