shipflow 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -5
- package/dist/carriers/aramex/adapter.d.ts +7 -0
- package/dist/carriers/aramex/adapter.d.ts.map +1 -1
- package/dist/carriers/aramex/index.js +5 -3
- package/dist/carriers/aramex/index.js.map +3 -3
- package/dist/carriers/aymakan/index.js +1 -1
- package/dist/carriers/smsaexpress/index.js +1 -1
- package/dist/core/errors.d.ts +19 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/http.d.ts +24 -3
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/retry.d.ts +42 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/schemas.d.ts +196 -812
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/index-qnxj8bct.js +1067 -0
- package/dist/index-qnxj8bct.js.map +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +2 -2
- package/package.json +4 -4
- package/dist/index-qjtxhwzv.js +0 -4602
- package/dist/index-qjtxhwzv.js.map +0 -22
package/README.md
CHANGED
|
@@ -8,9 +8,10 @@ Think EasyPost / Shippo, but purpose-built for Saudi Arabia and the GCC.
|
|
|
8
8
|
|
|
9
9
|
- **Unified types** — one `CreateShipmentInput`, one `TrackingResult`, one `WebhookEvent`, regardless of carrier
|
|
10
10
|
- **Tree-shakeable** — only the carriers you import are bundled
|
|
11
|
-
- **Auto-validation** —
|
|
11
|
+
- **Auto-validation** — Valibot schemas validate every `createShipment()` call before it hits the network
|
|
12
12
|
- **Webhook parsing** — normalize incoming carrier webhooks into a single event format
|
|
13
|
-
- **
|
|
13
|
+
- **Smart retries** — dependency-free retry with jittered backoff that honors carrier `Retry-After` on 429/503, surfacing a `RateLimitError` when the wait is too long to absorb inline
|
|
14
|
+
- **Minimal dependencies** — only [Valibot](https://github.com/fabian-hiller/valibot) for validation; uses the runtime's global `fetch` (Node 20+, Deno, Bun, edge/workers), no axios/node-fetch
|
|
14
15
|
- **TypeScript-first** — strict types, no `any`
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
@@ -165,7 +166,8 @@ things make it different from the other carriers:
|
|
|
165
166
|
set `locationBaseUrl` on the config.
|
|
166
167
|
- **"Fake 200 OK" errors** — Aramex returns HTTP 200 even on logical failures, with
|
|
167
168
|
`HasErrors: true` + `Notifications[]`. ShipFlow surfaces these as `APIError`, including
|
|
168
|
-
per-shipment errors inside an otherwise-clean `CreateShipments` batch.
|
|
169
|
+
per-shipment errors inside an otherwise-clean `CreateShipments` batch. Throttling
|
|
170
|
+
notifications in that envelope are surfaced as `RateLimitError` so retries back off.
|
|
169
171
|
- **Rates are supported** (`getRates` → `CalculateRate`), unlike Aymakan/SMSA.
|
|
170
172
|
- **`cancelShipment` is unsupported** (the classic API has no shipment-cancel operation) and
|
|
171
173
|
throws `UnsupportedOperationError`. Pickups can be cancelled via `cancelPickup`.
|
|
@@ -308,7 +310,7 @@ interface WebhookEvent {
|
|
|
308
310
|
|
|
309
311
|
## Input Validation
|
|
310
312
|
|
|
311
|
-
All `createShipment()` calls are **automatically validated** using
|
|
313
|
+
All `createShipment()` calls are **automatically validated** using Valibot schemas before hitting the carrier API. Invalid input throws a `ValidationError` with field-level details:
|
|
312
314
|
|
|
313
315
|
```typescript
|
|
314
316
|
try {
|
|
@@ -340,7 +342,7 @@ validateCreateShipmentInput(input); // throws ValidationError or returns validat
|
|
|
340
342
|
validatePickupRequest(pickupInput); // same pattern
|
|
341
343
|
```
|
|
342
344
|
|
|
343
|
-
Exported
|
|
345
|
+
Exported Valibot schemas for advanced use (custom refinements, partial validation, etc.):
|
|
344
346
|
|
|
345
347
|
```typescript
|
|
346
348
|
import {
|
|
@@ -359,6 +361,7 @@ All errors extend `ShipFlowError` for easy catch-all handling:
|
|
|
359
361
|
import {
|
|
360
362
|
ShipFlowError,
|
|
361
363
|
NetworkError,
|
|
364
|
+
RateLimitError,
|
|
362
365
|
APIError,
|
|
363
366
|
ValidationError,
|
|
364
367
|
AuthenticationError,
|
|
@@ -373,6 +376,8 @@ try {
|
|
|
373
376
|
// Bad input — check error.issues
|
|
374
377
|
} else if (error instanceof AuthenticationError) {
|
|
375
378
|
// Invalid API key
|
|
379
|
+
} else if (error instanceof RateLimitError) {
|
|
380
|
+
// Rate limited — check error.retryAfterMs (ms to wait), reschedule if set
|
|
376
381
|
} else if (error instanceof APIError) {
|
|
377
382
|
// Carrier returned an error — check error.statusCode, error.errors
|
|
378
383
|
} else if (error instanceof NetworkError) {
|
|
@@ -381,6 +386,37 @@ try {
|
|
|
381
386
|
}
|
|
382
387
|
```
|
|
383
388
|
|
|
389
|
+
> `RateLimitError extends APIError`, so check it **before** `APIError` in your
|
|
390
|
+
> `if`/`else` chain (a plain `catch (e) { if (e instanceof APIError) }` still
|
|
391
|
+
> catches it).
|
|
392
|
+
|
|
393
|
+
### Retries & rate limiting
|
|
394
|
+
|
|
395
|
+
Safe, idempotent requests (GETs, plus tracking endpoints opted in by the
|
|
396
|
+
adapters) are retried automatically with **jittered exponential backoff**.
|
|
397
|
+
Mutating requests (create/cancel) are **not** retried by default, so a timed-out
|
|
398
|
+
`createShipment` never risks a duplicate on the carrier.
|
|
399
|
+
|
|
400
|
+
When a carrier replies `429`/`503` with a `Retry-After` header, ShipFlow:
|
|
401
|
+
|
|
402
|
+
- **honors it inline** if the wait is within the inline cap (15s by default),
|
|
403
|
+
sleeping out the window (plus a little jitter) before retrying; otherwise
|
|
404
|
+
- **stops and throws `RateLimitError`** carrying `retryAfterMs`, so a durable
|
|
405
|
+
queue/worker can reschedule instead of blocking the request for minutes.
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
try {
|
|
409
|
+
await client.carrier("aymakan").track(trackingNumber);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (error instanceof RateLimitError && error.retryAfterMs != null) {
|
|
412
|
+
await scheduleRetryIn(error.retryAfterMs); // your queue/worker
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Aramex reports throttling inside its "fake 200" envelope rather than via HTTP
|
|
418
|
+
429; ShipFlow detects that and raises the same `RateLimitError`.
|
|
419
|
+
|
|
384
420
|
## Custom Adapters
|
|
385
421
|
|
|
386
422
|
Implement the `CarrierAdapter` interface or extend `BaseCarrierAdapter`:
|
|
@@ -51,6 +51,13 @@ export declare class AramexAdapter extends BaseCarrierAdapter {
|
|
|
51
51
|
* Extracts the Aramex "Fake 200 OK" error (envelope-level `HasErrors` +
|
|
52
52
|
* `Notifications`). Passed to every request so the HttpClient raises APIError.
|
|
53
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Aramex reports throttling inside its "fake 200" envelope (and sometimes on a
|
|
56
|
+
* non-429 status) rather than via HTTP 429, so status-code detection misses it.
|
|
57
|
+
* Match the rate-limit wording in the notifications and flag it so the
|
|
58
|
+
* HttpClient raises a retryable `RateLimitError` instead of a plain APIError.
|
|
59
|
+
*/
|
|
60
|
+
private static readonly RATE_LIMIT_PATTERN;
|
|
54
61
|
private static aramexErrorExtractor;
|
|
55
62
|
private static notificationsToErrors;
|
|
56
63
|
protected executeCreateShipment(input: CreateShipmentInput): Promise<Shipment>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/carriers/aramex/adapter.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAYH,OAAO,KAAK,EACV,aAAa,EACb,IAAI,EACJ,mBAAmB,EACnB,QAAQ,EACR,MAAM,EACN,aAAa,EACb,IAAI,EACJ,QAAQ,EACR,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAsD7C,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,WAAW,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,aAAc,SAAQ,kBAAkB;IACnD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,kBAAkB,WAWzB;IAEF,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,YAAY,CAAa;gBAErB,MAAM,EAAE,YAAY;IAuBhC,SAAS,CAAC,UAAU,IAAI,MAAM;IAM9B,8EAA8E;IAC9E,OAAO,CAAC,WAAW;IAInB,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,CAAC,eAAe;IAQvB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/carriers/aramex/adapter.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAYH,OAAO,KAAK,EACV,aAAa,EACb,IAAI,EACJ,mBAAmB,EACnB,QAAQ,EACR,MAAM,EACN,aAAa,EACb,IAAI,EACJ,QAAQ,EACR,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAsD7C,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,WAAW,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,aAAc,SAAQ,kBAAkB;IACnD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,kBAAkB,WAWzB;IAEF,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,YAAY,CAAa;gBAErB,MAAM,EAAE,YAAY;IAuBhC,SAAS,CAAC,UAAU,IAAI,MAAM;IAM9B,8EAA8E;IAC9E,OAAO,CAAC,WAAW;IAInB,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,CAAC,eAAe;IAQvB;;;OAGG;IACH;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CACe;IAEzD,OAAO,CAAC,MAAM,CAAC,oBAAoB;IA4BnC,OAAO,CAAC,MAAM,CAAC,qBAAqB;cAcpB,qBAAqB,CACnC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,QAAQ,CAAC;IA6CpB;;;;;;;;OAQG;IACG,mBAAmB,CACvB,MAAM,EAAE,mBAAmB,EAAE,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC;IAwDtB;;;OAGG;IACH,cAAc,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQnD,QAAQ,CACZ,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAC9B,OAAO,CAAC,MAAM,CAAC;IA8BZ,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAetD,aAAa,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IA4BnE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAU5D,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IA4BrD,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAyDnD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBzD,SAAS,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAehD,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;CAcrE"}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
ValidationError,
|
|
7
7
|
validateCreateShipmentInput,
|
|
8
8
|
validatePickupRequest
|
|
9
|
-
} from "../../index-
|
|
9
|
+
} from "../../index-qnxj8bct.js";
|
|
10
10
|
|
|
11
11
|
// src/carriers/aramex/services.ts
|
|
12
12
|
var AramexProductGroup = {
|
|
@@ -481,6 +481,7 @@ class AramexAdapter extends BaseCarrierAdapter {
|
|
|
481
481
|
version: cfg.version
|
|
482
482
|
});
|
|
483
483
|
}
|
|
484
|
+
static RATE_LIMIT_PATTERN = /rate.?limit|too many request|throttl|quota exceeded/i;
|
|
484
485
|
static aramexErrorExtractor(json) {
|
|
485
486
|
const obj = json;
|
|
486
487
|
const notifications = obj?.Notifications ?? [];
|
|
@@ -489,7 +490,8 @@ class AramexAdapter extends BaseCarrierAdapter {
|
|
|
489
490
|
return {
|
|
490
491
|
hasError,
|
|
491
492
|
message,
|
|
492
|
-
errors: notifications.length ? AramexAdapter.notificationsToErrors(notifications) : undefined
|
|
493
|
+
errors: notifications.length ? AramexAdapter.notificationsToErrors(notifications) : undefined,
|
|
494
|
+
rateLimited: hasError && AramexAdapter.RATE_LIMIT_PATTERN.test(message ?? "")
|
|
493
495
|
};
|
|
494
496
|
}
|
|
495
497
|
static notificationsToErrors(notifications) {
|
|
@@ -691,4 +693,4 @@ export {
|
|
|
691
693
|
AramexAdapter
|
|
692
694
|
};
|
|
693
695
|
|
|
694
|
-
//# debugId=
|
|
696
|
+
//# debugId=A6EBA2B597BA645F64756E2164756E21
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"// file: src/carriers/aramex/services.ts\n/**\n * Aramex Service / Product Codes\n * Use these constants for type-safe product and payment selection.\n *\n * Reference: Aramex Shipping Services API manual, Appendix A (Product Types)\n * and Appendix B (Payment Types).\n */\n\n/** Shipment product group: domestic vs international/express. */\nexport const AramexProductGroup = {\n EXPRESS: \"EXP\",\n DOMESTIC: \"DOM\",\n} as const;\n\n/** Product type codes (Appendix A). `OND` is the only domestic type. */\nexport const AramexProductType = {\n /** Domestic (DOM group) */\n DOMESTIC: \"OND\",\n /** Priority Document Express */\n PRIORITY_DOCUMENT_EXPRESS: \"PDX\",\n /** Priority Parcel Express */\n PRIORITY_PARCEL_EXPRESS: \"PPX\",\n /** Priority Letter Express */\n PRIORITY_LETTER_EXPRESS: \"PLX\",\n /** Deferred Document Express */\n DEFERRED_DOCUMENT_EXPRESS: \"DDX\",\n /** Deferred Parcel Express */\n DEFERRED_PARCEL_EXPRESS: \"DPX\",\n /** Ground Document Express */\n GROUND_DOCUMENT_EXPRESS: \"GDX\",\n /** Ground Parcel Express */\n GROUND_PARCEL_EXPRESS: \"GPX\",\n /** Economy Parcel Express */\n ECONOMY_PARCEL_EXPRESS: \"EPX\",\n /** Return */\n RETURN: \"RTN\",\n} as const;\n\n/** Payment type codes (Appendix B). */\nexport const AramexPaymentType = {\n PREPAID: \"P\",\n COLLECT: \"C\",\n THIRD_PARTY: \"3\",\n} as const;\n\n/**\n * Special service codes joined (comma-separated) into `Details.Services`.\n * e.g. `\"CODS\"` enables Cash-On-Delivery handling.\n */\nexport const AramexService = {\n COD: \"CODS\",\n INSURANCE: \"INSR\",\n} as const;\n\nexport type AramexProductGroupType =\n (typeof AramexProductGroup)[keyof typeof AramexProductGroup];\nexport type AramexProductTypeCode =\n (typeof AramexProductType)[keyof typeof AramexProductType];\nexport type AramexPaymentTypeCode =\n (typeof AramexPaymentType)[keyof typeof AramexPaymentType];\n\n/**\n * Aramex Tracking Status Codes (`UpdateCode`) → unified ShipmentStatus.\n *\n * IMPORTANT: Aramex does not publish a stable, comprehensive `UpdateCode` list,\n * and the codes vary by region/account. This table is a best-effort starter —\n * entries marked `VERIFY` should be confirmed against live tracking data. The\n * mapper falls back to a description-keyword heuristic and then `\"unknown\"`, so\n * an unmapped code never breaks tracking.\n */\nexport const AramexStatusCodes = {\n \"SH001\": \"created\", // Shipment information received (VERIFY)\n \"SH002\": \"picked_up\", // Picked up from shipper (VERIFY)\n \"SH004\": \"at_warehouse\", // Received at origin facility (VERIFY)\n \"SH005\": \"out_for_delivery\", // Out on delivery courier\n \"SH014\": \"delivered\", // Shipment delivered (VERIFY)\n \"SH060\": \"in_transit\", // Departed / in transit (VERIFY)\n \"SH074\": \"exception\", // Delivery attempt failed (VERIFY)\n \"SH159\": \"exception\", // Held / customs / address problem (VERIFY)\n \"SH212\": \"returned\", // Returned to shipper (VERIFY)\n \"SH235\": \"delivered\", // Delivered (VERIFY)\n} as const;\n",
|
|
6
6
|
"// file: src/carriers/aramex/mappers.ts\n/**\n * Aramex Data Mappers\n * Transform between unified ShipFlow types and the Aramex JSON API formats.\n */\n\nimport type {\n Address,\n City,\n CreateShipmentInput,\n Dimensions,\n Location,\n Pickup,\n PickupRequest,\n Rate,\n Shipment,\n ShipmentStatus,\n TrackingEvent,\n TrackingResult,\n} from \"../../core/types\";\nimport {\n AramexProductType,\n AramexService,\n AramexStatusCodes,\n} from \"./services\";\nimport type {\n AramexCalculateRateResponse,\n AramexClientInfo,\n AramexContact,\n AramexDimensions,\n AramexMoney,\n AramexOffice,\n AramexParty,\n AramexPartyAddress,\n AramexPickupObject,\n AramexProcessedPickup,\n AramexProcessedShipment,\n AramexShipment,\n AramexShipmentDetails,\n AramexTrackingResult,\n AramexTrackShipmentsResponse,\n AramexWeight,\n} from \"./types\";\n\n// ============================================================================\n// CREDENTIALS / CLIENT INFO\n// ============================================================================\n\nexport interface AramexCredentials {\n userName: string;\n password: string;\n accountNumber: string;\n accountPin: string;\n accountEntity: string;\n accountCountryCode: string;\n}\n\n/** Build the ClientInfo block injected into every Aramex request body. */\nexport function buildClientInfo(\n credentials: AramexCredentials,\n opts?: { source?: number; version?: string },\n): AramexClientInfo {\n return {\n UserName: credentials.userName,\n Password: credentials.password,\n Version: opts?.version ?? \"v1.0\",\n AccountNumber: credentials.accountNumber,\n AccountPin: credentials.accountPin,\n AccountEntity: credentials.accountEntity,\n AccountCountryCode: credentials.accountCountryCode,\n Source: opts?.source ?? 24,\n };\n}\n\n// ============================================================================\n// STATUS MAPPING\n// ============================================================================\n\nexport function mapAramexStatus(\n updateCode: string,\n): ShipmentStatus | undefined {\n const mapped = AramexStatusCodes[updateCode as keyof typeof AramexStatusCodes];\n return mapped as ShipmentStatus | undefined;\n}\n\n/**\n * Secondary heuristic: derive a status from the human-readable update text when\n * the `UpdateCode` isn't in our table (Aramex codes vary by region). Ordered so\n * the more specific phrases win (e.g. \"out for delivery\" before \"delivered\").\n */\nexport function statusFromDescription(\n description: string | undefined,\n): ShipmentStatus | undefined {\n if (!description) return undefined;\n const d = description.toLowerCase();\n if (/out for delivery|on delivery|with delivery courier/.test(d))\n return \"out_for_delivery\";\n // Failure phrases must be checked before the generic \"deliver\" match, so\n // \"delivery failed\" / \"undeliverable\" aren't mistaken for a delivery.\n if (/fail|unable|undeliver|exception|held|on hold|problem|refused|damaged/.test(d))\n return \"exception\";\n if (/deliver/.test(d)) return \"delivered\";\n if (/return/.test(d)) return \"returned\";\n if (/cancel/.test(d)) return \"cancelled\";\n if (/picked up|collected|pickup/.test(d)) return \"picked_up\";\n if (/transit|departed|forwarded|en route/.test(d)) return \"in_transit\";\n if (/received at|arrived|facility|warehouse|sorting|hub/.test(d))\n return \"at_warehouse\";\n if (/created|information received|booked/.test(d)) return \"created\";\n return undefined;\n}\n\n// ============================================================================\n// DATES (WCF /Date(ms)/ format)\n// ============================================================================\n\n/** Parse an Aramex WCF date (`/Date(1609459200000+0300)/`) or ISO string. */\nexport function parseAramexDate(value: string | null | undefined): Date {\n if (!value) return new Date(Number.NaN);\n const wcf = /\\/Date\\((-?\\d+)(?:[+-]\\d{4})?\\)\\//.exec(value);\n if (wcf?.[1]) return new Date(Number.parseInt(wcf[1], 10));\n return new Date(value);\n}\n\nfunction toWcfDate(date: Date): string {\n return `/Date(${date.getTime()})/`;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction round(value: number): number {\n return Math.round(value * 1000) / 1000;\n}\n\nconst VALID_PRODUCT_TYPES = new Set(\n Object.values(AramexProductType) as string[],\n);\n\nfunction getMeta(\n input: CreateShipmentInput,\n key: string,\n): string | undefined {\n const value = input.options?.metadata?.[key];\n return typeof value === \"string\" ? value : undefined;\n}\n\n/**\n * Resolve ProductGroup (DOM/EXP) and ProductType. Defaults to domestic when\n * shipper and consignee share a country, else express. Overridable via\n * `serviceType` (a valid Aramex product code) or `options.metadata`.\n */\nexport function resolveProductGroupAndType(input: CreateShipmentInput): {\n productGroup: \"EXP\" | \"DOM\";\n productType: string;\n} {\n const sameCountry =\n input.shipper.countryCode.trim().toUpperCase() ===\n input.consignee.countryCode.trim().toUpperCase();\n\n const metaGroup = getMeta(input, \"productGroup\")?.toUpperCase();\n const productGroup: \"EXP\" | \"DOM\" =\n metaGroup === \"EXP\" || metaGroup === \"DOM\"\n ? metaGroup\n : sameCountry\n ? \"DOM\"\n : \"EXP\";\n\n const candidate = getMeta(input, \"productType\") ?? input.serviceType;\n const productType =\n candidate && VALID_PRODUCT_TYPES.has(candidate)\n ? candidate\n : productGroup === \"DOM\"\n ? AramexProductType.DOMESTIC\n : AramexProductType.ECONOMY_PARCEL_EXPRESS;\n\n return { productGroup, productType };\n}\n\n/**\n * Resolve the freight PaymentType — who pays the SHIPPING cost: P = prepaid\n * (billed to the shipper's Aramex account), C = collect (charged to the\n * consignee), 3 = third party.\n *\n * This is INDEPENDENT of COD. COD is the cash collected from the consignee for\n * the goods, carried by the `CODS` service + `CashOnDeliveryAmount` — not by\n * PaymentType. KSA/GCC e-commerce COD is overwhelmingly prepaid freight (billed\n * to the merchant) with the goods value collected on delivery, so freight\n * defaults to \"P\". Enabling COD must NOT force freight onto the consignee.\n * Override the freight payer with `options.metadata.paymentType`.\n */\nfunction resolvePaymentType(input: CreateShipmentInput): \"P\" | \"C\" | \"3\" {\n const meta = getMeta(input, \"paymentType\");\n if (meta === \"P\" || meta === \"C\" || meta === \"3\") return meta;\n return \"P\";\n}\n\n/** Aggregate parcel weights; preserve the unit when uniform, else normalize to kg. */\nfunction aggregateWeight(input: CreateShipmentInput): AramexWeight {\n const allLb = input.parcels.every((p) => p.weight.unit === \"lb\");\n if (allLb) {\n const value = input.parcels.reduce((s, p) => s + p.weight.value, 0);\n return { Value: round(value), Unit: \"Lb\" };\n }\n const value = input.parcels.reduce(\n (s, p) =>\n s + (p.weight.unit === \"lb\" ? p.weight.value * 0.453592 : p.weight.value),\n 0,\n );\n return { Value: round(value), Unit: \"Kg\" };\n}\n\nfunction mapDimensions(dims?: Dimensions): AramexDimensions | null {\n if (!dims) return null;\n const factor = dims.unit === \"in\" ? 2.54 : 1;\n return {\n Length: round(dims.length * factor),\n Width: round(dims.width * factor),\n Height: round(dims.height * factor),\n Unit: \"CM\",\n };\n}\n\n/**\n * Build an Aramex `PartyAddress` with every WCF-required member present. The\n * contract marks `Line2`, `Line3` and `PostCode` as REQUIRED — omitting any\n * fails deserialization (\"required data members 'Line2, Line3, PostCode' were\n * not found\") — so they default to \"\" rather than being dropped. Coordinates\n * stay optional and are omitted when unset, so a 0,0 default is never sent as a\n * real location. This is the single source of truth for the address shape;\n * every Aramex address (shipper, consignee, pickup) goes through it.\n */\nfunction buildPartyAddress(fields: {\n line1: string;\n line2?: string;\n line3?: string;\n city: string;\n state?: string;\n postCode?: string;\n countryCode: string;\n coordinates?: { latitude: number; longitude: number };\n}): AramexPartyAddress {\n return {\n Line1: fields.line1,\n Line2: fields.line2 ?? \"\",\n Line3: fields.line3 ?? \"\",\n City: fields.city,\n StateOrProvinceCode: fields.state,\n PostCode: fields.postCode ?? \"\",\n CountryCode: fields.countryCode,\n Longitude: fields.coordinates?.longitude,\n Latitude: fields.coordinates?.latitude,\n };\n}\n\nfunction mapAddress(addr: Address): AramexPartyAddress {\n return buildPartyAddress({\n line1: addr.line1,\n line2: addr.line2,\n line3: addr.neighbourhood,\n city: addr.city,\n state: addr.state,\n postCode: addr.postalCode,\n countryCode: addr.countryCode,\n coordinates: addr.coordinates,\n });\n}\n\n/**\n * Build an Aramex `Contact`. Aramex's WCF Contact contract marks its string\n * members as REQUIRED (the same pattern confirmed live for Address and\n * Transaction — an empty value fails deserialization, an empty string is\n * accepted), so every member is sent, defaulting to \"\".\n */\nfunction buildContact(opts: {\n personName: string;\n companyName: string;\n phone: string;\n email?: string;\n}): AramexContact {\n return {\n Department: \"\",\n PersonName: opts.personName,\n Title: \"\",\n CompanyName: opts.companyName,\n PhoneNumber1: opts.phone,\n PhoneNumber1Ext: \"\",\n PhoneNumber2: \"\",\n CellPhone: opts.phone,\n EmailAddress: opts.email ?? \"\",\n Type: \"\",\n };\n}\n\nfunction mapContact(addr: Address, fallbackCompany?: string): AramexContact {\n return buildContact({\n personName: addr.name,\n companyName: addr.company ?? fallbackCompany ?? addr.name,\n phone: addr.phone,\n email: addr.email,\n });\n}\n\nfunction mapParty(\n addr: Address,\n accountNumber?: string,\n fallbackCompany?: string,\n): AramexParty {\n return {\n AccountNumber: accountNumber,\n PartyAddress: mapAddress(addr),\n Contact: mapContact(addr, fallbackCompany),\n };\n}\n\n/** Shared between CreateShipments and CalculateRate. */\nfunction mapShipmentDetails(\n input: CreateShipmentInput,\n productGroup: \"EXP\" | \"DOM\",\n productType: string,\n paymentType: \"P\" | \"C\" | \"3\",\n): AramexShipmentDetails {\n const numberOfPieces =\n input.parcels.reduce((s, p) => s + p.pieces, 0) || 1;\n const description =\n input.parcels\n .map((p) => p.description)\n .filter(Boolean)\n .join(\", \") || \"Goods\";\n\n const services: string[] = [];\n let cashOnDelivery: AramexMoney | null = null;\n if (input.cod?.enabled) {\n // Use the caller-supplied COD currency — never hardcode.\n cashOnDelivery = {\n Value: input.cod.amount,\n CurrencyCode: input.cod.currency,\n };\n services.push(AramexService.COD);\n }\n\n const customsValue: AramexMoney | null = input.declaredValue\n ? {\n Value: input.declaredValue.amount,\n CurrencyCode: input.declaredValue.currency,\n }\n : null;\n\n // Insurance (INSR) must be accompanied by an amount to insure. Aramex rejects\n // the service with a null InsuranceAmount, so only request it when a declared\n // value is present to base the insured amount on.\n const insuranceAmount: AramexMoney | null =\n input.options?.isInsured && customsValue ? customsValue : null;\n if (insuranceAmount) services.push(AramexService.INSURANCE);\n\n return {\n Dimensions: mapDimensions(input.parcels[0]?.dimensions),\n ActualWeight: aggregateWeight(input),\n ChargeableWeight: null,\n DescriptionOfGoods: description,\n GoodsOriginCountry: input.shipper.countryCode,\n NumberOfPieces: numberOfPieces,\n ProductGroup: productGroup,\n ProductType: productType,\n PaymentType: paymentType,\n PaymentOptions: \"\",\n CustomsValueAmount: customsValue,\n CashOnDeliveryAmount: cashOnDelivery,\n InsuranceAmount: insuranceAmount,\n Services: services.join(\",\") || undefined,\n };\n}\n\n// ============================================================================\n// REQUEST MAPPERS\n// ============================================================================\n\nexport interface AramexShipmentContext {\n accountNumber: string;\n companyName?: string;\n}\n\nexport function mapCreateShipmentRequest(\n input: CreateShipmentInput,\n ctx: AramexShipmentContext,\n): AramexShipment {\n const { productGroup, productType } = resolveProductGroupAndType(input);\n const paymentType = resolvePaymentType(input);\n const now = new Date();\n const due = new Date(now.getTime() + 24 * 60 * 60 * 1000);\n\n return {\n Reference1: input.reference,\n Reference2: input.options?.customerTracking,\n Shipper: mapParty(input.shipper, ctx.accountNumber, ctx.companyName),\n Consignee: mapParty(input.consignee),\n ThirdParty: null,\n ShippingDateTime: toWcfDate(now),\n DueDate: toWcfDate(due),\n Comments: getMeta(input, \"comments\"),\n PickupLocation: getMeta(input, \"pickupLocation\"),\n Details: mapShipmentDetails(input, productGroup, productType, paymentType),\n Attachments: null,\n ForeignHAWB: null,\n TransportType: 0,\n Number: null,\n };\n}\n\nexport function mapCalculateRateRequest(\n input: CreateShipmentInput,\n preferredCurrency?: string,\n): {\n OriginAddress: AramexPartyAddress;\n DestinationAddress: AramexPartyAddress;\n ShipmentDetails: AramexShipmentDetails;\n PreferredCurrencyCode?: string;\n} {\n const { productGroup, productType } = resolveProductGroupAndType(input);\n const paymentType = resolvePaymentType(input);\n return {\n OriginAddress: mapAddress(input.shipper),\n DestinationAddress: mapAddress(input.consignee),\n ShipmentDetails: mapShipmentDetails(\n input,\n productGroup,\n productType,\n paymentType,\n ),\n PreferredCurrencyCode:\n preferredCurrency ??\n input.cod?.currency ??\n input.declaredValue?.currency,\n };\n}\n\nexport interface AramexPickupContext {\n accountNumber: string;\n countryCode: string;\n companyName?: string;\n}\n\nexport function mapPickupRequest(\n input: PickupRequest,\n ctx: AramexPickupContext,\n): AramexPickupObject {\n const pickupDate = new Date(`${input.date}T00:00:00`);\n const ready = new Date(`${input.date}T09:00:00`);\n const closing = new Date(`${input.date}T17:00:00`);\n\n return {\n Reference1: input.trackingNumbers?.[0],\n // Routed through buildPartyAddress so the pickup address carries the same\n // WCF-required members (Line2/Line3/PostCode) as shipper/consignee — a\n // hand-built address that omits them fails Aramex deserialization.\n PickupAddress: buildPartyAddress({\n line1: input.address,\n city: input.city,\n countryCode: ctx.countryCode,\n }),\n PickupContact: buildContact({\n personName: input.contactName,\n companyName: ctx.companyName ?? input.contactName,\n phone: input.contactPhone,\n }),\n PickupLocation: \"Reception\",\n PickupDate: toWcfDate(pickupDate),\n ReadyTime: toWcfDate(ready),\n LastPickupTime: toWcfDate(closing),\n ClosingTime: toWcfDate(closing),\n Comments: input.timeSlot,\n Status: \"Ready\",\n PickupItems: {\n PickupItemDetail: [\n {\n ProductGroup: \"DOM\",\n ProductType: AramexProductType.DOMESTIC,\n Payment: \"P\",\n NumberOfShipments: input.shipmentCount,\n NumberOfPieces: input.shipmentCount,\n ShipmentWeight: { Value: 1, Unit: \"Kg\" },\n },\n ],\n },\n };\n}\n\n// ============================================================================\n// RESPONSE MAPPERS\n// ============================================================================\n\nexport function mapShipmentResponse(\n processed: AramexProcessedShipment,\n input?: CreateShipmentInput,\n): Shipment {\n const trackingNumber = processed.ID || processed.ShipmentNumber || \"\";\n const labelUrl = processed.ShipmentLabel?.LabelURL || undefined;\n\n return {\n carrier: \"aramex\",\n trackingNumber,\n customerTracking: input?.options?.customerTracking,\n reference: input?.reference,\n // Aramex returns no status code on create — the shipment is freshly created.\n status: \"created\",\n statusLabel: \"Created\",\n labelUrl,\n pdfLabelUrl: labelUrl,\n codAmount: input?.cod?.enabled ? input.cod.amount : undefined,\n declaredValue: input?.declaredValue?.amount,\n currency:\n input?.cod?.currency ?? input?.declaredValue?.currency ?? \"SAR\",\n createdAt: new Date(),\n raw: processed,\n };\n}\n\nexport function mapTrackingEvent(\n result: AramexTrackingResult,\n): TrackingEvent {\n return {\n timestamp: parseAramexDate(result.UpdateDateTime),\n statusCode: result.UpdateCode,\n status:\n mapAramexStatus(result.UpdateCode) ??\n statusFromDescription(result.UpdateDescription) ??\n \"unknown\",\n description: result.UpdateDescription,\n location: result.UpdateLocation || undefined,\n };\n}\n\nexport function mapTrackingResult(\n waybill: string,\n results: AramexTrackingResult[],\n): TrackingResult {\n const events = results\n .map(mapTrackingEvent)\n .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());\n\n const latest = events[0];\n const delivered = events.find((e) => e.status === \"delivered\");\n\n // Pull a weight from the most recent raw result that reports one.\n const weightStr = [...results]\n .reverse()\n .find((r) => r.GrossWeight)?.GrossWeight;\n const weight = weightStr ? Number.parseFloat(weightStr) : undefined;\n\n return {\n trackingNumber: waybill,\n carrier: \"aramex\",\n status: latest?.status ?? \"unknown\",\n statusLabel: latest?.description ?? \"Unknown\",\n events,\n deliveryDate: delivered?.timestamp,\n weight: weight != null && !Number.isNaN(weight) ? weight : undefined,\n raw: results,\n };\n}\n\n/**\n * Normalize the `TrackingResults` envelope into a plain map. The WCF JSON\n * serializer renders the dictionary as `[{ Key, Value }]`, but some gateways\n * return a plain object map instead — accept both.\n */\nexport function normalizeTrackingResults(\n raw: AramexTrackShipmentsResponse[\"TrackingResults\"],\n): Record<string, AramexTrackingResult[]> {\n if (!raw) return {};\n if (Array.isArray(raw)) {\n const map: Record<string, AramexTrackingResult[]> = {};\n for (const kv of raw) {\n if (kv && typeof kv === \"object\" && \"Key\" in kv) {\n map[kv.Key] = kv.Value ?? [];\n }\n }\n return map;\n }\n return raw;\n}\n\n/**\n * Build a placeholder `TrackingResult` for a waybill Aramex reports under\n * `NonExistingWaybills`. Aramex splits the TrackShipments response into found\n * (`TrackingResults`) and not-found (`NonExistingWaybills`) buckets; emitting an\n * \"unknown\" entry for the latter keeps the returned array aligned to the input\n * waybills so callers can map inputs→results positionally/by trackingNumber.\n */\nexport function mapNonExistingWaybill(waybill: string): TrackingResult {\n return {\n trackingNumber: waybill,\n carrier: \"aramex\",\n status: \"unknown\",\n statusLabel: \"Waybill not found\",\n events: [],\n raw: { nonExisting: true, waybill },\n };\n}\n\nexport function mapRate(\n response: AramexCalculateRateResponse,\n input: CreateShipmentInput,\n): Rate {\n const { productType } = resolveProductGroupAndType(input);\n return {\n carrier: \"aramex\",\n serviceType: productType,\n serviceName: productType,\n amount: response.TotalAmount?.Value ?? 0,\n currency:\n response.TotalAmount?.CurrencyCode ??\n input.cod?.currency ??\n input.declaredValue?.currency ??\n \"SAR\",\n raw: response,\n };\n}\n\nexport function mapPickupResponse(\n processed: AramexProcessedPickup,\n input: PickupRequest,\n): Pickup {\n // CancelPickup keys on the GUID (PickupGUID), NOT the numeric ID — a pickup\n // that also carries an ID must still surface the GUID as its `id` so the\n // round-trip createPickup → cancelPickup works. Prefer GUID; only fall back\n // to ID when no GUID is present (cancellation then isn't possible, but the\n // caller still gets a usable identifier).\n return {\n id: processed.GUID || processed.ID,\n carrier: \"aramex\",\n status: \"pending\",\n date: input.date,\n timeSlot: input.timeSlot,\n city: input.city,\n contactName: input.contactName,\n contactPhone: input.contactPhone,\n address: input.address,\n shipmentCount: input.shipmentCount,\n createdAt: new Date(),\n raw: processed,\n };\n}\n\nexport function mapCity(name: string): City {\n return { nameEn: name };\n}\n\nexport function mapOffice(office: AramexOffice): Location {\n const addr = office.Address;\n const lines = [addr?.Line1, addr?.Line2, addr?.Line3].filter(Boolean);\n return {\n id: office.EntityCode,\n name: office.EntityName ?? office.EntityCode,\n address: lines.length ? lines.join(\", \") : undefined,\n city: addr?.City,\n latitude: addr?.Latitude,\n longitude: addr?.Longitude,\n };\n}\n",
|
|
7
|
-
"// file: src/carriers/aramex/adapter.ts\n/**\n * Aramex Carrier Adapter\n * Implementation of the CarrierAdapter interface for the Aramex JSON Shipping\n * Services API v2.\n *\n * Key differences from the other carriers:\n * - Auth is a `ClientInfo` object sent in EVERY request body (not headers).\n * - The four Aramex services (Shipping, Tracking, RateCalculator, Location)\n * live on different base URLs, so the adapter holds one HttpClient per service.\n * - Errors use the \"Fake 200 OK\" pattern (HTTP 200 + `HasErrors`/`Notifications`),\n * surfaced via the shared HttpClient's `errorExtractor` hook on every call.\n */\n\nimport {\n APIError,\n UnsupportedOperationError,\n ValidationError,\n} from \"../../core/errors\";\nimport { HttpClient } from \"../../core/http\";\nimport {\n validateCreateShipmentInput,\n validatePickupRequest,\n} from \"../../core/schemas\";\nimport type {\n CarrierConfig,\n City,\n CreateShipmentInput,\n Location,\n Pickup,\n PickupRequest,\n Rate,\n Shipment,\n TrackingResult,\n} from \"../../core/types\";\nimport { BaseCarrierAdapter } from \"../base\";\nimport {\n buildClientInfo,\n mapCalculateRateRequest,\n mapCity,\n mapCreateShipmentRequest,\n mapNonExistingWaybill,\n mapOffice,\n mapPickupRequest,\n mapPickupResponse,\n mapRate,\n mapShipmentResponse,\n mapTrackingResult,\n normalizeTrackingResults,\n} from \"./mappers\";\nimport type {\n AramexCalculateRateResponse,\n AramexClientInfo,\n AramexCreatePickupResponse,\n AramexCancelPickupResponse,\n AramexCreateShipmentsResponse,\n AramexFetchCitiesResponse,\n AramexFetchOfficesResponse,\n AramexLabelInfo,\n AramexNotification,\n AramexPrintLabelResponse,\n AramexTrackShipmentsResponse,\n} from \"./types\";\n\nconst SHIPPING_SANDBOX_URL =\n \"https://ws.dev.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc\";\nconst SHIPPING_PRODUCTION_URL =\n \"https://ws.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc\";\n\n/** Default Aramex label report (HTML/PDF label at 9.7x21cm), returned as a URL. */\nconst DEFAULT_LABEL_INFO: AramexLabelInfo = {\n ReportID: 9201,\n ReportType: \"URL\",\n};\n\n/**\n * Aramex's WCF `Transaction` data contract marks Reference1–Reference5 as\n * REQUIRED members, so an empty `{}` fails deserialization (\"required data\n * members 'Reference1..Reference5' were not found\") on every operation. All five\n * must be present; empty strings are accepted.\n */\nconst EMPTY_TRANSACTION = {\n Reference1: \"\",\n Reference2: \"\",\n Reference3: \"\",\n Reference4: \"\",\n Reference5: \"\",\n} as const;\n\nexport interface AramexConfig extends CarrierConfig {\n credentials: {\n userName: string;\n password: string;\n accountNumber: string;\n accountPin: string;\n accountEntity: string;\n accountCountryCode: string;\n };\n /** API source channel (default 24). */\n source?: number;\n /** ClientInfo version (default \"v1.0\"). */\n version?: string;\n /** Company name used when an address has no company set. */\n companyName?: string;\n /**\n * Optional override for the Location service base URL. Live Aramex WSDLs\n * disagree on the production Location host (ws.aramex.net vs anfe02.aramex.com);\n * set this if your account is provisioned on a different host.\n */\n locationBaseUrl?: string;\n}\n\nexport class AramexAdapter extends BaseCarrierAdapter {\n readonly name = \"aramex\";\n readonly supportedCountries = [\n \"SA\",\n \"AE\",\n \"BH\",\n \"KW\",\n \"OM\",\n \"QA\",\n \"JO\",\n \"EG\",\n \"LB\",\n \"IQ\",\n ];\n\n private shippingHttp: HttpClient;\n private trackingHttp: HttpClient;\n private rateHttp: HttpClient;\n private locationHttp: HttpClient;\n\n constructor(config: AramexConfig) {\n super(config);\n const shippingBase = this.getBaseUrl();\n const common = {\n carrier: \"aramex\",\n // Aramex auth lives in the request body (ClientInfo), so no auth headers.\n };\n this.shippingHttp = new HttpClient({ ...common, baseUrl: shippingBase });\n this.trackingHttp = new HttpClient({\n ...common,\n baseUrl: this.serviceBase(shippingBase, \"Tracking\"),\n });\n this.rateHttp = new HttpClient({\n ...common,\n baseUrl: this.serviceBase(shippingBase, \"RateCalculator\"),\n });\n this.locationHttp = new HttpClient({\n ...common,\n baseUrl:\n config.locationBaseUrl ?? this.serviceBase(shippingBase, \"Location\"),\n });\n }\n\n protected getBaseUrl(): string {\n return this.config.mode === \"production\"\n ? SHIPPING_PRODUCTION_URL\n : SHIPPING_SANDBOX_URL;\n }\n\n /** Derive a sibling service base URL by swapping the `/Shipping/` segment. */\n private serviceBase(shippingBase: string, service: string): string {\n return shippingBase.replace(\"/Shipping/\", `/${service}/`);\n }\n\n private get aramexConfig(): AramexConfig {\n return this.config as AramexConfig;\n }\n\n private buildClientInfo(): AramexClientInfo {\n const cfg = this.aramexConfig;\n return buildClientInfo(cfg.credentials, {\n source: cfg.source,\n version: cfg.version,\n });\n }\n\n /**\n * Extracts the Aramex \"Fake 200 OK\" error (envelope-level `HasErrors` +\n * `Notifications`). Passed to every request so the HttpClient raises APIError.\n */\n private static aramexErrorExtractor(json: unknown): {\n hasError: boolean;\n message?: string;\n errors?: Record<string, string[]>;\n } {\n const obj = json as {\n HasErrors?: boolean;\n Notifications?: AramexNotification[];\n } | null;\n const notifications = obj?.Notifications ?? [];\n const hasError = obj?.HasErrors === true;\n const message =\n notifications\n .map((n) => n?.Message)\n .filter(Boolean)\n .join(\"; \") || (hasError ? \"Aramex returned an error\" : undefined);\n return {\n hasError,\n message,\n errors: notifications.length\n ? AramexAdapter.notificationsToErrors(notifications)\n : undefined,\n };\n }\n\n private static notificationsToErrors(\n notifications: AramexNotification[],\n ): Record<string, string[]> {\n return {\n _aramex: notifications.map((n) =>\n `${n?.Code ?? \"\"}: ${n?.Message ?? \"\"}`.trim(),\n ),\n };\n }\n\n // =========================================================================\n // SHIPPING\n // =========================================================================\n\n protected async executeCreateShipment(\n input: CreateShipmentInput,\n ): Promise<Shipment> {\n const cfg = this.aramexConfig;\n const shipment = mapCreateShipmentRequest(input, {\n accountNumber: cfg.credentials.accountNumber,\n companyName: cfg.companyName,\n });\n\n const response = await this.shippingHttp.post<AramexCreateShipmentsResponse>(\n \"/json/CreateShipments\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n LabelInfo: DEFAULT_LABEL_INFO,\n Shipments: [shipment],\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const processed = response.Shipments?.[0];\n if (!processed) {\n throw new APIError(\"Aramex returned no shipment\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n // CreateShipments can also fail per-shipment while the envelope is clean.\n if (processed.HasErrors) {\n const notifications = processed.Notifications ?? [];\n throw new APIError(\n notifications\n .map((n) => n.Message)\n .filter(Boolean)\n .join(\"; \") || \"Failed to create shipment\",\n {\n carrier: \"aramex\",\n errors: AramexAdapter.notificationsToErrors(notifications),\n raw: processed,\n },\n );\n }\n\n return mapShipmentResponse(processed, input);\n }\n\n /**\n * Create multiple shipments in a single request. Aramex's `CreateShipments`\n * operation is natively batch — it takes a `Shipments` array and returns one\n * processed result per shipment, in request order.\n *\n * Fails loud: if ANY shipment in the batch errors, an `APIError` is thrown\n * whose `raw` holds the full response, so already-created AWBs remain\n * recoverable (the unified `Shipment` type has no per-item error channel).\n */\n async createBulkShipments(\n inputs: CreateShipmentInput[],\n ): Promise<Shipment[]> {\n if (inputs.length === 0) {\n throw new ValidationError(\n \"At least one shipment is required for bulk create\",\n { raw: { count: 0 } },\n );\n }\n for (const input of inputs) validateCreateShipmentInput(input);\n\n const cfg = this.aramexConfig;\n const shipments = inputs.map((input) =>\n mapCreateShipmentRequest(input, {\n accountNumber: cfg.credentials.accountNumber,\n companyName: cfg.companyName,\n }),\n );\n\n const response = await this.shippingHttp.post<AramexCreateShipmentsResponse>(\n \"/json/CreateShipments\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n LabelInfo: DEFAULT_LABEL_INFO,\n Shipments: shipments,\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const processed = response.Shipments ?? [];\n if (processed.length === 0) {\n throw new APIError(\"Aramex returned no shipments\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n const failed = processed.filter((p) => p.HasErrors);\n if (failed.length > 0) {\n const notifications = failed.flatMap((p) => p.Notifications ?? []);\n throw new APIError(\n notifications\n .map((n) => n.Message)\n .filter(Boolean)\n .join(\"; \") ||\n `Failed to create ${failed.length} of ${processed.length} shipments`,\n {\n carrier: \"aramex\",\n errors: AramexAdapter.notificationsToErrors(notifications),\n raw: response,\n },\n );\n }\n\n return processed.map((p, i) => mapShipmentResponse(p, inputs[i]));\n }\n\n /**\n * Aramex's classic Shipping Services API has no shipment-cancellation\n * endpoint, so this is unsupported. (Pickups can be cancelled via cancelPickup.)\n */\n cancelShipment(_trackingNumber: string): Promise<boolean> {\n throw new UnsupportedOperationError(\"aramex\", \"cancelShipment\");\n }\n\n // =========================================================================\n // LABELS\n // =========================================================================\n\n async getLabel(\n trackingNumber: string,\n _format?: \"PDF\" | \"ZPL\" | \"PNG\",\n ): Promise<string> {\n // Aramex PrintLabel returns a URL to the label document. The requested\n // `format` cannot be honored (ZPL/PNG are not selectable) — always a URL.\n const response = await this.shippingHttp.post<AramexPrintLabelResponse>(\n \"/json/PrintLabel\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n ShipmentNumber: trackingNumber,\n LabelInfo: DEFAULT_LABEL_INFO,\n },\n // PrintLabel is a side-effect-free read (it returns a URL for an existing\n // shipment), so it's safe to retry like the other read operations.\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const url = response.ShipmentLabel?.LabelURL;\n if (!url) {\n throw new APIError(\"Failed to get label\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n return url;\n }\n\n // =========================================================================\n // TRACKING\n // =========================================================================\n\n async track(trackingNumber: string): Promise<TrackingResult> {\n const results = await this.trackMultiple([trackingNumber]);\n const result = results[0];\n // trackMultiple now emits an \"unknown\"/not-found entry for waybills Aramex\n // returns under NonExistingWaybills, so a missing-or-not-found waybill must\n // raise a precise not-found rather than returning a misleading \"unknown\".\n if (!result || (result.status === \"unknown\" && result.events.length === 0)) {\n throw new APIError(\"Shipment not found\", {\n carrier: \"aramex\",\n raw: result?.raw ?? { trackingNumber },\n });\n }\n return result;\n }\n\n async trackMultiple(trackingNumbers: string[]): Promise<TrackingResult[]> {\n const response = await this.trackingHttp.post<AramexTrackShipmentsResponse>(\n \"/json/TrackShipments\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n Shipments: trackingNumbers,\n GetLastTrackingUpdateOnly: false,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const map = normalizeTrackingResults(response.TrackingResults);\n const found = Object.entries(map).map(([waybill, results]) =>\n mapTrackingResult(waybill, results),\n );\n\n // Aramex returns not-found waybills in a separate NonExistingWaybills bucket\n // rather than as empty TrackingResults. Surface them as \"unknown\" entries so\n // the returned array stays aligned to the input waybills (callers can map\n // inputs→results); silently dropping them desynchronizes that mapping.\n const nonExisting = (response.NonExistingWaybills ?? []).map(\n mapNonExistingWaybill,\n );\n\n return [...found, ...nonExisting];\n }\n\n async trackByReference(reference: string): Promise<TrackingResult> {\n // Aramex tracks references through the same TrackShipments operation.\n const result = await this.track(reference);\n return { ...result, reference };\n }\n\n // =========================================================================\n // RATES\n // =========================================================================\n\n async getRates(input: CreateShipmentInput): Promise<Rate[]> {\n const rateRequest = mapCalculateRateRequest(input);\n const response = await this.rateHttp.post<AramexCalculateRateResponse>(\n \"/json/CalculateRate\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n ...rateRequest,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n // A clean envelope with no TotalAmount means Aramex couldn't price the\n // route. Surface it as an error rather than returning a misleading 0 rate.\n if (response.TotalAmount?.Value == null) {\n throw new APIError(\"Aramex returned no rate for this shipment\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n return [mapRate(response, input)];\n }\n\n // =========================================================================\n // PICKUPS\n // =========================================================================\n\n async createPickup(input: PickupRequest): Promise<Pickup> {\n validatePickupRequest(input);\n const cfg = this.aramexConfig;\n const pickup = mapPickupRequest(input, {\n accountNumber: cfg.credentials.accountNumber,\n countryCode: cfg.credentials.accountCountryCode,\n companyName: cfg.companyName,\n });\n\n const response = await this.shippingHttp.post<AramexCreatePickupResponse>(\n \"/json/CreatePickup\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n Pickup: pickup,\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n // Judge success on the body payload, not the HTTP status: a \"Fake 200 OK\"\n // can carry an empty ProcessedPickup. A usable pickup needs a GUID — that's\n // the key CancelPickup requires (PickupGUID), so a pickup with only a\n // numeric ID can't be cancelled and isn't a usable round-trip result.\n const processed = response.ProcessedPickup;\n if (!processed || (!processed.GUID && !processed.ID)) {\n throw new APIError(\"Failed to create pickup\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n // A pickup can succeed at the header level (it has a GUID) while individual\n // shipments inside it fail (ProcessedShipments[].HasErrors). Those per-\n // shipment errors are otherwise swallowed — scan and surface them so a\n // partially-failed pickup isn't reported as a clean success.\n const failedShipments = (processed.ProcessedShipments ?? []).filter(\n (s) => s.HasErrors,\n );\n if (failedShipments.length > 0) {\n const notifications = failedShipments.flatMap((s) => s.Notifications ?? []);\n throw new APIError(\n notifications\n .map((n) => n.Message)\n .filter(Boolean)\n .join(\"; \") ||\n `Pickup created but ${failedShipments.length} shipment(s) failed`,\n {\n carrier: \"aramex\",\n errors: AramexAdapter.notificationsToErrors(notifications),\n raw: response,\n },\n );\n }\n\n return mapPickupResponse(processed, input);\n }\n\n async cancelPickup(pickupId: string | number): Promise<boolean> {\n const response = await this.shippingHttp.post<AramexCancelPickupResponse>(\n \"/json/CancelPickup\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n PickupGUID: String(pickupId),\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n // The errorExtractor already throws on `HasErrors: true`, so reaching here\n // means the body reported no error — judge success from that, treating a\n // missing flag as success (consistent with the extractor's `=== true`).\n return response.HasErrors !== true;\n }\n\n // =========================================================================\n // CITIES & LOCATIONS\n // =========================================================================\n\n async getCities(countryCode?: string): Promise<City[]> {\n const response = await this.locationHttp.post<AramexFetchCitiesResponse>(\n \"/json/FetchCities\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n CountryCode:\n countryCode ?? this.aramexConfig.credentials.accountCountryCode,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n return (response.Cities ?? []).map(mapCity);\n }\n\n async getDropoffLocations(countryCode?: string): Promise<Location[]> {\n const response = await this.locationHttp.post<AramexFetchOfficesResponse>(\n \"/json/FetchOffices\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n CountryCode:\n countryCode ?? this.aramexConfig.credentials.accountCountryCode,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n return (response.Offices ?? []).map(mapOffice);\n }\n}\n"
|
|
7
|
+
"// file: src/carriers/aramex/adapter.ts\n/**\n * Aramex Carrier Adapter\n * Implementation of the CarrierAdapter interface for the Aramex JSON Shipping\n * Services API v2.\n *\n * Key differences from the other carriers:\n * - Auth is a `ClientInfo` object sent in EVERY request body (not headers).\n * - The four Aramex services (Shipping, Tracking, RateCalculator, Location)\n * live on different base URLs, so the adapter holds one HttpClient per service.\n * - Errors use the \"Fake 200 OK\" pattern (HTTP 200 + `HasErrors`/`Notifications`),\n * surfaced via the shared HttpClient's `errorExtractor` hook on every call.\n */\n\nimport {\n APIError,\n UnsupportedOperationError,\n ValidationError,\n} from \"../../core/errors\";\nimport { HttpClient } from \"../../core/http\";\nimport {\n validateCreateShipmentInput,\n validatePickupRequest,\n} from \"../../core/schemas\";\nimport type {\n CarrierConfig,\n City,\n CreateShipmentInput,\n Location,\n Pickup,\n PickupRequest,\n Rate,\n Shipment,\n TrackingResult,\n} from \"../../core/types\";\nimport { BaseCarrierAdapter } from \"../base\";\nimport {\n buildClientInfo,\n mapCalculateRateRequest,\n mapCity,\n mapCreateShipmentRequest,\n mapNonExistingWaybill,\n mapOffice,\n mapPickupRequest,\n mapPickupResponse,\n mapRate,\n mapShipmentResponse,\n mapTrackingResult,\n normalizeTrackingResults,\n} from \"./mappers\";\nimport type {\n AramexCalculateRateResponse,\n AramexClientInfo,\n AramexCreatePickupResponse,\n AramexCancelPickupResponse,\n AramexCreateShipmentsResponse,\n AramexFetchCitiesResponse,\n AramexFetchOfficesResponse,\n AramexLabelInfo,\n AramexNotification,\n AramexPrintLabelResponse,\n AramexTrackShipmentsResponse,\n} from \"./types\";\n\nconst SHIPPING_SANDBOX_URL =\n \"https://ws.dev.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc\";\nconst SHIPPING_PRODUCTION_URL =\n \"https://ws.aramex.net/ShippingAPI.V2/Shipping/Service_1_0.svc\";\n\n/** Default Aramex label report (HTML/PDF label at 9.7x21cm), returned as a URL. */\nconst DEFAULT_LABEL_INFO: AramexLabelInfo = {\n ReportID: 9201,\n ReportType: \"URL\",\n};\n\n/**\n * Aramex's WCF `Transaction` data contract marks Reference1–Reference5 as\n * REQUIRED members, so an empty `{}` fails deserialization (\"required data\n * members 'Reference1..Reference5' were not found\") on every operation. All five\n * must be present; empty strings are accepted.\n */\nconst EMPTY_TRANSACTION = {\n Reference1: \"\",\n Reference2: \"\",\n Reference3: \"\",\n Reference4: \"\",\n Reference5: \"\",\n} as const;\n\nexport interface AramexConfig extends CarrierConfig {\n credentials: {\n userName: string;\n password: string;\n accountNumber: string;\n accountPin: string;\n accountEntity: string;\n accountCountryCode: string;\n };\n /** API source channel (default 24). */\n source?: number;\n /** ClientInfo version (default \"v1.0\"). */\n version?: string;\n /** Company name used when an address has no company set. */\n companyName?: string;\n /**\n * Optional override for the Location service base URL. Live Aramex WSDLs\n * disagree on the production Location host (ws.aramex.net vs anfe02.aramex.com);\n * set this if your account is provisioned on a different host.\n */\n locationBaseUrl?: string;\n}\n\nexport class AramexAdapter extends BaseCarrierAdapter {\n readonly name = \"aramex\";\n readonly supportedCountries = [\n \"SA\",\n \"AE\",\n \"BH\",\n \"KW\",\n \"OM\",\n \"QA\",\n \"JO\",\n \"EG\",\n \"LB\",\n \"IQ\",\n ];\n\n private shippingHttp: HttpClient;\n private trackingHttp: HttpClient;\n private rateHttp: HttpClient;\n private locationHttp: HttpClient;\n\n constructor(config: AramexConfig) {\n super(config);\n const shippingBase = this.getBaseUrl();\n const common = {\n carrier: \"aramex\",\n // Aramex auth lives in the request body (ClientInfo), so no auth headers.\n };\n this.shippingHttp = new HttpClient({ ...common, baseUrl: shippingBase });\n this.trackingHttp = new HttpClient({\n ...common,\n baseUrl: this.serviceBase(shippingBase, \"Tracking\"),\n });\n this.rateHttp = new HttpClient({\n ...common,\n baseUrl: this.serviceBase(shippingBase, \"RateCalculator\"),\n });\n this.locationHttp = new HttpClient({\n ...common,\n baseUrl:\n config.locationBaseUrl ?? this.serviceBase(shippingBase, \"Location\"),\n });\n }\n\n protected getBaseUrl(): string {\n return this.config.mode === \"production\"\n ? SHIPPING_PRODUCTION_URL\n : SHIPPING_SANDBOX_URL;\n }\n\n /** Derive a sibling service base URL by swapping the `/Shipping/` segment. */\n private serviceBase(shippingBase: string, service: string): string {\n return shippingBase.replace(\"/Shipping/\", `/${service}/`);\n }\n\n private get aramexConfig(): AramexConfig {\n return this.config as AramexConfig;\n }\n\n private buildClientInfo(): AramexClientInfo {\n const cfg = this.aramexConfig;\n return buildClientInfo(cfg.credentials, {\n source: cfg.source,\n version: cfg.version,\n });\n }\n\n /**\n * Extracts the Aramex \"Fake 200 OK\" error (envelope-level `HasErrors` +\n * `Notifications`). Passed to every request so the HttpClient raises APIError.\n */\n /**\n * Aramex reports throttling inside its \"fake 200\" envelope (and sometimes on a\n * non-429 status) rather than via HTTP 429, so status-code detection misses it.\n * Match the rate-limit wording in the notifications and flag it so the\n * HttpClient raises a retryable `RateLimitError` instead of a plain APIError.\n */\n private static readonly RATE_LIMIT_PATTERN =\n /rate.?limit|too many request|throttl|quota exceeded/i;\n\n private static aramexErrorExtractor(json: unknown): {\n hasError: boolean;\n message?: string;\n errors?: Record<string, string[]>;\n rateLimited?: boolean;\n } {\n const obj = json as {\n HasErrors?: boolean;\n Notifications?: AramexNotification[];\n } | null;\n const notifications = obj?.Notifications ?? [];\n const hasError = obj?.HasErrors === true;\n const message =\n notifications\n .map((n) => n?.Message)\n .filter(Boolean)\n .join(\"; \") || (hasError ? \"Aramex returned an error\" : undefined);\n return {\n hasError,\n message,\n errors: notifications.length\n ? AramexAdapter.notificationsToErrors(notifications)\n : undefined,\n rateLimited:\n hasError && AramexAdapter.RATE_LIMIT_PATTERN.test(message ?? \"\"),\n };\n }\n\n private static notificationsToErrors(\n notifications: AramexNotification[],\n ): Record<string, string[]> {\n return {\n _aramex: notifications.map((n) =>\n `${n?.Code ?? \"\"}: ${n?.Message ?? \"\"}`.trim(),\n ),\n };\n }\n\n // =========================================================================\n // SHIPPING\n // =========================================================================\n\n protected async executeCreateShipment(\n input: CreateShipmentInput,\n ): Promise<Shipment> {\n const cfg = this.aramexConfig;\n const shipment = mapCreateShipmentRequest(input, {\n accountNumber: cfg.credentials.accountNumber,\n companyName: cfg.companyName,\n });\n\n const response = await this.shippingHttp.post<AramexCreateShipmentsResponse>(\n \"/json/CreateShipments\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n LabelInfo: DEFAULT_LABEL_INFO,\n Shipments: [shipment],\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const processed = response.Shipments?.[0];\n if (!processed) {\n throw new APIError(\"Aramex returned no shipment\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n // CreateShipments can also fail per-shipment while the envelope is clean.\n if (processed.HasErrors) {\n const notifications = processed.Notifications ?? [];\n throw new APIError(\n notifications\n .map((n) => n.Message)\n .filter(Boolean)\n .join(\"; \") || \"Failed to create shipment\",\n {\n carrier: \"aramex\",\n errors: AramexAdapter.notificationsToErrors(notifications),\n raw: processed,\n },\n );\n }\n\n return mapShipmentResponse(processed, input);\n }\n\n /**\n * Create multiple shipments in a single request. Aramex's `CreateShipments`\n * operation is natively batch — it takes a `Shipments` array and returns one\n * processed result per shipment, in request order.\n *\n * Fails loud: if ANY shipment in the batch errors, an `APIError` is thrown\n * whose `raw` holds the full response, so already-created AWBs remain\n * recoverable (the unified `Shipment` type has no per-item error channel).\n */\n async createBulkShipments(\n inputs: CreateShipmentInput[],\n ): Promise<Shipment[]> {\n if (inputs.length === 0) {\n throw new ValidationError(\n \"At least one shipment is required for bulk create\",\n { raw: { count: 0 } },\n );\n }\n for (const input of inputs) validateCreateShipmentInput(input);\n\n const cfg = this.aramexConfig;\n const shipments = inputs.map((input) =>\n mapCreateShipmentRequest(input, {\n accountNumber: cfg.credentials.accountNumber,\n companyName: cfg.companyName,\n }),\n );\n\n const response = await this.shippingHttp.post<AramexCreateShipmentsResponse>(\n \"/json/CreateShipments\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n LabelInfo: DEFAULT_LABEL_INFO,\n Shipments: shipments,\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const processed = response.Shipments ?? [];\n if (processed.length === 0) {\n throw new APIError(\"Aramex returned no shipments\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n const failed = processed.filter((p) => p.HasErrors);\n if (failed.length > 0) {\n const notifications = failed.flatMap((p) => p.Notifications ?? []);\n throw new APIError(\n notifications\n .map((n) => n.Message)\n .filter(Boolean)\n .join(\"; \") ||\n `Failed to create ${failed.length} of ${processed.length} shipments`,\n {\n carrier: \"aramex\",\n errors: AramexAdapter.notificationsToErrors(notifications),\n raw: response,\n },\n );\n }\n\n return processed.map((p, i) => mapShipmentResponse(p, inputs[i]));\n }\n\n /**\n * Aramex's classic Shipping Services API has no shipment-cancellation\n * endpoint, so this is unsupported. (Pickups can be cancelled via cancelPickup.)\n */\n cancelShipment(_trackingNumber: string): Promise<boolean> {\n throw new UnsupportedOperationError(\"aramex\", \"cancelShipment\");\n }\n\n // =========================================================================\n // LABELS\n // =========================================================================\n\n async getLabel(\n trackingNumber: string,\n _format?: \"PDF\" | \"ZPL\" | \"PNG\",\n ): Promise<string> {\n // Aramex PrintLabel returns a URL to the label document. The requested\n // `format` cannot be honored (ZPL/PNG are not selectable) — always a URL.\n const response = await this.shippingHttp.post<AramexPrintLabelResponse>(\n \"/json/PrintLabel\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n ShipmentNumber: trackingNumber,\n LabelInfo: DEFAULT_LABEL_INFO,\n },\n // PrintLabel is a side-effect-free read (it returns a URL for an existing\n // shipment), so it's safe to retry like the other read operations.\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const url = response.ShipmentLabel?.LabelURL;\n if (!url) {\n throw new APIError(\"Failed to get label\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n return url;\n }\n\n // =========================================================================\n // TRACKING\n // =========================================================================\n\n async track(trackingNumber: string): Promise<TrackingResult> {\n const results = await this.trackMultiple([trackingNumber]);\n const result = results[0];\n // trackMultiple now emits an \"unknown\"/not-found entry for waybills Aramex\n // returns under NonExistingWaybills, so a missing-or-not-found waybill must\n // raise a precise not-found rather than returning a misleading \"unknown\".\n if (!result || (result.status === \"unknown\" && result.events.length === 0)) {\n throw new APIError(\"Shipment not found\", {\n carrier: \"aramex\",\n raw: result?.raw ?? { trackingNumber },\n });\n }\n return result;\n }\n\n async trackMultiple(trackingNumbers: string[]): Promise<TrackingResult[]> {\n const response = await this.trackingHttp.post<AramexTrackShipmentsResponse>(\n \"/json/TrackShipments\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n Shipments: trackingNumbers,\n GetLastTrackingUpdateOnly: false,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n const map = normalizeTrackingResults(response.TrackingResults);\n const found = Object.entries(map).map(([waybill, results]) =>\n mapTrackingResult(waybill, results),\n );\n\n // Aramex returns not-found waybills in a separate NonExistingWaybills bucket\n // rather than as empty TrackingResults. Surface them as \"unknown\" entries so\n // the returned array stays aligned to the input waybills (callers can map\n // inputs→results); silently dropping them desynchronizes that mapping.\n const nonExisting = (response.NonExistingWaybills ?? []).map(\n mapNonExistingWaybill,\n );\n\n return [...found, ...nonExisting];\n }\n\n async trackByReference(reference: string): Promise<TrackingResult> {\n // Aramex tracks references through the same TrackShipments operation.\n const result = await this.track(reference);\n return { ...result, reference };\n }\n\n // =========================================================================\n // RATES\n // =========================================================================\n\n async getRates(input: CreateShipmentInput): Promise<Rate[]> {\n const rateRequest = mapCalculateRateRequest(input);\n const response = await this.rateHttp.post<AramexCalculateRateResponse>(\n \"/json/CalculateRate\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n ...rateRequest,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n // A clean envelope with no TotalAmount means Aramex couldn't price the\n // route. Surface it as an error rather than returning a misleading 0 rate.\n if (response.TotalAmount?.Value == null) {\n throw new APIError(\"Aramex returned no rate for this shipment\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n return [mapRate(response, input)];\n }\n\n // =========================================================================\n // PICKUPS\n // =========================================================================\n\n async createPickup(input: PickupRequest): Promise<Pickup> {\n validatePickupRequest(input);\n const cfg = this.aramexConfig;\n const pickup = mapPickupRequest(input, {\n accountNumber: cfg.credentials.accountNumber,\n countryCode: cfg.credentials.accountCountryCode,\n companyName: cfg.companyName,\n });\n\n const response = await this.shippingHttp.post<AramexCreatePickupResponse>(\n \"/json/CreatePickup\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n Pickup: pickup,\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n // Judge success on the body payload, not the HTTP status: a \"Fake 200 OK\"\n // can carry an empty ProcessedPickup. A usable pickup needs a GUID — that's\n // the key CancelPickup requires (PickupGUID), so a pickup with only a\n // numeric ID can't be cancelled and isn't a usable round-trip result.\n const processed = response.ProcessedPickup;\n if (!processed || (!processed.GUID && !processed.ID)) {\n throw new APIError(\"Failed to create pickup\", {\n carrier: \"aramex\",\n raw: response,\n });\n }\n\n // A pickup can succeed at the header level (it has a GUID) while individual\n // shipments inside it fail (ProcessedShipments[].HasErrors). Those per-\n // shipment errors are otherwise swallowed — scan and surface them so a\n // partially-failed pickup isn't reported as a clean success.\n const failedShipments = (processed.ProcessedShipments ?? []).filter(\n (s) => s.HasErrors,\n );\n if (failedShipments.length > 0) {\n const notifications = failedShipments.flatMap((s) => s.Notifications ?? []);\n throw new APIError(\n notifications\n .map((n) => n.Message)\n .filter(Boolean)\n .join(\"; \") ||\n `Pickup created but ${failedShipments.length} shipment(s) failed`,\n {\n carrier: \"aramex\",\n errors: AramexAdapter.notificationsToErrors(notifications),\n raw: response,\n },\n );\n }\n\n return mapPickupResponse(processed, input);\n }\n\n async cancelPickup(pickupId: string | number): Promise<boolean> {\n const response = await this.shippingHttp.post<AramexCancelPickupResponse>(\n \"/json/CancelPickup\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n PickupGUID: String(pickupId),\n },\n { errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n // The errorExtractor already throws on `HasErrors: true`, so reaching here\n // means the body reported no error — judge success from that, treating a\n // missing flag as success (consistent with the extractor's `=== true`).\n return response.HasErrors !== true;\n }\n\n // =========================================================================\n // CITIES & LOCATIONS\n // =========================================================================\n\n async getCities(countryCode?: string): Promise<City[]> {\n const response = await this.locationHttp.post<AramexFetchCitiesResponse>(\n \"/json/FetchCities\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n CountryCode:\n countryCode ?? this.aramexConfig.credentials.accountCountryCode,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n return (response.Cities ?? []).map(mapCity);\n }\n\n async getDropoffLocations(countryCode?: string): Promise<Location[]> {\n const response = await this.locationHttp.post<AramexFetchOfficesResponse>(\n \"/json/FetchOffices\",\n {\n ClientInfo: this.buildClientInfo(),\n Transaction: EMPTY_TRANSACTION,\n CountryCode:\n countryCode ?? this.aramexConfig.credentials.accountCountryCode,\n },\n { retry: true, errorExtractor: AramexAdapter.aramexErrorExtractor },\n );\n\n return (response.Offices ?? []).map(mapOffice);\n }\n}\n"
|
|
8
8
|
],
|
|
9
|
-
"mappings": "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",
|
|
10
|
-
"debugId": "
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "A6EBA2B597BA645F64756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -32,7 +32,25 @@ export declare class APIError extends ShipFlowError {
|
|
|
32
32
|
raw?: unknown;
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Carrier rate-limited the request (HTTP 429, 503, or a carrier-specific throttle
|
|
37
|
+
* such as Aramex's "fake 200" envelope). Extends APIError so existing
|
|
38
|
+
* `instanceof APIError` handling keeps working, while `retryAfterMs` lets callers
|
|
39
|
+
* (and the retry helper) honor or reschedule against the carrier's wait window.
|
|
40
|
+
*/
|
|
41
|
+
export declare class RateLimitError extends APIError {
|
|
42
|
+
/** Suggested wait before retrying, in ms (parsed from Retry-After when present). */
|
|
43
|
+
readonly retryAfterMs?: number;
|
|
44
|
+
constructor(message: string, options?: {
|
|
45
|
+
carrier?: string;
|
|
46
|
+
code?: string;
|
|
47
|
+
statusCode?: number;
|
|
48
|
+
errors?: Record<string, string[]>;
|
|
49
|
+
retryAfterMs?: number;
|
|
50
|
+
raw?: unknown;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Validation failed before sending request (Valibot parse failure) */
|
|
36
54
|
export declare class ValidationError extends ShipFlowError {
|
|
37
55
|
readonly field?: string;
|
|
38
56
|
readonly issues?: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;gBAGrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAQ9E;AAED,8DAA8D;AAC9D,qBAAa,YAAa,SAAQ,aAAa;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE;CAI3E;AAED,8FAA8F;AAC9F,qBAAa,QAAS,SAAQ,aAAa;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAGzC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAClC,GAAG,CAAC,EAAE,OAAO,CAAC;KACf;CAOJ;AAED,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/core/http.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* - Uses
|
|
2
|
+
* HTTP client for ShipFlow
|
|
3
|
+
* - Uses the runtime's global `fetch` (Node 18+, Deno, Bun, edge/workers)
|
|
4
4
|
* - Handles "Fake 200 OK" responses (Aramex pattern)
|
|
5
5
|
* - Automatic JSON parsing with error extraction
|
|
6
|
-
* -
|
|
6
|
+
* - Native retry with jittered exponential backoff (GET only by default)
|
|
7
|
+
* - Honors Retry-After on 429/503 within an inline cap (see ./retry)
|
|
7
8
|
*
|
|
8
9
|
* SAFETY: Mutating requests (POST/PUT/DELETE/PATCH) are NOT retried by default
|
|
9
10
|
* because retrying e.g. createShipment after a 500 could create duplicate
|
|
@@ -15,6 +16,17 @@ export interface RetryConfig {
|
|
|
15
16
|
retries?: number;
|
|
16
17
|
/** HTTP status codes to retry on (default: [429, 500, 502, 503, 504]) */
|
|
17
18
|
retryableStatuses?: number[];
|
|
19
|
+
/** Base delay (ms) for exponential backoff (default: 500) */
|
|
20
|
+
baseMs?: number;
|
|
21
|
+
/** Ceiling (ms) for the exponential-backoff window (default: 20_000) */
|
|
22
|
+
maxMs?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Largest Retry-After delay (ms) we sleep on inline (default: 15_000).
|
|
25
|
+
* A 429/503 asking us to wait longer than this is surfaced as a
|
|
26
|
+
* `RateLimitError` (carrying `retryAfterMs`) instead of being retried inline,
|
|
27
|
+
* so a durable queue/workflow can reschedule it.
|
|
28
|
+
*/
|
|
29
|
+
inlineCapMs?: number;
|
|
18
30
|
}
|
|
19
31
|
export interface HttpClientConfig {
|
|
20
32
|
baseUrl: string;
|
|
@@ -35,11 +47,20 @@ export interface RequestOptions {
|
|
|
35
47
|
* - omitted → auto (GET retries, mutating methods do not)
|
|
36
48
|
*/
|
|
37
49
|
retry?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Caller cancellation. Aborts both the in-flight request and any
|
|
52
|
+
* inter-attempt retry sleep, so one signal tears down the whole operation.
|
|
53
|
+
*/
|
|
54
|
+
signal?: AbortSignal;
|
|
38
55
|
/** Custom error extractor for carriers with non-standard error formats */
|
|
39
56
|
errorExtractor?: (json: unknown) => {
|
|
40
57
|
hasError: boolean;
|
|
41
58
|
message?: string;
|
|
42
59
|
errors?: Record<string, string[]>;
|
|
60
|
+
/** Carrier signalled throttling inside an otherwise-OK response. */
|
|
61
|
+
rateLimited?: boolean;
|
|
62
|
+
/** Suggested wait (ms) if the carrier provided one in-band. */
|
|
63
|
+
retryAfterMs?: number;
|
|
43
64
|
};
|
|
44
65
|
}
|
|
45
66
|
export declare class HttpClient {
|
package/dist/core/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"AACA
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native, dependency-free retry helper for ShipFlow's HTTP client (replaces p-retry).
|
|
3
|
+
*
|
|
4
|
+
* Delay policy per failed-but-retryable attempt:
|
|
5
|
+
* - explicit Retry-After ≤ inlineCap → wait the window out + small positive jitter
|
|
6
|
+
* - explicit Retry-After > inlineCap → stop now, surface the error so a durable
|
|
7
|
+
* queue/workflow can reschedule (a long inline sleep is unsafe in serverless /
|
|
8
|
+
* request-bound contexts, and Retry-After is a contract — don't retry early)
|
|
9
|
+
* - otherwise → full-jitter exponential backoff
|
|
10
|
+
*
|
|
11
|
+
* The inter-attempt sleep uses `node:timers/promises` with an AbortSignal so a
|
|
12
|
+
* caller cancellation cleans up the timer (no zombie retries, no leaked `abort`
|
|
13
|
+
* listeners). Never hand-roll `new Promise(r => setTimeout(r, ms))` here.
|
|
14
|
+
*/
|
|
15
|
+
export interface RetryOptions {
|
|
16
|
+
/** Max number of retries after the first attempt. */
|
|
17
|
+
retries: number;
|
|
18
|
+
/** Base delay (ms) for exponential backoff. Default 500. */
|
|
19
|
+
baseMs?: number;
|
|
20
|
+
/** Ceiling (ms) for the exponential-backoff window. Default 20_000. */
|
|
21
|
+
maxMs?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Largest Retry-After delay (ms) we sleep on inline. Beyond this we stop
|
|
24
|
+
* retrying and surface the error so the caller can reschedule durably.
|
|
25
|
+
* Default 15_000.
|
|
26
|
+
*/
|
|
27
|
+
inlineCapMs?: number;
|
|
28
|
+
/** Whether a thrown error is retryable at all. */
|
|
29
|
+
shouldRetry: (error: unknown) => boolean;
|
|
30
|
+
/** Extracts an explicit Retry-After delay (ms) from the error, if any. */
|
|
31
|
+
retryAfterMs?: (error: unknown) => number | undefined;
|
|
32
|
+
/** Caller cancellation — aborts the in-flight request and the inter-attempt sleep. */
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
36
|
+
/**
|
|
37
|
+
* Parse an HTTP `Retry-After` header into milliseconds.
|
|
38
|
+
* Supports both delay-seconds (`"120"`) and HTTP-date forms. Returns undefined
|
|
39
|
+
* when absent or unparseable; clamps past dates to 0.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseRetryAfter(headerValue: string | null | undefined): number | undefined;
|
|
42
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/core/retry.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;GAaG;AAIH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACzC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,sFAAsF;IACtF,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,CAAC,CAAC,CAuCZ;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,MAAM,GAAG,SAAS,CAUpB"}
|