shipflow 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +139 -33
  2. package/dist/carriers/aramex/adapter.d.ts +82 -0
  3. package/dist/carriers/aramex/adapter.d.ts.map +1 -0
  4. package/dist/carriers/aramex/index.d.ts +9 -0
  5. package/dist/carriers/aramex/index.d.ts.map +1 -0
  6. package/dist/carriers/aramex/index.js +694 -0
  7. package/dist/carriers/aramex/index.js.map +12 -0
  8. package/dist/carriers/aramex/mappers.d.ts +76 -0
  9. package/dist/carriers/aramex/mappers.d.ts.map +1 -0
  10. package/dist/carriers/aramex/services.d.ts +74 -0
  11. package/dist/carriers/aramex/services.d.ts.map +1 -0
  12. package/dist/carriers/aramex/types.d.ts +302 -0
  13. package/dist/carriers/aramex/types.d.ts.map +1 -0
  14. package/dist/carriers/aymakan/adapter.d.ts +8 -2
  15. package/dist/carriers/aymakan/adapter.d.ts.map +1 -1
  16. package/dist/carriers/aymakan/index.js +138 -61
  17. package/dist/carriers/aymakan/index.js.map +4 -4
  18. package/dist/carriers/aymakan/mappers.d.ts +5 -0
  19. package/dist/carriers/aymakan/mappers.d.ts.map +1 -1
  20. package/dist/carriers/aymakan/types.d.ts +4 -6
  21. package/dist/carriers/aymakan/types.d.ts.map +1 -1
  22. package/dist/carriers/base.d.ts +2 -2
  23. package/dist/carriers/base.d.ts.map +1 -1
  24. package/dist/carriers/smsaexpress/adapter.d.ts +1 -1
  25. package/dist/carriers/smsaexpress/adapter.d.ts.map +1 -1
  26. package/dist/carriers/smsaexpress/index.js +32 -22
  27. package/dist/carriers/smsaexpress/index.js.map +4 -4
  28. package/dist/carriers/smsaexpress/mappers.d.ts.map +1 -1
  29. package/dist/core/http.d.ts.map +1 -1
  30. package/dist/core/schemas.d.ts +3 -3
  31. package/dist/core/types.d.ts +1 -1
  32. package/dist/core/types.d.ts.map +1 -1
  33. package/dist/{index-x8sk1kw9.js → index-qjtxhwzv.js} +5 -3
  34. package/dist/{index-x8sk1kw9.js.map → index-qjtxhwzv.js.map} +5 -5
  35. package/dist/index.js +1 -1
  36. package/package.json +8 -3
