Skip to content

Commit 4a6812c

Browse files
authored
Merge pull request #51 from halprin/release
Initial Release
2 parents cc0b691 + 3a72563 commit 4a6812c

18 files changed

+255
-223
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
node-version-file: package.json
2323
cache: npm
2424

25-
- run: npm ci
25+
- run: npm ci
2626

2727
- run: npm run lint
2828

CHANGELOG.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Delivery Tracker Changelog
22

3-
## [Initial Release] - {PR_MERGE_DATE}
3+
## [Initial Release] - 2025-03-04
44

5-
The initial release. Has support for...
5+
Tracks deliveries, packages, and parcels.
6+
7+
Has two commands to start: one to add a new delivery and one to view all the deliveries you're tracking.
8+
9+
Has initial support for the following carriers...
610
- UPS.
711
- FedEx.
812
- USPS.

assets/extension-icon.png

-98.1 KB
Loading

metadata/delivery-tracker-1.png

-1.06 MB
Loading

metadata/delivery-tracker-2.png

-961 KB
Loading

metadata/delivery-tracker-3.png

-1.06 MB
Loading

metadata/delivery-tracker-4.png

-1.07 MB
Loading

package-lock.json

+106-123
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,24 @@
1414
"package",
1515
"packages",
1616
"parcel",
17-
"parcels"
17+
"parcels",
18+
"USPS",
19+
"UPS",
20+
"FedEx"
1821
],
1922
"license": "MIT",
2023
"commands": [
2124
{
2225
"name": "track-deliveries",
2326
"title": "Track Deliveries",
24-
"subtitle": "Deliveries, packages, and parcels",
27+
"subtitle": "Delivery Tracker",
2528
"description": "View the deliveries you're tracking.",
2629
"mode": "view"
2730
},
2831
{
2932
"name": "track-new-delivery",
3033
"title": "Track New Delivery",
31-
"subtitle": "Deliveries, packages, and parcels",
34+
"subtitle": "Delivery Tracker",
3235
"description": "Starts tracking a new delivery.",
3336
"mode": "view"
3437
}

src/carriers/fedex.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ export async function updateFedexTracking(delivery: Delivery): Promise<Package[]
3636
const loginResponse = await loginWithCachedData(apiKey, secretKey);
3737

3838
console.log("Calling FedEx tracking");
39-
const upsTrackingInfo = await track(trackingNumber, loginResponse.access_token);
39+
const fedexTrackingInfo = await track(trackingNumber, loginResponse.access_token);
4040

41-
const packages = convertUpsTrackingToPackages(upsTrackingInfo);
41+
const packages = convertFedexTrackingToPackages(fedexTrackingInfo);
4242

4343
console.log(`Updated tracking for ${trackingNumber}`);
4444

@@ -59,17 +59,25 @@ async function loginWithCachedData(apiKey: string, secretKey: string): Promise<L
5959
console.log("Logging into FedEx");
6060
loginResponse = await login(apiKey, secretKey);
6161

62+
// expires_in is in seconds and not a timestamp, e.g. `3599`.
63+
// turn it into a timestamp for later validation.
64+
loginResponse.expires_in = new Date().getTime() + loginResponse.expires_in * 1000;
65+
6266
cache.set(cacheKey, JSON.stringify(loginResponse));
6367
} else {
6468
loginResponse = JSON.parse(cache.get(cacheKey) ?? "{}");
6569

6670
const now = new Date().getTime();
6771

68-
if (now + loginResponse.expires_in * 1000 < now + 30 * 1000) {
69-
// we are less than 30 seconds form the access token expiring
72+
if (loginResponse.expires_in < now + 30 * 1000) {
73+
// we are less than 30 seconds from the access token expiring
7074
console.log("Access key expired; logging into FedEx");
7175
loginResponse = await login(apiKey, secretKey);
7276

77+
// expires_in is in seconds and not a timestamp, e.g. `3599`.
78+
// turn it into a timestamp for later validation.
79+
loginResponse.expires_in = new Date().getTime() + loginResponse.expires_in * 1000;
80+
7381
cache.set(cacheKey, JSON.stringify(loginResponse));
7482
}
7583
}
@@ -158,14 +166,14 @@ async function track(trackingNumber: string, accessToken: string): Promise<Fedex
158166

159167
const trackingResponse = (await response.json()) as FedexTrackingInfo;
160168
if (!trackingResponse) {
161-
console.log("Failed to parse FedEx login response");
169+
console.log("Failed to parse FedEx tracking response");
162170
throw new Error("Failed to parse FedEx track response. Please file a bug report.");
163171
}
164172

165173
return trackingResponse;
166174
}
167175

