universal-parcel-scraper 0.2.0 → 0.3.0-main.298
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 +209 -71
- package/data/carrier.schema.json +6 -4
- package/data/catalog.json +6 -6
- package/dist/app.d.ts +11 -0
- package/dist/app.js +11 -0
- package/dist/browser/scraper.js +1386 -1568
- package/dist/carriers/aliexpress/adapter.d.ts +5 -3
- package/dist/carriers/aliexpress/adapter.js +28 -8
- package/dist/carriers/amazon-shipping/adapter.d.ts +3 -2
- package/dist/carriers/amazon-shipping/adapter.js +15 -10
- package/dist/carriers/amazon-shipping/eligibility.d.ts +2 -1
- package/dist/carriers/amazon-shipping/eligibility.js +2 -2
- package/dist/carriers/aramex/adapter.d.ts +2 -0
- package/dist/carriers/aramex/adapter.js +5 -3
- package/dist/carriers/aramex/parser.js +2 -2
- package/dist/carriers/asendia/adapter.d.ts +8 -0
- package/dist/carriers/asendia/adapter.js +66 -11
- package/dist/carriers/asendia/probe.d.ts +2 -0
- package/dist/carriers/asendia/probe.js +9 -7
- package/dist/carriers/australia-post/adapter.js +2 -2
- package/dist/carriers/austrian-post/adapter.js +2 -2
- package/dist/carriers/blue-dart/parser.js +2 -2
- package/dist/carriers/bpost/parser.js +2 -2
- package/dist/carriers/bring-posten/parser.js +2 -2
- package/dist/carriers/brt/parser.js +2 -2
- package/dist/carriers/c-chez-vous/adapter.d.ts +3 -2
- package/dist/carriers/c-chez-vous/adapter.js +15 -10
- package/dist/carriers/canada-post/adapter.d.ts +1 -0
- package/dist/carriers/canada-post/adapter.js +5 -3
- package/dist/carriers/canada-post/parser.js +2 -2
- package/dist/carriers/canpar/parser.js +2 -2
- package/dist/carriers/ciblex/parser.js +2 -2
- package/dist/carriers/colis-prive/adapter.d.ts +3 -2
- package/dist/carriers/colis-prive/adapter.js +0 -0
- package/dist/carriers/colisweb/adapter.d.ts +3 -2
- package/dist/carriers/colisweb/adapter.js +13 -8
- package/dist/carriers/correios-br/parser.js +2 -2
- package/dist/carriers/correos-chile/parser.js +3 -3
- package/dist/carriers/correos-express/parser.js +2 -2
- package/dist/carriers/correos-spain/adapter.d.ts +4 -2
- package/dist/carriers/correos-spain/adapter.js +16 -8
- package/dist/carriers/ctt/adapter.d.ts +2 -2
- package/dist/carriers/ctt/adapter.js +30 -25
- package/dist/carriers/ctt-express/parser.js +2 -2
- package/dist/carriers/dachser/adapter.d.ts +3 -2
- package/dist/carriers/dachser/adapter.js +34 -16
- package/dist/carriers/delhivery/parser.js +2 -2
- package/dist/carriers/dhl/adapter.d.ts +2 -2
- package/dist/carriers/dhl/adapter.js +50 -22
- package/dist/carriers/dhl-ecommerce/adapter.d.ts +3 -3
- package/dist/carriers/dhl-ecommerce/adapter.js +8 -8
- package/dist/carriers/dpd/adapter.d.ts +3 -3
- package/dist/carriers/dpd/adapter.js +113 -57
- package/dist/carriers/dpd/carrier.json +1 -1
- package/dist/carriers/dpd-fr/adapter.d.ts +2 -2
- package/dist/carriers/dpd-fr/adapter.js +29 -20
- package/dist/carriers/dtdc/parser.js +2 -2
- package/dist/carriers/ecoscooting/parser.js +2 -2
- package/dist/carriers/ems/adapter.js +2 -2
- package/dist/carriers/estafeta/parser.js +2 -2
- package/dist/carriers/evri/adapter.js +2 -2
- package/dist/carriers/fedex/adapter.d.ts +2 -2
- package/dist/carriers/fedex/adapter.js +14 -10
- package/dist/carriers/four-px/adapter.js +2 -2
- package/dist/carriers/geodis/adapter.d.ts +3 -2
- package/dist/carriers/geodis/adapter.js +13 -8
- package/dist/carriers/gls-ch/adapter.d.ts +4 -3
- package/dist/carriers/gls-ch/adapter.js +27 -19
- package/dist/carriers/gls-ch/carrier.json +1 -1
- package/dist/carriers/gls-de/adapter.d.ts +3 -3
- package/dist/carriers/gls-de/adapter.js +14 -9
- package/dist/carriers/gls-de/carrier.json +1 -1
- package/dist/carriers/gls-fr/adapter.d.ts +3 -2
- package/dist/carriers/gls-fr/adapter.js +16 -11
- package/dist/carriers/gofo/parser.js +2 -2
- package/dist/carriers/heppner/adapter.d.ts +4 -2
- package/dist/carriers/heppner/adapter.js +19 -14
- package/dist/carriers/heppner/carrier.json +1 -1
- package/dist/carriers/hermes/adapter.d.ts +3 -2
- package/dist/carriers/hermes/adapter.js +13 -8
- package/dist/carriers/hermes-de/adapter.d.ts +3 -2
- package/dist/carriers/hermes-de/adapter.js +16 -12
- package/dist/carriers/india-post/adapter.d.ts +2 -2
- package/dist/carriers/india-post/adapter.js +42 -21
- package/dist/carriers/inpost/adapter.d.ts +4 -2
- package/dist/carriers/inpost/adapter.js +17 -10
- package/dist/carriers/japan-post/adapter.js +2 -2
- package/dist/carriers/korea-post/adapter.js +2 -2
- package/dist/carriers/la-poste/adapter.d.ts +4 -2
- package/dist/carriers/la-poste/adapter.js +18 -13
- package/dist/carriers/landmark-global/parser.js +2 -2
- package/dist/carriers/mondial-relay/adapter.d.ts +2 -2
- package/dist/carriers/mondial-relay/adapter.js +26 -17
- package/dist/carriers/mondial-relay/carrier.json +1 -1
- package/dist/carriers/mrw/adapter.js +4 -2
- package/dist/carriers/mrw/parser.js +3 -3
- package/dist/carriers/nacex/parser.js +2 -2
- package/dist/carriers/ninja-van/parser.js +2 -2
- package/dist/carriers/nz-post/parser.js +2 -2
- package/dist/carriers/ontrac/parser.js +2 -2
- package/dist/carriers/paack/adapter.d.ts +4 -2
- package/dist/carriers/paack/adapter.js +24 -14
- package/dist/carriers/paack/carrier.json +1 -1
- package/dist/carriers/packeta/adapter.d.ts +4 -2
- package/dist/carriers/packeta/adapter.js +16 -8
- package/dist/carriers/planzer/adapter.d.ts +5 -3
- package/dist/carriers/planzer/adapter.js +35 -12
- package/dist/carriers/planzer/shared.d.ts +4 -1
- package/dist/carriers/planzer/shared.js +20 -6
- package/dist/carriers/poczta-polska/parser.js +2 -2
- package/dist/carriers/pos-malaysia/adapter.d.ts +2 -0
- package/dist/carriers/pos-malaysia/adapter.js +9 -5
- package/dist/carriers/poste-italiane/adapter.d.ts +4 -2
- package/dist/carriers/poste-italiane/adapter.js +16 -8
- package/dist/carriers/posti/adapter.js +2 -2
- package/dist/carriers/postlogistics/adapter.d.ts +6 -4
- package/dist/carriers/postlogistics/adapter.js +49 -17
- package/dist/carriers/postlogistics/status.d.ts +2 -0
- package/dist/carriers/postlogistics/status.js +2 -0
- package/dist/carriers/postnord/parser.js +2 -2
- package/dist/carriers/purolator/parser.js +2 -2
- package/dist/carriers/relais-colis/adapter.js +2 -2
- package/dist/carriers/royal-mail/adapter.d.ts +2 -2
- package/dist/carriers/royal-mail/adapter.js +14 -10
- package/dist/carriers/seur/parser.js +2 -2
- package/dist/carriers/sf-express/parser.js +2 -2
- package/dist/carriers/singapore-post/adapter.js +2 -2
- package/dist/carriers/spring-gds/adapter.d.ts +2 -0
- package/dist/carriers/spring-gds/adapter.js +16 -15
- package/dist/carriers/sunyou/adapter.d.ts +5 -3
- package/dist/carriers/sunyou/adapter.js +16 -9
- package/dist/carriers/swiss-post/adapter.d.ts +4 -4
- package/dist/carriers/swiss-post/adapter.js +33 -15
- package/dist/carriers/swiss-post-cargo/adapter.d.ts +3 -2
- package/dist/carriers/swiss-post-cargo/adapter.js +14 -9
- package/dist/carriers/the-courier-guy/number.js +3 -2
- package/dist/carriers/tipsa/parser.js +3 -3
- package/dist/carriers/tnt/adapter.js +3 -3
- package/dist/carriers/ukrposhta/adapter.js +20 -62
- package/dist/carriers/ukrposhta/parser.js +2 -2
- package/dist/carriers/uniuni/parser.js +3 -3
- package/dist/carriers/ups/adapter.d.ts +2 -2
- package/dist/carriers/ups/adapter.js +40 -32
- package/dist/carriers/usps/adapter.d.ts +2 -2
- package/dist/carriers/usps/adapter.js +14 -10
- package/dist/carriers/yamato/adapter.js +2 -2
- package/dist/carriers/yanwen/adapter.js +2 -2
- package/dist/carriers/yto/parser.js +2 -2
- package/dist/carriers/yunda/parser.js +2 -2
- package/dist/carriers/yunexpress/adapter.js +21 -39
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +49 -19
- package/dist/core/adapter/index.d.ts +18 -1
- package/dist/core/adapter/index.js +28 -4
- package/dist/core/adapter/track.js +21 -7
- package/dist/core/catalog/index.d.ts +1 -15
- package/dist/core/catalog/index.js +3 -106
- package/dist/core/catalog/inputs.d.ts +2 -2
- package/dist/core/catalog/inputs.js +4 -4
- package/dist/core/catalog/parcel.d.ts +53 -0
- package/dist/core/catalog/parcel.js +105 -0
- package/dist/core/catalog/types.d.ts +1 -29
- package/dist/core/catalog/urls.js +1 -0
- package/dist/core/errors/hint.d.ts +7 -0
- package/dist/core/errors/hint.js +11 -1
- package/dist/core/errors/index.d.ts +8 -1
- package/dist/core/errors/index.js +11 -0
- package/dist/core/runner/index.d.ts +23 -1
- package/dist/core/runner/index.js +71 -14
- package/dist/core/transport/boundedFetch.js +24 -19
- package/dist/core/transport/browser.d.ts +1 -0
- package/dist/core/transport/browser.js +76 -99
- package/dist/core/transport/index.d.ts +4 -1
- package/dist/core/transport/index.js +3 -1
- package/dist/core/transport/localBrowser.d.ts +24 -0
- package/dist/core/transport/localBrowser.js +87 -0
- package/dist/core/transport/trawl.d.ts +6 -0
- package/dist/core/transport/trawl.js +30 -9
- package/dist/core/transport/userAgent.d.ts +8 -0
- package/dist/core/transport/userAgent.js +15 -0
- package/dist/data/catalog.json +6 -6
- package/dist/facade/index.d.ts +3 -0
- package/dist/facade/index.js +29 -11
- package/dist/generated/catalog.d.ts +6 -6
- package/dist/generated/catalog.js +6 -6
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -4
- package/dist/providers/parcelsapp/adapter.d.ts +1 -1
- package/dist/providers/parcelsapp/adapter.js +13 -10
- package/dist/providers/parcelsapp/http.d.ts +1 -1
- package/dist/providers/parcelsapp/http.js +2 -2
- package/dist/providers/postal-ninja/adapter.d.ts +2 -2
- package/dist/providers/postal-ninja/adapter.js +9 -8
- package/dist/providers/seventeentrack/adapter.d.ts +1 -1
- package/dist/providers/seventeentrack/adapter.js +7 -7
- package/dist/providers/shared/capture.d.ts +2 -0
- package/dist/providers/shared/capture.js +1 -1
- package/dist/providers/shared/result.js +4 -2
- package/dist/providers/ship24/adapter.d.ts +2 -2
- package/dist/providers/ship24/adapter.js +15 -11
- package/dist/providers/ship24/http.d.ts +1 -1
- package/dist/providers/ship24/http.js +4 -3
- package/dist/providers/universal.d.ts +3 -2
- package/dist/providers/universal.js +20 -2
- package/dist/providers/upu/adapter.js +2 -2
- package/dist/scripts/carrier-canary.js +2 -1
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.js +90 -12
- package/dist/server/openapi.json +399 -52
- package/package.json +13 -7
package/README.md
CHANGED
|
@@ -1,124 +1,262 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
+
<img src="docs/assets/logo.svg" width="88" alt="">
|
|
4
|
+
|
|
3
5
|
# Universal Parcel Scraper
|
|
4
6
|
|
|
5
|
-
**
|
|
7
|
+
**Parcel tracking that asks the carrier directly, from your own machine.**
|
|
6
8
|
|
|
9
|
+
[](https://www.npmjs.com/package/universal-parcel-scraper)
|
|
7
10
|
[](https://github.com/plhery/universal-parcel-scraper/actions/workflows/ci.yml)
|
|
8
11
|
[](LICENSE)
|
|
9
12
|
|
|
10
|
-
Detect a carrier, fetch its tracking history, and get consistent stages and scan times.
|
|
11
|
-
A TypeScript library, a CLI, and a small HTTP server. No tracking-service account required.
|
|
12
|
-
|
|
13
13
|
<!-- GENERATED:summary -->
|
|
14
14
|
**105 carriers · 85 active dedicated adapters · 58 countries represented**
|
|
15
15
|
<!-- /GENERATED:summary -->
|
|
16
16
|
|
|
17
|
+
[Try it](#try-it) · [Ways to run it](#ways-to-run-it) · [Coverage](#how-much-can-it-track) · [Carriers](carriers/) · [Add a carrier](CONTRIBUTING.md)
|
|
18
|
+
|
|
19
|
+
<img src="docs/assets/demo.svg" width="840" alt="The track command printing a parcel's history as JSON, next to a timeline drawn from it">
|
|
20
|
+
|
|
17
21
|
</div>
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
Give it a tracking number. It works out which carrier the number belongs to and fetches the
|
|
24
|
+
history from that carrier's own website. What comes back has one shape, so a UPS parcel and
|
|
25
|
+
a Poczta Polska parcel look the same to your code.
|
|
26
|
+
|
|
27
|
+
There is no account to open and no API key. Every carrier publishes tracking in its own way,
|
|
28
|
+
and hosted tracking APIs smooth that over for a fee while seeing every number you look up.
|
|
29
|
+
Here the same work is open code: one adapter per carrier, each in [its own folder](carriers/)
|
|
30
|
+
with a README on how that site is read.
|
|
31
|
+
|
|
32
|
+
## Try it
|
|
33
|
+
|
|
34
|
+
You need Node.js 24 or newer.
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx universal-parcel-scraper detect 1Z999AA10123456784
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Detection is offline. It checks the number against each carrier's formats and checksums and
|
|
41
|
+
answers `ups` without sending anything anywhere.
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npx universal-parcel-scraper track YOUR_TRACKING_NUMBER
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Tracking does go out, to the carrier that owns the number. If a number fits several carriers,
|
|
48
|
+
name one with `--carrier dhl`. A few carriers also want the delivery postcode (`--postcode`)
|
|
49
|
+
or the link from the shipping email (`--tracking-url`).
|
|
21
50
|
|
|
22
|
-
##
|
|
51
|
+
## Ways to run it
|
|
23
52
|
|
|
24
|
-
|
|
53
|
+
### Command line
|
|
25
54
|
|
|
26
55
|
```sh
|
|
27
|
-
npm install universal-parcel-scraper
|
|
28
|
-
npx parcel-scraper detect 1Z999AA10123456784
|
|
29
|
-
npx parcel-scraper track YOUR_TRACKING_NUMBER --carrier ups
|
|
56
|
+
npm install -g universal-parcel-scraper
|
|
30
57
|
```
|
|
31
58
|
|
|
59
|
+
The command is `parcel-scraper`:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
parcel-scraper detect <number, link or text> # which carrier is this? no network
|
|
63
|
+
parcel-scraper track <number> # the parcel's history
|
|
64
|
+
parcel-scraper recognize <number> # ask the possible carriers which one knows it
|
|
65
|
+
parcel-scraper carriers # the catalog, with the ids --carrier accepts
|
|
66
|
+
parcel-scraper serve # the HTTP API described below
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Everything prints JSON, so it pipes:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
parcel-scraper track "$NUMBER" | jq -r '.result.current_stage'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`parcel-scraper --help` lists the environment variables.
|
|
76
|
+
|
|
77
|
+
### Node
|
|
78
|
+
|
|
32
79
|
```js
|
|
33
80
|
import { createTracker } from 'universal-parcel-scraper/node';
|
|
34
81
|
|
|
35
82
|
const tracker = createTracker();
|
|
36
|
-
const { carrier, source, result } = await tracker.track({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
console.log(
|
|
83
|
+
const { carrier, source, result } = await tracker.track({ number: process.env.PARCEL_NUMBER });
|
|
84
|
+
|
|
85
|
+
console.log(carrier, result.current_stage);
|
|
86
|
+
for (const scan of result.events) {
|
|
87
|
+
console.log(scan.time, scan.location, scan.description, scan.stage);
|
|
88
|
+
}
|
|
41
89
|
```
|
|
42
90
|
|
|
43
|
-
|
|
91
|
+
Each scan keeps the carrier's wording and gains a `stage` from [one shared list](data/stages.json).
|
|
92
|
+
`source` names who answered, and `attempts` lists everything that was tried. When nothing
|
|
93
|
+
returns a history, `track()` throws a `TrackingError` carrying those attempts and a hint about
|
|
94
|
+
the cause. [examples/node.mjs](examples/node.mjs) is a runnable version.
|
|
95
|
+
|
|
96
|
+
### Browser
|
|
44
97
|
|
|
45
98
|
```js
|
|
46
99
|
import { parseTrackingInput } from 'universal-parcel-scraper';
|
|
47
|
-
|
|
100
|
+
|
|
101
|
+
parseTrackingInput('1Z999AA10123456784');
|
|
102
|
+
// { carrier: 'ups', confidence: 'high', candidates: ['ups'], … }
|
|
48
103
|
```
|
|
49
104
|
|
|
50
|
-
|
|
51
|
-
|
|
105
|
+
The root import has no Node imports and makes no requests, so it can sit behind a form field
|
|
106
|
+
and name the carrier as someone types.
|
|
107
|
+
|
|
108
|
+
### HTTP
|
|
109
|
+
|
|
110
|
+
For anything that is not JavaScript:
|
|
52
111
|
|
|
53
112
|
```sh
|
|
54
|
-
|
|
113
|
+
parcel-scraper serve --port 8080
|
|
55
114
|
```
|
|
56
115
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
116
|
+
```sh
|
|
117
|
+
curl http://127.0.0.1:8080/v1/track \
|
|
118
|
+
-H 'Content-Type: application/json' \
|
|
119
|
+
-d '{"number":"1Z999AA10123456784","carrier":"ups"}'
|
|
120
|
+
```
|
|
60
121
|
|
|
61
|
-
|
|
122
|
+
`/v1/detect`, `/v1/recognize` and `/v1/carriers` sit next to it, and the
|
|
123
|
+
[OpenAPI file](server/openapi.json) describes them all. The server caches recent answers in
|
|
124
|
+
memory and rate-limits callers. It has no database and never polls on its own.
|
|
62
125
|
|
|
63
|
-
|
|
64
|
-
|
|
126
|
+
Set `SCRAPER_TOKEN` to require a bearer token. `SCRAPER_DEMO_PAGE=true` serves a small page
|
|
127
|
+
at `/` for trying numbers by hand. Behind a reverse proxy, set `SCRAPER_TRUSTED_PROXIES` so
|
|
128
|
+
the rate limit counts each caller and not the proxy. The other settings are in
|
|
129
|
+
[.env.example](.env.example).
|
|
65
130
|
|
|
66
|
-
|
|
67
|
-
| Source | Carriers with history |
|
|
68
|
-
| --- | ---: |
|
|
69
|
-
| **Universal Parcel Scraper, all fallbacks enabled** | **70 / 100** |
|
|
70
|
-
| Dedicated adapters alone | 51 / 100 |
|
|
71
|
-
| ParcelsApp | 50 / 100 |
|
|
72
|
-
| Postal Ninja | 41 / 100 |
|
|
73
|
-
| 17TRACK | 40 / 100 |
|
|
74
|
-
| Ship24 | 38 / 100 |
|
|
75
|
-
| UPU | 11 / 100 |
|
|
76
|
-
<!-- /GENERATED:coverage -->
|
|
131
|
+
### Docker
|
|
77
132
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
133
|
+
```sh
|
|
134
|
+
docker run --rm -p 127.0.0.1:8080:8080 ghcr.io/plhery/universal-parcel-scraper:0.3.0
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The image runs `serve` and ships Chromium, for the carriers that only answer a real browser.
|
|
138
|
+
|
|
139
|
+
## What you can build with it
|
|
140
|
+
|
|
141
|
+
- **A tracking screen in your own app.** [Delivery Tracker](https://github.com/plhery/delivery-tracker),
|
|
142
|
+
an open-source iPhone app and PWA, runs on this package.
|
|
143
|
+
- **A Home Assistant sensor** for the parcel you are waiting on.
|
|
144
|
+
[The config is in examples](examples/home-assistant.yaml).
|
|
145
|
+
- **Order status inside a shop or help desk**, so customers are not sent off to the carrier's
|
|
146
|
+
site. Any backend that speaks HTTP can call the server.
|
|
147
|
+
- **Tracking numbers pulled out of shipping emails.** `detect` takes pasted text and carrier
|
|
148
|
+
links as well as bare numbers.
|
|
149
|
+
- **A carrier field that fills itself in**, with the browser import.
|
|
150
|
+
- **A cron job** that pings you when `current_stage` turns `delivered`.
|
|
151
|
+
|
|
152
|
+
The library answers one lookup at a time. Storing parcels and deciding when to check again
|
|
153
|
+
are yours to do.
|
|
83
154
|
|
|
84
|
-
|
|
85
|
-
gives you the retrieval code, control over which sources receive a number, and no per-lookup
|
|
86
|
-
subscription. Catalog totals from vendors are not comparable to this reference test.
|
|
155
|
+
## How it works
|
|
87
156
|
|
|
88
|
-
|
|
157
|
+
<picture>
|
|
158
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/how-it-works-dark.svg">
|
|
159
|
+
<img src="docs/assets/how-it-works-light.svg" width="840" alt="An input is detected offline and tracked by the carrier's dedicated adapter. With no history, the lookup moves to the fallbacks you enabled. Either way the result is one timeline.">
|
|
160
|
+
</picture>
|
|
161
|
+
|
|
162
|
+
Each adapter uses plain HTTP wherever the carrier's site allows it. Some sites only answer a
|
|
163
|
+
real browser and others need image processing, so those adapters can drive a local Chromium
|
|
164
|
+
or the optional [TRAWL browser service](trawl/README.md). Install what your carriers need:
|
|
89
165
|
|
|
90
166
|
```sh
|
|
91
|
-
|
|
92
|
-
curl http://127.0.0.1:8080/v1/track \
|
|
93
|
-
-H 'Content-Type: application/json' \
|
|
94
|
-
-d '{"number":"1Z999AA10123456784","carrier":"ups"}'
|
|
167
|
+
npm install playwright-core sharp onnxruntime-web
|
|
95
168
|
```
|
|
96
169
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
authentication, `SCRAPER_PROVIDERS` to select fallbacks, and `SCRAPER_DEMO_PAGE=true` for a
|
|
100
|
-
small one-off tracking page. [HTTP contract](server/openapi.json) · [Configuration](.env.example).
|
|
170
|
+
Then pass `chromiumPath` or `trawlUrl` to `createTracker()`. The CLI and the server read
|
|
171
|
+
`TRACKING_CHROMIUM_PATH` and `FLARESOLVERR_URL`.
|
|
101
172
|
|
|
102
|
-
|
|
173
|
+
Where a carrier accepts a plain client, the adapter names itself with the package's default
|
|
174
|
+
User-Agent. Pass `userAgent`, or set `SCRAPER_USER_AGENT`, to send your own.
|
|
103
175
|
|
|
104
|
-
|
|
105
|
-
|
|
176
|
+
When the adapter finds no history, the lookup can move on to an aggregator. Only UPU is on
|
|
177
|
+
by default, for the postal numbers it can serve. The commercial ones are opt-in, and each
|
|
178
|
+
one you enable receives the tracking number:
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
createTracker({ providers: ['ParcelsApp', 'Ship24', '17TRACK', 'Postal Ninja', 'UPU'] });
|
|
106
182
|
```
|
|
107
183
|
|
|
108
|
-
|
|
109
|
-
|
|
184
|
+
[ARCHITECTURE.md](ARCHITECTURE.md) has the rest.
|
|
185
|
+
|
|
186
|
+
## How much can it track?
|
|
187
|
+
|
|
188
|
+
A carrier count says what the catalog knows about. Whether a real parcel comes back with its
|
|
189
|
+
history is a different question, so each source was run on its own against the same
|
|
190
|
+
**100-carrier reference set**. The top bar counts a carrier when any source below it returned
|
|
191
|
+
a history.
|
|
110
192
|
|
|
111
|
-
|
|
193
|
+
<!-- GENERATED:coverage -->
|
|
194
|
+
<picture>
|
|
195
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/coverage-dark.svg">
|
|
196
|
+
<img src="docs/assets/coverage-light.svg" alt="Carriers with tracking history: This project, all fallbacks enabled 70, This project, dedicated adapters alone 51, ParcelsApp 50, Postal Ninja 41, 17TRACK 40, Ship24 38, UPU 11." width="760">
|
|
197
|
+
</picture>
|
|
112
198
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
postcode. A configured TRAWL instance handles the requested pages. Browser pages can load the
|
|
116
|
-
carrier's or provider's challenge resources. The library has no telemetry destination;
|
|
117
|
-
HTTP logs contain route names and outcomes, never parcel inputs.
|
|
199
|
+
<details>
|
|
200
|
+
<summary>The same numbers as a table</summary>
|
|
118
201
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
202
|
+
| Source | Carriers with history |
|
|
203
|
+
| --- | ---: |
|
|
204
|
+
| **Universal Parcel Scraper, all fallbacks enabled** | **70 / 100** |
|
|
205
|
+
| Dedicated adapters alone | 51 / 100 |
|
|
206
|
+
| ParcelsApp | 50 / 100 |
|
|
207
|
+
| Postal Ninja | 41 / 100 |
|
|
208
|
+
| 17TRACK | 40 / 100 |
|
|
209
|
+
| Ship24 | 38 / 100 |
|
|
210
|
+
| UPU | 11 / 100 |
|
|
211
|
+
|
|
212
|
+
</details>
|
|
213
|
+
<!-- /GENERATED:coverage -->
|
|
122
214
|
|
|
123
|
-
The
|
|
124
|
-
|
|
215
|
+
The numbers come from [coverage.json](providers/coverage.json): the recorded outcome for each
|
|
216
|
+
carrier's comparison reference, partial histories included and alternate samples left out.
|
|
217
|
+
The set is curated. Read it as a comparison between sources, not as a market-share ranking
|
|
218
|
+
or a promise about today, because parcels expire and carrier sites change.
|
|
219
|
+
[Carrier-by-carrier results and limitations](providers/COVERAGE.md).
|
|
220
|
+
|
|
221
|
+
## Next to a hosted tracking API
|
|
222
|
+
|
|
223
|
+
| | Universal Parcel Scraper | Hosted tracking API |
|
|
224
|
+
| --- | --- | --- |
|
|
225
|
+
| Account | None | Sign-up and an API key |
|
|
226
|
+
| Cost | Your own compute | A plan or a per-shipment price |
|
|
227
|
+
| Who sees the tracking number | The carrier, plus any fallback you enable | The vendor, then the carrier |
|
|
228
|
+
| Carriers | The [catalog](carriers/) in this repository | A much larger catalog |
|
|
229
|
+
| Updates | You poll | Webhooks |
|
|
230
|
+
| When a carrier changes its site | The adapter breaks until it is fixed here | The vendor deals with it |
|
|
231
|
+
| Hosting | Yours | Theirs |
|
|
232
|
+
|
|
233
|
+
If you want the widest catalog and nothing to run, a hosted API is the better choice. This
|
|
234
|
+
project is for when the numbers should stay on your side, or when a price per parcel makes
|
|
235
|
+
no sense for what you are building. Vendor catalog totals are not comparable with the
|
|
236
|
+
reference test above.
|
|
237
|
+
|
|
238
|
+
## Privacy and limits
|
|
239
|
+
|
|
240
|
+
A lookup sends the tracking number to the carrier, along with the postcode or tracking link
|
|
241
|
+
when that carrier requires one. Fallbacks you enabled receive the number too, and ParcelsApp
|
|
242
|
+
also gets a postcode if you supplied it. Pages opened in a browser, locally or through TRAWL,
|
|
243
|
+
can load the carrier's or provider's challenge scripts. The library has no telemetry, and
|
|
244
|
+
the server's logs hold route names and outcomes, never parcel inputs.
|
|
245
|
+
|
|
246
|
+
This is scraping, so things break. Carriers redesign their sites and turn away automated
|
|
247
|
+
requests. A few need more than the number, such as a postcode or the link from the shipping
|
|
248
|
+
email, and the ones that require an account stay limited. Keep to the catalog's refresh
|
|
249
|
+
limits and to the retry advice that comes with an error. A scan time with no known UTC offset
|
|
250
|
+
is returned as the carrier wrote it, with no zone guessed.
|
|
251
|
+
|
|
252
|
+
## Contributing
|
|
253
|
+
|
|
254
|
+
Adapters break when carriers change their sites. Each carrier lives in its own folder with
|
|
255
|
+
synthetic fixtures and offline tests, so a fix stays local to that folder. For a new carrier,
|
|
256
|
+
`npm run carrier:new` scaffolds the folder and [CONTRIBUTING.md](CONTRIBUTING.md) covers the
|
|
257
|
+
rest. Report security issues through [SECURITY.md](SECURITY.md).
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
The core is [Apache-2.0](LICENSE). The optional TRAWL image is a derivative under
|
|
262
|
+
[AGPL-3.0](trawl/LICENSE). Data and model credits are in [NOTICE](NOTICE).
|
package/data/carrier.schema.json
CHANGED
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"minLength": 1
|
|
206
206
|
},
|
|
207
207
|
"canaryUrl": {
|
|
208
|
-
"description": "Public, credential-free HTTPS URL the
|
|
208
|
+
"description": "Public, credential-free HTTPS URL the weekly canary probes. Required for automatic carriers.",
|
|
209
209
|
"type": "string"
|
|
210
210
|
},
|
|
211
211
|
"shows": {
|
|
@@ -273,9 +273,11 @@
|
|
|
273
273
|
],
|
|
274
274
|
"properties": {
|
|
275
275
|
"field": {
|
|
276
|
-
"description": "Input name
|
|
277
|
-
"
|
|
278
|
-
|
|
276
|
+
"description": "Input name. The generator checks that the validator is one this input accepts.",
|
|
277
|
+
"enum": [
|
|
278
|
+
"trackingUrl",
|
|
279
|
+
"postcode"
|
|
280
|
+
]
|
|
279
281
|
},
|
|
280
282
|
"validator": {
|
|
281
283
|
"type": "string",
|
package/data/catalog.json
CHANGED
|
@@ -713,7 +713,7 @@
|
|
|
713
713
|
"adapter": "gls-ch",
|
|
714
714
|
"requirements": [
|
|
715
715
|
{
|
|
716
|
-
"field": "
|
|
716
|
+
"field": "postcode",
|
|
717
717
|
"validator": "swissPostcode",
|
|
718
718
|
"label": "Delivery postcode",
|
|
719
719
|
"type": "text",
|
|
@@ -780,7 +780,7 @@
|
|
|
780
780
|
"adapter": "dpd",
|
|
781
781
|
"requirements": [
|
|
782
782
|
{
|
|
783
|
-
"field": "
|
|
783
|
+
"field": "postcode",
|
|
784
784
|
"validator": "swissPostcode",
|
|
785
785
|
"optional": true,
|
|
786
786
|
"label": "Delivery postcode",
|
|
@@ -879,7 +879,7 @@
|
|
|
879
879
|
"adapter": "mondial-relay",
|
|
880
880
|
"requirements": [
|
|
881
881
|
{
|
|
882
|
-
"field": "
|
|
882
|
+
"field": "postcode",
|
|
883
883
|
"whenTrackingNumber": "^(?![0-9]{26}$).*$",
|
|
884
884
|
"validator": "francePostcode",
|
|
885
885
|
"label": "Delivery postcode",
|
|
@@ -1268,7 +1268,7 @@
|
|
|
1268
1268
|
"adapter": "heppner",
|
|
1269
1269
|
"requirements": [
|
|
1270
1270
|
{
|
|
1271
|
-
"field": "
|
|
1271
|
+
"field": "postcode",
|
|
1272
1272
|
"validator": "swissOrFrancePostcode",
|
|
1273
1273
|
"label": "Delivery postcode",
|
|
1274
1274
|
"type": "text",
|
|
@@ -1350,7 +1350,7 @@
|
|
|
1350
1350
|
"adapter": "paack",
|
|
1351
1351
|
"requirements": [
|
|
1352
1352
|
{
|
|
1353
|
-
"field": "
|
|
1353
|
+
"field": "postcode",
|
|
1354
1354
|
"validator": "paackPostcode",
|
|
1355
1355
|
"label": "Delivery postcode",
|
|
1356
1356
|
"type": "text",
|
|
@@ -1545,7 +1545,7 @@
|
|
|
1545
1545
|
"adapter": "gls-de",
|
|
1546
1546
|
"requirements": [
|
|
1547
1547
|
{
|
|
1548
|
-
"field": "
|
|
1548
|
+
"field": "postcode",
|
|
1549
1549
|
"validator": "swissOrFrancePostcode",
|
|
1550
1550
|
"label": "Delivery postcode",
|
|
1551
1551
|
"type": "text",
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers shaped for the parcel app this package was extracted from: its
|
|
3
|
+
* parcel view, carrier-name hints for provider results, and the clock helpers
|
|
4
|
+
* its sync uses. They change with that app and carry no stability promise;
|
|
5
|
+
* the tracking contract is the other entry points.
|
|
6
|
+
*/
|
|
7
|
+
export * from './core/catalog/parcel.js';
|
|
8
|
+
export * from './core/catalog/hints.js';
|
|
9
|
+
export * from './core/time/result.js';
|
|
10
|
+
export { universalCarrierHints } from './providers/shared/hints.js';
|
|
11
|
+
export { recognitionAskedCarriers } from './core/catalog/recognition.js';
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers shaped for the parcel app this package was extracted from: its
|
|
3
|
+
* parcel view, carrier-name hints for provider results, and the clock helpers
|
|
4
|
+
* its sync uses. They change with that app and carry no stability promise;
|
|
5
|
+
* the tracking contract is the other entry points.
|
|
6
|
+
*/
|
|
7
|
+
export * from './core/catalog/parcel.js';
|
|
8
|
+
export * from './core/catalog/hints.js';
|
|
9
|
+
export * from './core/time/result.js';
|
|
10
|
+
export { universalCarrierHints } from './providers/shared/hints.js';
|
|
11
|
+
export { recognitionAskedCarriers } from './core/catalog/recognition.js';
|