@stores.com/xpo 0.1.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.
Files changed (2) hide show
  1. package/index.js +72 -0
  2. package/package.json +30 -0
package/index.js ADDED
@@ -0,0 +1,72 @@
1
+ const cache = require('memory-cache');
2
+
3
+ const HttpError = require('@stores.com/http-error');
4
+
5
+ function XPO(args) {
6
+ const options = {
7
+ url: 'https://api.ltl.xpo.com',
8
+ ...args
9
+ };
10
+
11
+ /**
12
+ * XPO LTL APIs use the OAuth 2.0 protocol for authentication and authorization using the password grant type.
13
+ * @see https://www.xpo.com/help-center/integration-with-customer-systems/api/
14
+ */
15
+ this.getAccessToken = async (_options = {}) => {
16
+ const key = `xpo_${options.username}`;
17
+ const accessToken = cache.get(key);
18
+
19
+ if (accessToken) {
20
+ return accessToken;
21
+ }
22
+
23
+ const res = await fetch(`${options.url}/token`, {
24
+ body: `grant_type=password&username=${encodeURIComponent(options.username)}&password=${encodeURIComponent(options.password)}`,
25
+ headers: {
26
+ Authorization: `Basic ${options.api_key}`,
27
+ 'Content-Type': 'application/x-www-form-urlencoded'
28
+ },
29
+ method: 'POST',
30
+ signal: AbortSignal.timeout(_options.timeout || 30000)
31
+ });
32
+
33
+ if (!res.ok) {
34
+ throw await HttpError.from(res);
35
+ }
36
+
37
+ const json = await res.json();
38
+
39
+ cache.put(key, json, Number(json.expires_in) * 1000 / 2);
40
+
41
+ return json;
42
+ };
43
+
44
+ /**
45
+ * Retrieves status and basic details about a shipment that match the search criteria based on PRO number(s) and/or customer reference number(s).
46
+ * @see https://www.xpo.com/cdn/files/s1/XPO_API_Shipment_Tracking_Guide.pdf
47
+ */
48
+ this.getShipmentStatus = async (referenceNumbers, _options = {}) => {
49
+ const accessToken = await this.getAccessToken();
50
+
51
+ if (!Array.isArray(referenceNumbers)) {
52
+ referenceNumbers = [referenceNumbers];
53
+ }
54
+
55
+ const query = referenceNumbers.map(r => `referenceNumbers=${encodeURIComponent(r)}`).join('&');
56
+
57
+ const res = await fetch(`${options.url}/tracking/1.0/shipments/shipment-status-details?${query}`, {
58
+ headers: {
59
+ Authorization: `Bearer ${accessToken.access_token}`
60
+ },
61
+ signal: AbortSignal.timeout(_options.timeout || 30000)
62
+ });
63
+
64
+ if (!res.ok) {
65
+ throw await HttpError.from(res);
66
+ }
67
+
68
+ return await res.json();
69
+ };
70
+ }
71
+
72
+ module.exports = XPO;
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "dependencies": {
3
+ "@stores.com/http-error": "~1.0.0",
4
+ "memory-cache": "~0.2.0"
5
+ },
6
+ "description": "XPO LTL Shipment Tracking API client for retrieving shipment status, reference numbers, and tracking events using PRO numbers.",
7
+ "devDependencies": {
8
+ "@eslint/js": "*",
9
+ "globals": "*"
10
+ },
11
+ "keywords": [
12
+ "logistics",
13
+ "tracking",
14
+ "xpo"
15
+ ],
16
+ "license": "Apache-2.0",
17
+ "name": "@stores.com/xpo",
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/stores-com/xpo.git"
24
+ },
25
+ "scripts": {
26
+ "coveralls": "node --test --test-concurrency=true --test-force-exit --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=lcov.info && coveralls < lcov.info",
27
+ "test": "node --test --test-concurrency=true --test-force-exit --test-reporter=spec"
28
+ },
29
+ "version": "0.1.0"
30
+ }