168-
function convertUpsTrackingToPackages(fedexTrackingInfo: FedexTrackingInfo): Package[] {
176+
function convertFedexTrackingToPackages(fedexTrackingInfo: FedexTrackingInfo): Package[] {
169177
return fedexTrackingInfo.output.completeTrackResults
170178
.flatMap((results) => results.trackResults)
171179
.map((aPackage) => {

src/carriers/ups.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Delivery } from "../delivery";
77
const cache = new Cache();
88
const cacheKey = "upsLogin";
99
const host = "onlinetools.ups.com";
10+
const deliveredStatusCode = "011";
1011

1112
export async function ableToTrackUpsRemotely(): Promise<boolean> {
1213
const preferences = getPreferenceValues<Preferences.TrackDeliveries>();
@@ -67,7 +68,7 @@ async function loginWithCachedData(clientId: string, clientSecret: string): Prom
6768
loginResponse = JSON.parse(cache.get(cacheKey) ?? "{}");
6869

6970
if (Number(loginResponse.issued_at) + Number(loginResponse.expires_in) * 1000 < new Date().getTime() + 30 * 1000) {
70-
// we are less than 30 seconds form the access token expiring
71+
// we are less than 30 seconds from the access token expiring
7172
console.log("Access key expired; logging into UPS");
7273
loginResponse = await login(clientId, clientSecret);
7374

@@ -82,7 +83,7 @@ async function login(clientId: string, clientSecret: string): Promise<LoginRespo
8283
const response = await fetch(`https://${host}/security/v1/oauth/token`, {
8384
method: "POST",
8485
headers: {
85-
Authorization: "Basic " + btoa(clientId + ":" + clientSecret),
86+
Authorization: "Basic " + Buffer.from(clientId + ":" + clientSecret).toString("base64"),
8687
"Content-Type": "application/x-www-form-urlencoded",
8788
},
8889
body: new URLSearchParams({
@@ -153,7 +154,7 @@ async function track(trackingNumber: string, accessToken: string): Promise<UpsTr
153154

154155
const trackingResponse = (await response.json()) as UpsTrackingInfo;
155156
if (!trackingResponse) {
156-
console.log("Failed to parse UPS login response");
157+
console.log("Failed to parse UPS tracking response");
157158
throw new Error("Failed to parse UPS track response. Please file a bug report.");
158159
}
159160

@@ -169,7 +170,7 @@ function convertUpsTrackingToPackages(upsTrackingInfo: UpsTrackingInfo): Package
169170
const scheduledDeliveryDate = aPackage.deliveryDate.find((deliveryDate) => deliveryDate.type === "SDD")?.date;
170171

171172
return {
172-
delivered: aPackage.currentStatus.code === "011",
173+
delivered: aPackage.currentStatus.code === deliveredStatusCode,
173174
deliveryDate: convertUpsDateToDate(deliveryDate || rescheduledDeliveryDate || scheduledDeliveryDate),
174175
activity: [],
175176
};

src/carriers/usps.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ export async function updateUspsTracking(delivery: Delivery): Promise<Package[]>
1919

2020
return [
2121
{
22-
delivered: new Date() === delivery.manualDeliveryDate,
22+
delivered: delivery.manualDeliveryDate
23+
? new Date().setHours(0, 0, 0, 0) > delivery.manualDeliveryDate.setHours(0, 0, 0, 0)
24+
: false, // truncate the time from both now and the manual delivery date
2325
deliveryDate: delivery.manualDeliveryDate,
2426
activity: [],
2527
},

src/debugData.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ export const debugDeliveries: Delivery[] = [
4444
carrier: "fedex",
4545
debug: true,
4646
},
47+
{
48+
id: "DD17AC8D-9048-43ED-AF35-4195A2F97243",
49+
name: "no packages",
50+
trackingNumber: "198451726304587",
51+
carrier: "fedex",
52+
debug: true,
53+
},
4754
];
4855

4956
export const debugPackages: PackageMap = {};
@@ -91,12 +98,12 @@ debugPackages[debugDeliveries[4].id] = {
9198
activity: [],
9299
},
93100
{
94-
deliveryDate: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 4), // 2 days ahead
101+
deliveryDate: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 2), // 2 days ahead
95102
delivered: false,
96103
activity: [],
97104
},
98105
{
99-
deliveryDate: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 4), // 1 days ahead
106+
deliveryDate: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 1), // 1 day ahead
100107
delivered: true,
101108
activity: [],
102109
},
@@ -121,3 +128,6 @@ debugPackages[debugDeliveries[5].id] = {
121128
},
122129
],
123130
};
131+
debugPackages[debugDeliveries[6].id] = {
132+
packages: [],
133+
};

src/package.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface PackageMap {
2020
}
2121

2222
export function deliveryIcon(packages?: Package[]): Icon {
23-
if (!packages || packages.length == 0) {
23+
if (!packages || packages.length === 0) {
2424
// there are no packages for this tracking, possible before data has been gotten from API
2525
return Icon.QuestionMarkCircle;
2626
}
@@ -43,7 +43,7 @@ export function deliveryIcon(packages?: Package[]): Icon {
4343
export function deliveryStatus(packages?: Package[]): { value: string; color?: Color } {
4444
// check whether all, some, or no packages in a track are delivered
4545

46-
if (!packages || packages.length == 0) {
46+
if (!packages || packages.length === 0) {
4747
return {
4848
value: "No packages",
4949
color: Color.Orange,
@@ -63,12 +63,13 @@ export function deliveryStatus(packages?: Package[]): { value: string; color?: C
6363
};
6464
}
6565

66-
//find closest estimated delivered package
66+
// find closest estimated delivered package
6767
const closestPackage = getPackageWithEarliestDeliveryDate(packages);
6868

6969
let accessoryText = "En route";
70-
if (closestPackage.deliveryDate) {
71-
accessoryText = calculateDayDifference(closestPackage.deliveryDate).toString() + " days until delivery";
70+
if (closestPackage?.deliveryDate) {
71+
const now = new Date();
72+
accessoryText = calculateDayDifference(closestPackage.deliveryDate, now).toString() + " days until delivery";
7273
}
7374

7475
let accessoryColor = undefined;
@@ -83,7 +84,11 @@ export function deliveryStatus(packages?: Package[]): { value: string; color?: C
8384
};
8485
}
8586

86-
export function getPackageWithEarliestDeliveryDate(packages: Package[]): Package {
87+
export function getPackageWithEarliestDeliveryDate(packages: Package[]): Package | null {
88+
if (packages.length === 0) {
89+
return null;
90+
}
91+
8792
const now = new Date();
8893

8994
return packages.reduce((closest, current) => {
@@ -110,10 +115,10 @@ export function getPackageWithEarliestDeliveryDate(packages: Package[]): Package
110115
});
111116
}
112117

113-
export function calculateDayDifference(deliverDate: Date): number {
118+
export function calculateDayDifference(deliveryDate: Date, comparisonDate: Date): number {
114119
const millisecondsInDay = 1000 * 60 * 60 * 24;
115120

116-
const millisecondsDifference = deliverDate.getTime() - new Date().getTime();
121+
const millisecondsDifference = deliveryDate.getTime() - comparisonDate.getTime();
117122
let dayDifference = Math.ceil(millisecondsDifference / millisecondsInDay);
118123

119124
if (dayDifference < 0) {

0 commit comments

Comments
 (0)