@@ -0,0 +1,694 @@
1
+ import {
2
+ APIError,
3
+ BaseCarrierAdapter,
4
+ HttpClient,
5
+ UnsupportedOperationError,
6
+ ValidationError,
7
+ validateCreateShipmentInput,
8
+ validatePickupRequest
9
+ } from "../../index-qjtxhwzv.js";
10
+
11
+ // src/carriers/aramex/services.ts
12
+ var AramexProductGroup = {
13
+ EXPRESS: "EXP",
14
+ DOMESTIC: "DOM"
15
+ };
16
+ var AramexProductType = {
17
+ DOMESTIC: "OND",
18
+ PRIORITY_DOCUMENT_EXPRESS: "PDX",
19
+ PRIORITY_PARCEL_EXPRESS: "PPX",
20
+ PRIORITY_LETTER_EXPRESS: "PLX",
21
+ DEFERRED_DOCUMENT_EXPRESS: "DDX",
22
+ DEFERRED_PARCEL_EXPRESS: "DPX",
23
+ GROUND_DOCUMENT_EXPRESS: "GDX",
24
+ GROUND_PARCEL_EXPRESS: "GPX",
25
+ ECONOMY_PARCEL_EXPRESS: "EPX",
26
+ RETURN: "RTN"
27
+ };
28
+ var AramexPaymentType = {
29
+ PREPAID: "P",
30
+ COLLECT: "C",
31
+ THIRD_PARTY: "3"
32
+ };
33
+ var AramexService = {
34
+ COD: "CODS",
35
+ INSURANCE: "INSR"
36
+ };
37
+ var AramexStatusCodes = {
38
+ SH001: "created",
39
+ SH002: "picked_up",
40
+ SH004: "at_warehouse",
41
+ SH005: "out_for_delivery",
42
+ SH014: "delivered",
43
+ SH060: "in_transit",
44
+ SH074: "exception",
45
+ SH159: "exception",
46
+ SH212: "returned",
47
+ SH235: "delivered"
48
+ };
49
+
50
+ // src/carriers/aramex/mappers.ts
51
+ function buildClientInfo(credentials, opts) {
52
+ return {
53
+ UserName: credentials.userName,
54
+ Password: credentials.password,
55
+ Version: opts?.version ?? "v1.0",
56
+ AccountNumber: credentials.accountNumber,
57
+ AccountPin: credentials.accountPin,
58
+ AccountEntity: credentials.accountEntity,
59
+ AccountCountryCode: credentials.accountCountryCode,
60
+ Source: opts?.source ?? 24
61
+ };
62
+ }
63
+ function mapAramexStatus(updateCode) {
64
+ const mapped = AramexStatusCodes[updateCode];
65
+ return mapped;
66
+ }
67
+ function statusFromDescription(description) {
68
+ if (!description)
69
+ return;
70
+ const d = description.toLowerCase();
71
+ if (/out for delivery|on delivery|with delivery courier/.test(d))
72
+ return "out_for_delivery";
73
+ if (/fail|unable|undeliver|exception|held|on hold|problem|refused|damaged/.test(d))
74
+ return "exception";
75
+ if (/deliver/.test(d))
76
+ return "delivered";
77
+ if (/return/.test(d))
78
+ return "returned";
79
+ if (/cancel/.test(d))
80
+ return "cancelled";
81
+ if (/picked up|collected|pickup/.test(d))
82
+ return "picked_up";
83
+ if (/transit|departed|forwarded|en route/.test(d))
84
+ return "in_transit";
85
+ if (/received at|arrived|facility|warehouse|sorting|hub/.test(d))
86
+ return "at_warehouse";
87
+ if (/created|information received|booked/.test(d))
88
+ return "created";
89
+ return;
90
+ }
91
+ function parseAramexDate(value) {
92
+ if (!value)
93
+ return new Date(Number.NaN);
94
+ const wcf = /\/Date\((-?\d+)(?:[+-]\d{4})?\)\//.exec(value);
95
+ if (wcf?.[1])
96
+ return new Date(Number.parseInt(wcf[1], 10));
97
+ return new Date(value);
98
+ }
99
+ function toWcfDate(date) {
100
+ return `/Date(${date.getTime()})/`;
101
+ }
102
+ function round(value) {
103
+ return Math.round(value * 1000) / 1000;
104
+ }
105
+ var VALID_PRODUCT_TYPES = new Set(Object.values(AramexProductType));
106
+ function getMeta(input, key) {
107
+ const value = input.options?.metadata?.[key];
108
+ return typeof value === "string" ? value : undefined;
109
+ }
110
+ function resolveProductGroupAndType(input) {
111
+ const sameCountry = input.shipper.countryCode.trim().toUpperCase() === input.consignee.countryCode.trim().toUpperCase();
112
+ const metaGroup = getMeta(input, "productGroup")?.toUpperCase();
113
+ const productGroup = metaGroup === "EXP" || metaGroup === "DOM" ? metaGroup : sameCountry ? "DOM" : "EXP";
114
+ const candidate = getMeta(input, "productType") ?? input.serviceType;
115
+ const productType = candidate && VALID_PRODUCT_TYPES.has(candidate) ? candidate : productGroup === "DOM" ? AramexProductType.DOMESTIC : AramexProductType.ECONOMY_PARCEL_EXPRESS;
116
+ return { productGroup, productType };
117
+ }
118
+ function resolvePaymentType(input) {
119
+ const meta = getMeta(input, "paymentType");
120
+ if (meta === "P" || meta === "C" || meta === "3")
121
+ return meta;
122
+ return "P";
123
+ }
124
+ function aggregateWeight(input) {
125
+ const allLb = input.parcels.every((p) => p.weight.unit === "lb");
126
+ if (allLb) {
127
+ const value2 = input.parcels.reduce((s, p) => s + p.weight.value, 0);
128
+ return { Value: round(value2), Unit: "Lb" };
129
+ }
130
+ const value = input.parcels.reduce((s, p) => s + (p.weight.unit === "lb" ? p.weight.value * 0.453592 : p.weight.value), 0);
131
+ return { Value: round(value), Unit: "Kg" };
132
+ }
133
+ function mapDimensions(dims) {
134
+ if (!dims)
135
+ return null;
136
+ const factor = dims.unit === "in" ? 2.54 : 1;
137
+ return {
138
+ Length: round(dims.length * factor),
139
+ Width: round(dims.width * factor),
140
+ Height: round(dims.height * factor),
141
+ Unit: "CM"
142
+ };
143
+ }
144
+ function buildPartyAddress(fields) {
145
+ return {
146
+ Line1: fields.line1,
147
+ Line2: fields.line2 ?? "",
148
+ Line3: fields.line3 ?? "",
149
+ City: fields.city,
150
+ StateOrProvinceCode: fields.state,
151
+ PostCode: fields.postCode ?? "",
152
+ CountryCode: fields.countryCode,
153
+ Longitude: fields.coordinates?.longitude,
154
+ Latitude: fields.coordinates?.latitude
155
+ };
156
+ }
157
+ function mapAddress(addr) {
158
+ return buildPartyAddress({
159
+ line1: addr.line1,
160
+ line2: addr.line2,
161
+ line3: addr.neighbourhood,
162
+ city: addr.city,
163
+ state: addr.state,
164
+ postCode: addr.postalCode,
165
+ countryCode: addr.countryCode,
166
+ coordinates: addr.coordinates
167
+ });
168
+ }
169
+ function buildContact(opts) {
170
+ return {
171
+ Department: "",
172
+ PersonName: opts.personName,
173
+ Title: "",
174
+ CompanyName: opts.companyName,
175
+ PhoneNumber1: opts.phone,
176
+ PhoneNumber1Ext: "",
177
+ PhoneNumber2: "",
178
+ CellPhone: opts.phone,
179
+ EmailAddress: opts.email ?? "",
180
+ Type: ""
181
+ };
182
+ }
183
+ function mapContact(addr, fallbackCompany) {
184
+ return buildContact({
185
+ personName: addr.name,
186
+ companyName: addr.company ?? fallbackCompany ?? addr.name,
187
+ phone: addr.phone,
188
+ email: addr.email
189
+ });
190
+ }
191
+ function mapParty(addr, accountNumber, fallbackCompany) {
192
+ return {
193
+ AccountNumber: accountNumber,
194
+ PartyAddress: mapAddress(addr),
195
+ Contact: mapContact(addr, fallbackCompany)
196
+ };
197
+ }
198
+ function mapShipmentDetails(input, productGroup, productType, paymentType) {
199
+ const numberOfPieces = input.parcels.reduce((s, p) => s + p.pieces, 0) || 1;
200
+ const description = input.parcels.map((p) => p.description).filter(Boolean).join(", ") || "Goods";
201
+ const services = [];
202
+ let cashOnDelivery = null;
203
+ if (input.cod?.enabled) {
204
+ cashOnDelivery = {
205
+ Value: input.cod.amount,
206
+ CurrencyCode: input.cod.currency
207
+ };
208
+ services.push(AramexService.COD);
209
+ }
210
+ const customsValue = input.declaredValue ? {
211
+ Value: input.declaredValue.amount,
212
+ CurrencyCode: input.declaredValue.currency
213
+ } : null;
214
+ const insuranceAmount = input.options?.isInsured && customsValue ? customsValue : null;
215
+ if (insuranceAmount)
216
+ services.push(AramexService.INSURANCE);
217
+ return {
218
+ Dimensions: mapDimensions(input.parcels[0]?.dimensions),
219
+ ActualWeight: aggregateWeight(input),
220
+ ChargeableWeight: null,
221
+ DescriptionOfGoods: description,
222
+ GoodsOriginCountry: input.shipper.countryCode,
223
+ NumberOfPieces: numberOfPieces,
224
+ ProductGroup: productGroup,
225
+ ProductType: productType,
226
+ PaymentType: paymentType,
227
+ PaymentOptions: "",
228
+ CustomsValueAmount: customsValue,
229
+ CashOnDeliveryAmount: cashOnDelivery,
230
+ InsuranceAmount: insuranceAmount,
231
+ Services: services.join(",") || undefined
232
+ };
233
+ }
234
+ function mapCreateShipmentRequest(input, ctx) {
235
+ const { productGroup, productType } = resolveProductGroupAndType(input);
236
+ const paymentType = resolvePaymentType(input);
237
+ const now = new Date;
238
+ const due = new Date(now.getTime() + 24 * 60 * 60 * 1000);
239
+ return {
240
+ Reference1: input.reference,
241
+ Reference2: input.options?.customerTracking,
242
+ Shipper: mapParty(input.shipper, ctx.accountNumber, ctx.companyName),
243
+ Consignee: mapParty(input.consignee),
244
+ ThirdParty: null,
245
+ ShippingDateTime: toWcfDate(now),
246
+ DueDate: toWcfDate(due),
247
+ Comments: getMeta(input, "comments"),
248
+ PickupLocation: getMeta(input, "pickupLocation"),
249
+ Details: mapShipmentDetails(input, productGroup, productType, paymentType),
250
+ Attachments: null,
251
+ ForeignHAWB: null,
252
+ TransportType: 0,
253
+ Number: null
254
+ };
255
+ }
256
+ function mapCalculateRateRequest(input, preferredCurrency) {
257
+ const { productGroup, productType } = resolveProductGroupAndType(input);
258
+ const paymentType = resolvePaymentType(input);
259
+ return {
260
+ OriginAddress: mapAddress(input.shipper),
261
+ DestinationAddress: mapAddress(input.consignee),
262
+ ShipmentDetails: mapShipmentDetails(input, productGroup, productType, paymentType),
263
+ PreferredCurrencyCode: preferredCurrency ?? input.cod?.currency ?? input.declaredValue?.currency
264
+ };
265
+ }
266
+ function mapPickupRequest(input, ctx) {
267
+ const pickupDate = new Date(`${input.date}T00:00:00`);
268
+ const ready = new Date(`${input.date}T09:00:00`);
269
+ const closing = new Date(`${input.date}T17:00:00`);
270
+ return {
271
+ Reference1: input.trackingNumbers?.[0],
272
+ PickupAddress: buildPartyAddress({
273
+ line1: input.address,
274
+ city: input.city,
275
+ countryCode: ctx.countryCode
276
+ }),
277
+ PickupContact: buildContact({
278
+ personName: input.contactName,
279
+ companyName: ctx.companyName ?? input.contactName,
280
+ phone: input.contactPhone
281
+ }),
282
+ PickupLocation: "Reception",
283
+ PickupDate: toWcfDate(pickupDate),
284
+ ReadyTime: toWcfDate(ready),
285
+ LastPickupTime: toWcfDate(closing),
286
+ ClosingTime: toWcfDate(closing),
287
+ Comments: input.timeSlot,
288
+ Status: "Ready",
289
+ PickupItems: {
290
+ PickupItemDetail: [
291
+ {
292
+ ProductGroup: "DOM",
293
+ ProductType: AramexProductType.DOMESTIC,
294
+ Payment: "P",
295
+ NumberOfShipments: input.shipmentCount,
296
+ NumberOfPieces: input.shipmentCount,
297
+ ShipmentWeight: { Value: 1, Unit: "Kg" }
298
+ }
299
+ ]
300
+ }
301
+ };
302
+ }
303
+ function mapShipmentResponse(processed, input) {
304
+ const trackingNumber = processed.ID || processed.ShipmentNumber || "";
305
+ const labelUrl = processed.ShipmentLabel?.LabelURL || undefined;
306
+ return {
307
+ carrier: "aramex",
308
+ trackingNumber,
309
+ customerTracking: input?.options?.customerTracking,
310
+ reference: input?.reference,
311
+ status: "created",
312
+ statusLabel: "Created",
313
+ labelUrl,
314
+ pdfLabelUrl: labelUrl,
315
+ codAmount: input?.cod?.enabled ? input.cod.amount : undefined,
316
+ declaredValue: input?.declaredValue?.amount,
317
+ currency: input?.cod?.currency ?? input?.declaredValue?.currency ?? "SAR",
318
+ createdAt: new Date,
319
+ raw: processed
320
+ };
321
+ }
322
+ function mapTrackingEvent(result) {
323
+ return {
324
+ timestamp: parseAramexDate(result.UpdateDateTime),
325
+ statusCode: result.UpdateCode,
326
+ status: mapAramexStatus(result.UpdateCode) ?? statusFromDescription(result.UpdateDescription) ?? "unknown",
327
+ description: result.UpdateDescription,
328
+ location: result.UpdateLocation || undefined
329
+ };
330
+ }
331
+ function mapTrackingResult(waybill, results) {
332
+ const events = results.map(mapTrackingEvent).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
333
+ const latest = events[0];
334
+ const delivered = events.find((e) => e.status === "delivered");
335
+ const weightStr = [...results].reverse().find((r) => r.GrossWeight)?.GrossWeight;
336
+ const weight = weightStr ? Number.parseFloat(weightStr) : undefined;
337
+ return {
338
+ trackingNumber: waybill,
339
+ carrier: "aramex",
340
+ status: latest?.status ?? "unknown",
341
+ statusLabel: latest?.description ?? "Unknown",
342
+ events,
343
+ deliveryDate: delivered?.timestamp,
344
+ weight: weight != null && !Number.isNaN(weight) ? weight : undefined,
345
+ raw: results
346
+ };
347
+ }
348
+ function normalizeTrackingResults(raw) {
349
+ if (!raw)
350
+ return {};
351
+ if (Array.isArray(raw)) {
352
+ const map = {};
353
+ for (const kv of raw) {
354
+ if (kv && typeof kv === "object" && "Key" in kv) {
355
+ map[kv.Key] = kv.Value ?? [];
356
+ }
357
+ }
358
+ return map;
359
+ }
360
+ return raw;
361
+ }
362
+ function mapNonExistingWaybill(waybill) {
363
+ return {
364
+ trackingNumber: waybill,
365
+ carrier: "aramex",
366
+ status: "unknown",
367
+ statusLabel: "Waybill not found",
368
+ events: [],
369
+ raw: { nonExisting: true, waybill }
370
+ };
371
+ }
372
+ function mapRate(response, input) {
373
+ const { productType } = resolveProductGroupAndType(input);
374
+ return {
375
+ carrier: "aramex",
376
+ serviceType: productType,
377
+ serviceName: productType,
378
+ amount: response.TotalAmount?.Value ?? 0,
379
+ currency: response.TotalAmount?.CurrencyCode ?? input.cod?.currency ?? input.declaredValue?.currency ?? "SAR",
380
+ raw: response
381
+ };
382
+ }
383
+ function mapPickupResponse(processed, input) {
384
+ return {
385
+ id: processed.GUID || processed.ID,
386
+ carrier: "aramex",
387
+ status: "pending",
388
+ date: input.date,
389
+ timeSlot: input.timeSlot,
390
+ city: input.city,
391
+ contactName: input.contactName,
392
+ contactPhone: input.contactPhone,
393
+ address: input.address,
394
+ shipmentCount: input.shipmentCount,
395
+ createdAt: new Date,
396
+ raw: processed
397
+ };
398
+ }
399
+ function mapCity(name) {
400
+ return { nameEn: name };
401
+ }
402
+ function mapOffice(office) {
403
+ const addr = office.Address;
404
+ const lines = [addr?.Line1, addr?.Line2, addr?.Line3].filter(Boolean);
405
+ return {
406
+ id: office.EntityCode,
407
+ name: office.EntityName ?? office.EntityCode,
408
+ address: lines.length ? lines.join(", ") : undefined,
409
+ city: addr?.City,
410
+ latitude: addr?.Latitude,
411
+ longitude: addr?.Longitude
412
+ };
413
+ }
414
+
415
+ // src/carriers/aramex/adapter.ts
416
+ var SHIPPING_SANDBOX_URL = "https://ws.dev.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc";
417
+ var SHIPPING_PRODUCTION_URL = "https://ws.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc";
418
+ var DEFAULT_LABEL_INFO = {
419
+ ReportID: 9201,
420
+ ReportType: "URL"
421
+ };
422
+ var EMPTY_TRANSACTION = {
423
+ Reference1: "",
424
+ Reference2: "",
425
+ Reference3: "",
426
+ Reference4: "",
427
+ Reference5: ""
428
+ };
429
+
430
+ class AramexAdapter extends BaseCarrierAdapter {
431
+ name = "aramex";
432
+ supportedCountries = [
433
+ "SA",
434
+ "AE",
435
+ "BH",
436
+ "KW",
437
+ "OM",
438
+ "QA",
439
+ "JO",
440
+ "EG",
441
+ "LB",
442
+ "IQ"
443
+ ];
444
+ shippingHttp;
445
+ trackingHttp;
446
+ rateHttp;
447
+ locationHttp;
448
+ constructor(config) {
449
+ super(config);
450
+ const shippingBase = this.getBaseUrl();
451
+ const common = {
452
+ carrier: "aramex"
453
+ };
454
+ this.shippingHttp = new HttpClient({ ...common, baseUrl: shippingBase });
455
+ this.trackingHttp = new HttpClient({
456
+ ...common,
457
+ baseUrl: this.serviceBase(shippingBase, "Tracking")
458
+ });
459
+ this.rateHttp = new HttpClient({
460
+ ...common,
461
+ baseUrl: this.serviceBase(shippingBase, "RateCalculator")
462
+ });
463
+ this.locationHttp = new HttpClient({
464
+ ...common,
465
+ baseUrl: config.locationBaseUrl ?? this.serviceBase(shippingBase, "Location")
466
+ });
467
+ }
468
+ getBaseUrl() {
469
+ return this.config.mode === "production" ? SHIPPING_PRODUCTION_URL : SHIPPING_SANDBOX_URL;
470
+ }
471
+ serviceBase(shippingBase, service) {
472
+ return shippingBase.replace("/Shipping/", `/${service}/`);
473
+ }
474
+ get aramexConfig() {
475
+ return this.config;
476
+ }
477
+ buildClientInfo() {
478
+ const cfg = this.aramexConfig;
479
+ return buildClientInfo(cfg.credentials, {
480
+ source: cfg.source,
481
+ version: cfg.version
482
+ });
483
+ }
484
+ static aramexErrorExtractor(json) {
485
+ const obj = json;
486
+ const notifications = obj?.Notifications ?? [];
487
+ const hasError = obj?.HasErrors === true;
488
+ const message = notifications.map((n) => n?.Message).filter(Boolean).join("; ") || (hasError ? "Aramex returned an error" : undefined);
489
+ return {
490
+ hasError,
491
+ message,
492
+ errors: notifications.length ? AramexAdapter.notificationsToErrors(notifications) : undefined
493
+ };
494
+ }
495
+ static notificationsToErrors(notifications) {
496
+ return {
497
+ _aramex: notifications.map((n) => `${n?.Code ?? ""}: ${n?.Message ?? ""}`.trim())
498
+ };
499
+ }
500
+ async executeCreateShipment(input) {
501
+ const cfg = this.aramexConfig;
502
+ const shipment = mapCreateShipmentRequest(input, {
503
+ accountNumber: cfg.credentials.accountNumber,
504
+ companyName: cfg.companyName
505
+ });
506
+ const response = await this.shippingHttp.post("/json/CreateShipments", {
507
+ ClientInfo: this.buildClientInfo(),
508
+ Transaction: EMPTY_TRANSACTION,
509
+ LabelInfo: DEFAULT_LABEL_INFO,
510
+ Shipments: [shipment]
511
+ }, { errorExtractor: AramexAdapter.aramexErrorExtractor });
512
+ const processed = response.Shipments?.[0];
513
+ if (!processed) {
514
+ throw new APIError("Aramex returned no shipment", {
515
+ carrier: "aramex",
516
+ raw: response
517
+ });
518
+ }
519
+ if (processed.HasErrors) {
520
+ const notifications = processed.Notifications ?? [];
521
+ throw new APIError(notifications.map((n) => n.Message).filter(Boolean).join("; ") || "Failed to create shipment", {
522
+ carrier: "aramex",
523
+ errors: AramexAdapter.notificationsToErrors(notifications),
524
+ raw: processed
525
+ });
526
+ }
527
+ return mapShipmentResponse(processed, input);
528
+ }
529
+ async createBulkShipments(inputs) {
530
+ if (inputs.length === 0) {
531
+ throw new ValidationError("At least one shipment is required for bulk create", { raw: { count: 0 } });
532
+ }
533
+ for (const input of inputs)
534
+ validateCreateShipmentInput(input);
535
+ const cfg = this.aramexConfig;
536
+ const shipments = inputs.map((input) => mapCreateShipmentRequest(input, {
537
+ accountNumber: cfg.credentials.accountNumber,
538
+ companyName: cfg.companyName
539
+ }));
540
+ const response = await this.shippingHttp.post("/json/CreateShipments", {
541
+ ClientInfo: this.buildClientInfo(),
542
+ Transaction: EMPTY_TRANSACTION,
543
+ LabelInfo: DEFAULT_LABEL_INFO,
544
+ Shipments: shipments
545
+ }, { errorExtractor: AramexAdapter.aramexErrorExtractor });
546
+ const processed = response.Shipments ?? [];
547
+ if (processed.length === 0) {
548
+ throw new APIError("Aramex returned no shipments", {
549
+ carrier: "aramex",
550
+ raw: response
551
+ });
552
+ }
553
+ const failed = processed.filter((p) => p.HasErrors);
554
+ if (failed.length > 0) {
555
+ const notifications = failed.flatMap((p) => p.Notifications ?? []);
556
+ throw new APIError(notifications.map((n) => n.Message).filter(Boolean).join("; ") || `Failed to create ${failed.length} of ${processed.length} shipments`, {
557
+ carrier: "aramex",
558
+ errors: AramexAdapter.notificationsToErrors(notifications),
559
+ raw: response
560
+ });
561
+ }
562
+ return processed.map((p, i) => mapShipmentResponse(p, inputs[i]));
563
+ }
564
+ cancelShipment(_trackingNumber) {
565
+ throw new UnsupportedOperationError("aramex", "cancelShipment");
566
+ }
567
+ async getLabel(trackingNumber, _format) {
568
+ const response = await this.shippingHttp.post("/json/PrintLabel", {
569
+ ClientInfo: this.buildClientInfo(),
570
+ Transaction: EMPTY_TRANSACTION,
571
+ ShipmentNumber: trackingNumber,
572
+ LabelInfo: DEFAULT_LABEL_INFO
573
+ }, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
574
+ const url = response.ShipmentLabel?.LabelURL;
575
+ if (!url) {
576
+ throw new APIError("Failed to get label", {
577
+ carrier: "aramex",
578
+ raw: response
579
+ });
580
+ }
581
+ return url;
582
+ }
583
+ async track(trackingNumber) {
584
+ const results = await this.trackMultiple([trackingNumber]);
585
+ const result = results[0];
586
+ if (!result || result.status === "unknown" && result.events.length === 0) {
587
+ throw new APIError("Shipment not found", {
588
+ carrier: "aramex",
589
+ raw: result?.raw ?? { trackingNumber }
590
+ });
591
+ }
592
+ return result;
593
+ }
594
+ async trackMultiple(trackingNumbers) {
595
+ const response = await this.trackingHttp.post("/json/TrackShipments", {
596
+ ClientInfo: this.buildClientInfo(),
597
+ Transaction: EMPTY_TRANSACTION,
598
+ Shipments: trackingNumbers,
599
+ GetLastTrackingUpdateOnly: false
600
+ }, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
601
+ const map = normalizeTrackingResults(response.TrackingResults);
602
+ const found = Object.entries(map).map(([waybill, results]) => mapTrackingResult(waybill, results));
603
+ const nonExisting = (response.NonExistingWaybills ?? []).map(mapNonExistingWaybill);
604
+ return [...found, ...nonExisting];
605
+ }
606
+ async trackByReference(reference) {
607
+ const result = await this.track(reference);
608
+ return { ...result, reference };
609
+ }
610
+ async getRates(input) {
611
+ const rateRequest = mapCalculateRateRequest(input);
612
+ const response = await this.rateHttp.post("/json/CalculateRate", {
613
+ ClientInfo: this.buildClientInfo(),
614
+ Transaction: EMPTY_TRANSACTION,
615
+ ...rateRequest
616
+ }, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
617
+ if (response.TotalAmount?.Value == null) {
618
+ throw new APIError("Aramex returned no rate for this shipment", {
619
+ carrier: "aramex",
620
+ raw: response
621
+ });
622
+ }
623
+ return [mapRate(response, input)];
624
+ }
625
+ async createPickup(input) {
626
+ validatePickupRequest(input);
627
+ const cfg = this.aramexConfig;
628
+ const pickup = mapPickupRequest(input, {
629
+ accountNumber: cfg.credentials.accountNumber,
630
+ countryCode: cfg.credentials.accountCountryCode,
631
+ companyName: cfg.companyName
632
+ });
633
+ const response = await this.shippingHttp.post("/json/CreatePickup", {
634
+ ClientInfo: this.buildClientInfo(),
635
+ Transaction: EMPTY_TRANSACTION,
636
+ Pickup: pickup
637
+ }, { errorExtractor: AramexAdapter.aramexErrorExtractor });
638
+ const processed = response.ProcessedPickup;
639
+ if (!processed || !processed.GUID && !processed.ID) {
640
+ throw new APIError("Failed to create pickup", {
641
+ carrier: "aramex",
642
+ raw: response
643
+ });
644
+ }
645
+ const failedShipments = (processed.ProcessedShipments ?? []).filter((s) => s.HasErrors);
646
+ if (failedShipments.length > 0) {
647
+ const notifications = failedShipments.flatMap((s) => s.Notifications ?? []);
648
+ throw new APIError(notifications.map((n) => n.Message).filter(Boolean).join("; ") || `Pickup created but ${failedShipments.length} shipment(s) failed`, {
649
+ carrier: "aramex",
650
+ errors: AramexAdapter.notificationsToErrors(notifications),
651
+ raw: response
652
+ });
653
+ }
654
+ return mapPickupResponse(processed, input);
655
+ }
656
+ async cancelPickup(pickupId) {
657
+ const response = await this.shippingHttp.post("/json/CancelPickup", {
658
+ ClientInfo: this.buildClientInfo(),
659
+ Transaction: EMPTY_TRANSACTION,
660
+ PickupGUID: String(pickupId)
661
+ }, { errorExtractor: AramexAdapter.aramexErrorExtractor });
662
+ return response.HasErrors !== true;
663
+ }
664
+ async getCities(countryCode) {
665
+ const response = await this.locationHttp.post("/json/FetchCities", {
666
+ ClientInfo: this.buildClientInfo(),
667
+ Transaction: EMPTY_TRANSACTION,
668
+ CountryCode: countryCode ?? this.aramexConfig.credentials.accountCountryCode
669
+ }, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
670
+ return (response.Cities ?? []).map(mapCity);
671
+ }
672
+ async getDropoffLocations(countryCode) {
673
+ const response = await this.locationHttp.post("/json/FetchOffices", {
674
+ ClientInfo: this.buildClientInfo(),
675
+ Transaction: EMPTY_TRANSACTION,
676
+ CountryCode: countryCode ?? this.aramexConfig.credentials.accountCountryCode
677
+ }, { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor });
678
+ return (response.Offices ?? []).map(mapOffice);
679
+ }
680
+ }
681
+ export {
682
+ statusFromDescription,
683
+ parseAramexDate,
684
+ mapAramexStatus,
685
+ buildClientInfo,
686
+ AramexStatusCodes,
687
+ AramexService,
688
+ AramexProductType,
689
+ AramexProductGroup,
690
+ AramexPaymentType,
691
+ AramexAdapter
692
+ };
693
+
694
+ //# debugId=BC16779A6E5B3D1F64756E2164756E21