universal-parcel-scraper 0.89.0-main.975 → 0.91.0-main.981
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 +2 -2
- package/data/catalog.json +47 -7
- package/data/coverage-summary.json +1 -1
- package/data/detection-golden.json +32 -0
- package/dist/browser/scraper.js +39 -9
- package/dist/carriers/allegro-one/adapter.d.ts +15 -0
- package/dist/carriers/allegro-one/adapter.js +63 -0
- package/dist/carriers/allegro-one/carrier.json +56 -8
- package/dist/carriers/allegro-one/numbers.json +7 -7
- package/dist/carriers/allegro-one/parser.d.ts +15 -0
- package/dist/carriers/allegro-one/parser.js +91 -0
- package/dist/carriers/allegro-one/status.d.ts +12 -0
- package/dist/carriers/allegro-one/status.js +49 -0
- package/dist/carriers/allegro-one/statuses.json +26 -1
- package/dist/carriers/ceska-posta/adapter.d.ts +15 -0
- package/dist/carriers/ceska-posta/adapter.js +65 -0
- package/dist/carriers/ceska-posta/carrier.json +54 -7
- package/dist/carriers/ceska-posta/numbers.json +40 -0
- package/dist/carriers/ceska-posta/parser.d.ts +15 -0
- package/dist/carriers/ceska-posta/parser.js +137 -0
- package/dist/carriers/ceska-posta/status.d.ts +13 -0
- package/dist/carriers/ceska-posta/status.js +76 -0
- package/dist/carriers/ceska-posta/statuses.json +34 -1
- package/dist/carriers/express-one/adapter.d.ts +14 -0
- package/dist/carriers/express-one/adapter.js +49 -0
- package/dist/carriers/express-one/carrier.json +40 -7
- package/dist/carriers/express-one/parser.d.ts +12 -0
- package/dist/carriers/express-one/parser.js +178 -0
- package/dist/carriers/express-one/status.d.ts +13 -0
- package/dist/carriers/express-one/status.js +48 -0
- package/dist/carriers/express-one/statuses.json +123 -1
- package/dist/carriers/posta-slovenije/adapter.d.ts +14 -0
- package/dist/carriers/posta-slovenije/adapter.js +70 -0
- package/dist/carriers/posta-slovenije/carrier.json +51 -8
- package/dist/carriers/posta-slovenije/numbers.json +8 -8
- package/dist/carriers/posta-slovenije/parser.d.ts +17 -0
- package/dist/carriers/posta-slovenije/parser.js +131 -0
- package/dist/carriers/posta-slovenije/status.d.ts +13 -0
- package/dist/carriers/posta-slovenije/status.js +56 -0
- package/dist/carriers/posta-slovenije/statuses.json +13 -1
- package/dist/core/catalog/statusMaps.js +11 -6
- package/dist/data/catalog.json +47 -7
- package/dist/generated/catalog.d.ts +33 -7
- package/dist/generated/catalog.js +49 -9
- package/dist/generated/recognition.js +4 -0
- package/dist/generated/registry.js +12 -4
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ The engine behind [Peek](https://peektracker.com) ([GitHub](https://github.com/p
|
|
|
13
13
|
[](LICENSE)
|
|
14
14
|
|
|
15
15
|
<!-- GENERATED:summary -->
|
|
16
|
-
**3,500+ carriers** through **
|
|
16
|
+
**3,500+ carriers** through **149 dedicated adapters** and **5 universal fallbacks**
|
|
17
17
|
|
|
18
18
|
<sub>208 catalog entries · 66 countries represented</sub>
|
|
19
19
|
<!-- /GENERATED:summary -->
|
|
@@ -155,7 +155,7 @@ createTracker({ providers: ['ParcelsApp', 'Ship24', '17TRACK', 'Postal Ninja', '
|
|
|
155
155
|
<!-- GENERATED:stages -->
|
|
156
156
|
<img src="docs/assets/stages.svg" alt="DHL: Die Sendung wurde in das Zustellfahrzeug geladen.; Mondial Relay: En cours de livraison; Correios Brazil: Objeto saiu para entrega ao destinatário; Correos Express: EN REPARTO; Yamato Transport: 配達中; La Poste / Colissimo: DISTOU. All are filed under out_for_delivery." width="760">
|
|
157
157
|
|
|
158
|
-
The carrier folders record 4,
|
|
158
|
+
The carrier folders record 4,083 such statuses, each filed under one stage.
|
|
159
159
|
<!-- /GENERATED:stages -->
|
|
160
160
|
|
|
161
161
|
Wording nobody recorded yet goes through a classifier that reads seven European languages.
|
package/data/catalog.json
CHANGED
|
@@ -4805,10 +4805,32 @@
|
|
|
4805
4805
|
"timezone": "Europe/Warsaw",
|
|
4806
4806
|
"tracking": {
|
|
4807
4807
|
"mode": "automatic",
|
|
4808
|
-
"adapter": "
|
|
4808
|
+
"adapter": "allegro-one",
|
|
4809
|
+
"recognitionRank": 770
|
|
4809
4810
|
},
|
|
4810
|
-
"canaryUrl": "https://allegro.pl/",
|
|
4811
|
-
"
|
|
4811
|
+
"canaryUrl": "https://allegro.pl/allegrodelivery/sledzenie-paczki",
|
|
4812
|
+
"trackingUrlTemplate": "https://allegro.pl/allegrodelivery/sledzenie-paczki?numer={trackingNumber}",
|
|
4813
|
+
"trackingSearchUrl": "https://allegro.pl/allegrodelivery/sledzenie-paczki",
|
|
4814
|
+
"linkRules": [
|
|
4815
|
+
{
|
|
4816
|
+
"domains": [
|
|
4817
|
+
"allegro.pl"
|
|
4818
|
+
],
|
|
4819
|
+
"pathPattern": "^/allegrodelivery/sledzenie-paczki/?$",
|
|
4820
|
+
"params": [
|
|
4821
|
+
"numer"
|
|
4822
|
+
]
|
|
4823
|
+
},
|
|
4824
|
+
{
|
|
4825
|
+
"domains": [
|
|
4826
|
+
"allegro.pl"
|
|
4827
|
+
],
|
|
4828
|
+
"pathPattern": "^/kampania/one/kurier/sledzenie-paczki/?$",
|
|
4829
|
+
"params": [
|
|
4830
|
+
"numer"
|
|
4831
|
+
]
|
|
4832
|
+
}
|
|
4833
|
+
],
|
|
4812
4834
|
"detectionRules": [
|
|
4813
4835
|
{
|
|
4814
4836
|
"pattern": "^A[0D][0-9A-Z]{8}$",
|
|
@@ -5069,7 +5091,9 @@
|
|
|
5069
5091
|
"timezone": "Europe/Prague",
|
|
5070
5092
|
"tracking": {
|
|
5071
5093
|
"mode": "automatic",
|
|
5072
|
-
"adapter": "
|
|
5094
|
+
"adapter": "ceska-posta",
|
|
5095
|
+
"recognitionRank": 760,
|
|
5096
|
+
"localClocks": true
|
|
5073
5097
|
},
|
|
5074
5098
|
"canaryUrl": "https://www.postaonline.cz/trackandtrace",
|
|
5075
5099
|
"trackingUrlTemplate": "https://www.postaonline.cz/trackandtrace/-/zasilka/{trackingNumber}",
|
|
@@ -5086,6 +5110,12 @@
|
|
|
5086
5110
|
{
|
|
5087
5111
|
"pattern": "^[BD][A-Z]\\d{10}[A-Z]$",
|
|
5088
5112
|
"confidence": "high"
|
|
5113
|
+
},
|
|
5114
|
+
{
|
|
5115
|
+
"pattern": "^[A-Z]{2}\\d{9}CZ$",
|
|
5116
|
+
"confidence": "low",
|
|
5117
|
+
"checksum": "s10",
|
|
5118
|
+
"preferred": true
|
|
5089
5119
|
}
|
|
5090
5120
|
]
|
|
5091
5121
|
},
|
|
@@ -6155,7 +6185,9 @@
|
|
|
6155
6185
|
"timezone": "Europe/Budapest",
|
|
6156
6186
|
"tracking": {
|
|
6157
6187
|
"mode": "automatic",
|
|
6158
|
-
"adapter": "
|
|
6188
|
+
"adapter": "express-one",
|
|
6189
|
+
"recognitionRank": 780,
|
|
6190
|
+
"localClocks": true
|
|
6159
6191
|
},
|
|
6160
6192
|
"canaryUrl": "https://tracking.expressone.hu/",
|
|
6161
6193
|
"trackingUrlTemplate": "https://tracking.expressone.hu/?plc_number={trackingNumber}",
|
|
@@ -7558,7 +7590,8 @@
|
|
|
7558
7590
|
"timezone": "Europe/Ljubljana",
|
|
7559
7591
|
"tracking": {
|
|
7560
7592
|
"mode": "automatic",
|
|
7561
|
-
"adapter": "
|
|
7593
|
+
"adapter": "posta-slovenije",
|
|
7594
|
+
"recognitionRank": 790
|
|
7562
7595
|
},
|
|
7563
7596
|
"canaryUrl": "https://moja.posta.si/tracking",
|
|
7564
7597
|
"trackingUrlTemplate": "https://moja.posta.si/tracking?trackingNumber={trackingNumber}",
|
|
@@ -7574,7 +7607,14 @@
|
|
|
7574
7607
|
]
|
|
7575
7608
|
}
|
|
7576
7609
|
],
|
|
7577
|
-
"detectionRules": [
|
|
7610
|
+
"detectionRules": [
|
|
7611
|
+
{
|
|
7612
|
+
"pattern": "^[A-Z]{2}\\d{9}SI$",
|
|
7613
|
+
"confidence": "low",
|
|
7614
|
+
"checksum": "s10",
|
|
7615
|
+
"preferred": true
|
|
7616
|
+
}
|
|
7617
|
+
]
|
|
7578
7618
|
},
|
|
7579
7619
|
"posta-srbije": {
|
|
7580
7620
|
"displayName": "Pošta Srbije",
|
|
@@ -25888,6 +25888,7 @@
|
|
|
25888
25888
|
"intl-post"
|
|
25889
25889
|
],
|
|
25890
25890
|
"asked": [
|
|
25891
|
+
"posta-slovenije",
|
|
25891
25892
|
"chronopost"
|
|
25892
25893
|
]
|
|
25893
25894
|
},
|
|
@@ -25899,6 +25900,7 @@
|
|
|
25899
25900
|
"intl-post"
|
|
25900
25901
|
],
|
|
25901
25902
|
"asked": [
|
|
25903
|
+
"posta-slovenije",
|
|
25902
25904
|
"chronopost"
|
|
25903
25905
|
]
|
|
25904
25906
|
},
|
|
@@ -25910,6 +25912,7 @@
|
|
|
25910
25912
|
"intl-post"
|
|
25911
25913
|
],
|
|
25912
25914
|
"asked": [
|
|
25915
|
+
"posta-slovenije",
|
|
25913
25916
|
"chronopost"
|
|
25914
25917
|
]
|
|
25915
25918
|
},
|
|
@@ -26031,6 +26034,7 @@
|
|
|
26031
26034
|
"intl-post"
|
|
26032
26035
|
],
|
|
26033
26036
|
"asked": [
|
|
26037
|
+
"posta-slovenije",
|
|
26034
26038
|
"chronopost"
|
|
26035
26039
|
]
|
|
26036
26040
|
},
|
|
@@ -26851,6 +26855,18 @@
|
|
|
26851
26855
|
"intelcom"
|
|
26852
26856
|
]
|
|
26853
26857
|
},
|
|
26858
|
+
{
|
|
26859
|
+
"input": "CS011354097CZ",
|
|
26860
|
+
"carrier": "intl-post",
|
|
26861
|
+
"confidence": "high",
|
|
26862
|
+
"candidates": [
|
|
26863
|
+
"intl-post"
|
|
26864
|
+
],
|
|
26865
|
+
"asked": [
|
|
26866
|
+
"ceska-posta",
|
|
26867
|
+
"chronopost"
|
|
26868
|
+
]
|
|
26869
|
+
},
|
|
26854
26870
|
{
|
|
26855
26871
|
"input": "CT001362607SK",
|
|
26856
26872
|
"carrier": "intl-post",
|
|
@@ -27068,6 +27084,7 @@
|
|
|
27068
27084
|
"intl-post"
|
|
27069
27085
|
],
|
|
27070
27086
|
"asked": [
|
|
27087
|
+
"posta-slovenije",
|
|
27071
27088
|
"chronopost"
|
|
27072
27089
|
]
|
|
27073
27090
|
},
|
|
@@ -30477,6 +30494,7 @@
|
|
|
30477
30494
|
"intl-post"
|
|
30478
30495
|
],
|
|
30479
30496
|
"asked": [
|
|
30497
|
+
"posta-slovenije",
|
|
30480
30498
|
"chronopost"
|
|
30481
30499
|
]
|
|
30482
30500
|
},
|
|
@@ -30668,6 +30686,7 @@
|
|
|
30668
30686
|
"intl-post"
|
|
30669
30687
|
],
|
|
30670
30688
|
"asked": [
|
|
30689
|
+
"posta-slovenije",
|
|
30671
30690
|
"chronopost"
|
|
30672
30691
|
]
|
|
30673
30692
|
},
|
|
@@ -34133,6 +34152,7 @@
|
|
|
34133
34152
|
"intl-post"
|
|
34134
34153
|
],
|
|
34135
34154
|
"asked": [
|
|
34155
|
+
"posta-slovenije",
|
|
34136
34156
|
"chronopost"
|
|
34137
34157
|
]
|
|
34138
34158
|
},
|
|
@@ -34167,6 +34187,18 @@
|
|
|
34167
34187
|
"correos-chile"
|
|
34168
34188
|
]
|
|
34169
34189
|
},
|
|
34190
|
+
{
|
|
34191
|
+
"input": "RR562174447CZ",
|
|
34192
|
+
"carrier": "intl-post",
|
|
34193
|
+
"confidence": "high",
|
|
34194
|
+
"candidates": [
|
|
34195
|
+
"intl-post"
|
|
34196
|
+
],
|
|
34197
|
+
"asked": [
|
|
34198
|
+
"ceska-posta",
|
|
34199
|
+
"chronopost"
|
|
34200
|
+
]
|
|
34201
|
+
},
|
|
34170
34202
|
{
|
|
34171
34203
|
"input": "RR568370453CL",
|
|
34172
34204
|
"carrier": "correos-chile",
|
package/dist/browser/scraper.js
CHANGED
|
@@ -3947,10 +3947,21 @@ var be = {
|
|
|
3947
3947
|
timezone: "Europe/Warsaw",
|
|
3948
3948
|
tracking: {
|
|
3949
3949
|
mode: "automatic",
|
|
3950
|
-
adapter: "
|
|
3950
|
+
adapter: "allegro-one",
|
|
3951
|
+
recognitionRank: 770
|
|
3951
3952
|
},
|
|
3952
|
-
canaryUrl: "https://allegro.pl/",
|
|
3953
|
-
|
|
3953
|
+
canaryUrl: "https://allegro.pl/allegrodelivery/sledzenie-paczki",
|
|
3954
|
+
trackingUrlTemplate: "https://allegro.pl/allegrodelivery/sledzenie-paczki?numer={trackingNumber}",
|
|
3955
|
+
trackingSearchUrl: "https://allegro.pl/allegrodelivery/sledzenie-paczki",
|
|
3956
|
+
linkRules: [{
|
|
3957
|
+
domains: ["allegro.pl"],
|
|
3958
|
+
pathPattern: "^/allegrodelivery/sledzenie-paczki/?$",
|
|
3959
|
+
params: ["numer"]
|
|
3960
|
+
}, {
|
|
3961
|
+
domains: ["allegro.pl"],
|
|
3962
|
+
pathPattern: "^/kampania/one/kurier/sledzenie-paczki/?$",
|
|
3963
|
+
params: ["numer"]
|
|
3964
|
+
}],
|
|
3954
3965
|
detectionRules: [{
|
|
3955
3966
|
pattern: "^A[0D][0-9A-Z]{8}$",
|
|
3956
3967
|
confidence: "high"
|
|
@@ -4145,7 +4156,9 @@ var be = {
|
|
|
4145
4156
|
timezone: "Europe/Prague",
|
|
4146
4157
|
tracking: {
|
|
4147
4158
|
mode: "automatic",
|
|
4148
|
-
adapter: "
|
|
4159
|
+
adapter: "ceska-posta",
|
|
4160
|
+
recognitionRank: 760,
|
|
4161
|
+
localClocks: !0
|
|
4149
4162
|
},
|
|
4150
4163
|
canaryUrl: "https://www.postaonline.cz/trackandtrace",
|
|
4151
4164
|
trackingUrlTemplate: "https://www.postaonline.cz/trackandtrace/-/zasilka/{trackingNumber}",
|
|
@@ -4157,6 +4170,11 @@ var be = {
|
|
|
4157
4170
|
detectionRules: [{
|
|
4158
4171
|
pattern: "^[BD][A-Z]\\d{10}[A-Z]$",
|
|
4159
4172
|
confidence: "high"
|
|
4173
|
+
}, {
|
|
4174
|
+
pattern: "^[A-Z]{2}\\d{9}CZ$",
|
|
4175
|
+
confidence: "low",
|
|
4176
|
+
checksum: "s10",
|
|
4177
|
+
preferred: !0
|
|
4160
4178
|
}]
|
|
4161
4179
|
},
|
|
4162
4180
|
"chit-chats": {
|
|
@@ -4950,7 +4968,9 @@ var be = {
|
|
|
4950
4968
|
timezone: "Europe/Budapest",
|
|
4951
4969
|
tracking: {
|
|
4952
4970
|
mode: "automatic",
|
|
4953
|
-
adapter: "
|
|
4971
|
+
adapter: "express-one",
|
|
4972
|
+
recognitionRank: 780,
|
|
4973
|
+
localClocks: !0
|
|
4954
4974
|
},
|
|
4955
4975
|
canaryUrl: "https://tracking.expressone.hu/",
|
|
4956
4976
|
trackingUrlTemplate: "https://tracking.expressone.hu/?plc_number={trackingNumber}",
|
|
@@ -6005,7 +6025,8 @@ var be = {
|
|
|
6005
6025
|
timezone: "Europe/Ljubljana",
|
|
6006
6026
|
tracking: {
|
|
6007
6027
|
mode: "automatic",
|
|
6008
|
-
adapter: "
|
|
6028
|
+
adapter: "posta-slovenije",
|
|
6029
|
+
recognitionRank: 790
|
|
6009
6030
|
},
|
|
6010
6031
|
canaryUrl: "https://moja.posta.si/tracking",
|
|
6011
6032
|
trackingUrlTemplate: "https://moja.posta.si/tracking?trackingNumber={trackingNumber}",
|
|
@@ -6015,7 +6036,12 @@ var be = {
|
|
|
6015
6036
|
pathPattern: "^/tracking/?$",
|
|
6016
6037
|
params: ["trackingNumber"]
|
|
6017
6038
|
}],
|
|
6018
|
-
detectionRules: [
|
|
6039
|
+
detectionRules: [{
|
|
6040
|
+
pattern: "^[A-Z]{2}\\d{9}SI$",
|
|
6041
|
+
confidence: "low",
|
|
6042
|
+
checksum: "s10",
|
|
6043
|
+
preferred: !0
|
|
6044
|
+
}]
|
|
6019
6045
|
},
|
|
6020
6046
|
"posta-srbije": {
|
|
6021
6047
|
displayName: "Pošta Srbije",
|
|
@@ -7039,7 +7065,7 @@ var be = {
|
|
|
7039
7065
|
cargus: [],
|
|
7040
7066
|
cdek: [],
|
|
7041
7067
|
cdl: ["cdl-1", "cdl-2"],
|
|
7042
|
-
"ceska-posta": ["ceska-posta-1"],
|
|
7068
|
+
"ceska-posta": ["ceska-posta-1", "ceska-posta-s10"],
|
|
7043
7069
|
"chit-chats": ["chit-chats-1"],
|
|
7044
7070
|
cne: ["cne-1"],
|
|
7045
7071
|
"correos-de-mexico": [],
|
|
@@ -7133,7 +7159,7 @@ var be = {
|
|
|
7133
7159
|
paquetexpress: [],
|
|
7134
7160
|
passport: ["passport-1"],
|
|
7135
7161
|
"post-luxembourg": [],
|
|
7136
|
-
"posta-slovenije": [],
|
|
7162
|
+
"posta-slovenije": ["posta-slovenije-s10"],
|
|
7137
7163
|
"posta-srbije": [],
|
|
7138
7164
|
ppl: [],
|
|
7139
7165
|
ptt: ["ptt-1", "ptt-s10"],
|
|
@@ -11856,6 +11882,7 @@ function yc(e) {
|
|
|
11856
11882
|
var bc = {
|
|
11857
11883
|
"acs-courier": 570,
|
|
11858
11884
|
aliexpress: 140,
|
|
11885
|
+
"allegro-one": 770,
|
|
11859
11886
|
"aras-kargo": 670,
|
|
11860
11887
|
"australia-post": 60,
|
|
11861
11888
|
"austrian-post": 390,
|
|
@@ -11865,6 +11892,7 @@ var bc = {
|
|
|
11865
11892
|
"canada-post": 150,
|
|
11866
11893
|
canpar: 340,
|
|
11867
11894
|
cargus: 730,
|
|
11895
|
+
"ceska-posta": 760,
|
|
11868
11896
|
chronopost: 450,
|
|
11869
11897
|
ciblex: 200,
|
|
11870
11898
|
"colis-prive": 300,
|
|
@@ -11884,6 +11912,7 @@ var bc = {
|
|
|
11884
11912
|
econt: 560,
|
|
11885
11913
|
elta: 550,
|
|
11886
11914
|
emile: 40,
|
|
11915
|
+
"express-one": 780,
|
|
11887
11916
|
"fan-courier": 510,
|
|
11888
11917
|
"four-px": 130,
|
|
11889
11918
|
foxpost: 520,
|
|
@@ -11910,6 +11939,7 @@ var bc = {
|
|
|
11910
11939
|
ontrac: 210,
|
|
11911
11940
|
"orlen-paczka": 490,
|
|
11912
11941
|
"poczta-polska": 310,
|
|
11942
|
+
"posta-slovenije": 790,
|
|
11913
11943
|
"poste-italiane": 460,
|
|
11914
11944
|
posti: 270,
|
|
11915
11945
|
postlogistics: 260,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AdapterFactory, type TrackingContext } from '../../core/adapter/index.js';
|
|
2
|
+
import type { CarrierResult } from '../../core/result/index.js';
|
|
3
|
+
import { type StepRecorder } from '../../core/telemetry/index.js';
|
|
4
|
+
export declare const ALLEGRO_ONE_ENDPOINT = "https://edge.allegro.pl/one/tracking";
|
|
5
|
+
export declare function allegroOneTrackingUrl(raw: string): string;
|
|
6
|
+
export declare class AllegroOneTracker {
|
|
7
|
+
private readonly options;
|
|
8
|
+
constructor(options?: {
|
|
9
|
+
fetcher?: typeof fetch;
|
|
10
|
+
recorder?: StepRecorder;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
});
|
|
13
|
+
fetch(raw: string, context?: TrackingContext): Promise<CarrierResult>;
|
|
14
|
+
}
|
|
15
|
+
export declare const adapter: AdapterFactory;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { accepted, recognizeFromLookup } from '../../core/adapter/index.js';
|
|
2
|
+
import { ChallengeError, IndeterminateError, SchemaError } from '../../core/errors/index.js';
|
|
3
|
+
import { runSteps } from '../../core/runner/index.js';
|
|
4
|
+
import { firstAttemptMs, networkRetryStep } from '../../core/runner/networkRetry.js';
|
|
5
|
+
import { NOOP_RECORDER } from '../../core/telemetry/index.js';
|
|
6
|
+
import { decodeText, fetchBounded, userAgentOf } from '../../core/transport/index.js';
|
|
7
|
+
import { ALLEGRO_ONE_MAX_BYTES, normalizeAllegroOneNumber, parseAllegroOne, PROVIDER } from './parser.js';
|
|
8
|
+
// The service Allegro's tracking page asks through its edge gateway.
|
|
9
|
+
export const ALLEGRO_ONE_ENDPOINT = 'https://edge.allegro.pl/one/tracking';
|
|
10
|
+
// The gateway answers only the media type Allegro's own pages ask for.
|
|
11
|
+
const MEDIA_TYPE = 'application/vnd.allegro.internal.v1+json';
|
|
12
|
+
const REDIRECTS = [301, 302, 303, 307, 308];
|
|
13
|
+
// DataDome's interstitial, which guards Allegro's pages and could come instead of the reply.
|
|
14
|
+
const CHALLENGE = /captcha-delivery\.com|datadome/i;
|
|
15
|
+
export function allegroOneTrackingUrl(raw) {
|
|
16
|
+
return `${ALLEGRO_ONE_ENDPOINT}?packageNo=${encodeURIComponent(normalizeAllegroOneNumber(raw))}`;
|
|
17
|
+
}
|
|
18
|
+
export class AllegroOneTracker {
|
|
19
|
+
options;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
async fetch(raw, context = {}) {
|
|
24
|
+
const url = allegroOneTrackingUrl(raw);
|
|
25
|
+
const read = async (signal, timeoutMs) => {
|
|
26
|
+
// The scans are worded in the language asked for: Polish for `pl-PL` or
|
|
27
|
+
// `*`, English for `en-US`. The status map reads English.
|
|
28
|
+
const { response, bytes } = await fetchBounded(url, { signal, headers: {
|
|
29
|
+
Accept: MEDIA_TYPE, 'Accept-Language': 'en-US', 'User-Agent': userAgentOf(this.options.userAgent),
|
|
30
|
+
} }, { provider: PROVIDER, timeoutMs: Math.max(1, Math.floor(timeoutMs)), maxBytes: ALLEGRO_ONE_MAX_BYTES, redirect: 'manual',
|
|
31
|
+
allowHttpStatuses: [...REDIRECTS, 400, 404, 406, 410], fetcher: this.options.fetcher });
|
|
32
|
+
// A refused request, a retired route ("Feature unavailable") or a move says nothing about the parcel.
|
|
33
|
+
if (response.status !== 200)
|
|
34
|
+
throw new IndeterminateError(PROVIDER, 'Allegro One tracking service is unavailable');
|
|
35
|
+
const text = decodeText(bytes);
|
|
36
|
+
let reply;
|
|
37
|
+
try {
|
|
38
|
+
reply = JSON.parse(text);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (CHALLENGE.test(text))
|
|
42
|
+
throw new ChallengeError(PROVIDER, 'Allegro One answered with a bot check', { cause: error });
|
|
43
|
+
throw new SchemaError(PROVIDER, 'Allegro One returned a reply that is not JSON', { cause: error });
|
|
44
|
+
}
|
|
45
|
+
return parseAllegroOne(reply, raw);
|
|
46
|
+
};
|
|
47
|
+
// A request that fails to reach Allegro, or hangs, gets one more try: the
|
|
48
|
+
// first gets half of the budget, so the retry fits in the rest.
|
|
49
|
+
return runSteps({ carrier: 'allegro-one', budgetMs: context.budgetMs ?? 15_000, signal: context.signal,
|
|
50
|
+
recorder: this.options.recorder ?? NOOP_RECORDER }, [
|
|
51
|
+
{ id: 'direct', run: ({ signal, remainingMs }) => read(signal, firstAttemptMs(remainingMs)) },
|
|
52
|
+
networkRetryStep(({ signal, remainingMs }) => read(signal, remainingMs)),
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export const adapter = (environment) => {
|
|
57
|
+
const tracker = new AllegroOneTracker({ fetcher: environment.fetcher, recorder: environment.recorder, userAgent: environment.userAgent });
|
|
58
|
+
return {
|
|
59
|
+
id: 'allegro-one', recordsSteps: true, steps: ['direct', 'retry'],
|
|
60
|
+
track: (input, context) => tracker.fetch(input.number, context),
|
|
61
|
+
recognize: (number, context) => recognizeFromLookup(() => tracker.fetch(number, context), () => accepted(() => normalizeAllegroOneNumber(number))),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -19,18 +19,66 @@
|
|
|
19
19
|
"timezone": "Europe/Warsaw",
|
|
20
20
|
"tracking": {
|
|
21
21
|
"mode": "automatic",
|
|
22
|
-
"adapter": "
|
|
23
|
-
"steps": [
|
|
22
|
+
"adapter": "allegro-one",
|
|
23
|
+
"steps": [
|
|
24
|
+
"direct",
|
|
25
|
+
"retry"
|
|
26
|
+
],
|
|
27
|
+
"recognition": {
|
|
28
|
+
"rank": 770
|
|
29
|
+
}
|
|
24
30
|
},
|
|
25
|
-
"capabilities": [
|
|
31
|
+
"capabilities": [
|
|
32
|
+
"history",
|
|
33
|
+
"delivered_at"
|
|
34
|
+
],
|
|
26
35
|
"portal": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
36
|
+
"url": "https://allegro.pl/allegrodelivery/sledzenie-paczki?numer={trackingNumber}",
|
|
37
|
+
"searchUrl": "https://allegro.pl/allegrodelivery/sledzenie-paczki",
|
|
38
|
+
"canaryUrl": "https://allegro.pl/allegrodelivery/sledzenie-paczki",
|
|
39
|
+
"shows": [
|
|
40
|
+
"status",
|
|
41
|
+
"history",
|
|
42
|
+
"delivered_at"
|
|
43
|
+
],
|
|
44
|
+
"scraped": [
|
|
45
|
+
"status",
|
|
46
|
+
"history",
|
|
47
|
+
"delivered_at",
|
|
48
|
+
"offset_timestamps"
|
|
49
|
+
],
|
|
30
50
|
"discarded": [],
|
|
31
|
-
"unavailable": [
|
|
51
|
+
"unavailable": [
|
|
52
|
+
"location",
|
|
53
|
+
"eta",
|
|
54
|
+
"pickup_point",
|
|
55
|
+
"weight",
|
|
56
|
+
"service_name",
|
|
57
|
+
"sender_name",
|
|
58
|
+
"recipient_name",
|
|
59
|
+
"delivery_partner"
|
|
60
|
+
]
|
|
32
61
|
},
|
|
33
|
-
"links": [
|
|
62
|
+
"links": [
|
|
63
|
+
{
|
|
64
|
+
"domains": [
|
|
65
|
+
"allegro.pl"
|
|
66
|
+
],
|
|
67
|
+
"pathPattern": "^/allegrodelivery/sledzenie-paczki/?$",
|
|
68
|
+
"params": [
|
|
69
|
+
"numer"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"domains": [
|
|
74
|
+
"allegro.pl"
|
|
75
|
+
],
|
|
76
|
+
"pathPattern": "^/kampania/one/kurier/sledzenie-paczki/?$",
|
|
77
|
+
"params": [
|
|
78
|
+
"numer"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
],
|
|
34
82
|
"detection": [
|
|
35
83
|
{
|
|
36
84
|
"id": "allegro-one-1",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"access": "full_page"
|
|
13
13
|
},
|
|
14
14
|
"context": {
|
|
15
|
-
"assessment": "
|
|
15
|
+
"assessment": "confirmed"
|
|
16
16
|
},
|
|
17
17
|
"expect": {
|
|
18
18
|
"carrier": "allegro-one",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "full_page"
|
|
32
32
|
},
|
|
33
33
|
"context": {
|
|
34
|
-
"assessment": "
|
|
34
|
+
"assessment": "confirmed"
|
|
35
35
|
},
|
|
36
36
|
"expect": {
|
|
37
37
|
"carrier": "allegro-one",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "full_page"
|
|
51
51
|
},
|
|
52
52
|
"context": {
|
|
53
|
-
"assessment": "
|
|
53
|
+
"assessment": "confirmed"
|
|
54
54
|
},
|
|
55
55
|
"expect": {
|
|
56
56
|
"carrier": "allegro-one",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"access": "full_page"
|
|
70
70
|
},
|
|
71
71
|
"context": {
|
|
72
|
-
"assessment": "
|
|
72
|
+
"assessment": "confirmed"
|
|
73
73
|
},
|
|
74
74
|
"expect": {
|
|
75
75
|
"carrier": "allegro-one",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"access": "full_page"
|
|
89
89
|
},
|
|
90
90
|
"context": {
|
|
91
|
-
"assessment": "
|
|
91
|
+
"assessment": "confirmed"
|
|
92
92
|
},
|
|
93
93
|
"expect": {
|
|
94
94
|
"carrier": "allegro-one",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"access": "full_page"
|
|
108
108
|
},
|
|
109
109
|
"context": {
|
|
110
|
-
"assessment": "
|
|
110
|
+
"assessment": "confirmed"
|
|
111
111
|
},
|
|
112
112
|
"expect": {
|
|
113
113
|
"carrier": "allegro-one",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"access": "full_page"
|
|
127
127
|
},
|
|
128
128
|
"context": {
|
|
129
|
-
"assessment": "
|
|
129
|
+
"assessment": "confirmed"
|
|
130
130
|
},
|
|
131
131
|
"expect": {
|
|
132
132
|
"carrier": "allegro-one",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CarrierResult } from '../../core/result/index.js';
|
|
2
|
+
export declare const PROVIDER = "Allegro One";
|
|
3
|
+
export declare const ALLEGRO_ONE_MAX_BYTES: number;
|
|
4
|
+
/** An Allegro One number; anything else is a number Allegro One does not issue. */
|
|
5
|
+
export declare function normalizeAllegroOneNumber(raw: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* The reply of `GET /one/tracking?packageNo={number}` on Allegro's edge
|
|
8
|
+
* gateway: `{ "status": [...] }`, each scan a UTC instant and a wording. It
|
|
9
|
+
* does not echo the number: the scans are those of the number in the request.
|
|
10
|
+
* An empty or null list is what the tracker shows as "not found". Every A0
|
|
11
|
+
* number seen has scans from its label on, so an empty list is not found for
|
|
12
|
+
* them; every real AD number seen had an empty list, so for AD and the older
|
|
13
|
+
* ALE form it stays inconclusive.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseAllegroOne(payload: unknown, raw: string): CarrierResult;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { normalizeTrackingNumber } from '../../core/detection/index.js';
|
|
2
|
+
import { InvalidInputError, NoHistoryError, NotFoundError, SchemaError } from '../../core/errors/index.js';
|
|
3
|
+
import { classifyWording, languageStageStatus } from '../../core/status/index.js';
|
|
4
|
+
import { explicitOffsetTime } from '../../core/time/index.js';
|
|
5
|
+
import { clean } from '../../core/transport/index.js';
|
|
6
|
+
import { isRecord } from '../../core/types.js';
|
|
7
|
+
import { allegroOneStage } from './status.js';
|
|
8
|
+
export const PROVIDER = 'Allegro One';
|
|
9
|
+
// A parcel's reply is a list of short scans; a few kilobytes.
|
|
10
|
+
export const ALLEGRO_ONE_MAX_BYTES = 256 * 1024;
|
|
11
|
+
// Allegro One's ten-character numbers start A0, Allegro Delivery's start AD,
|
|
12
|
+
// and the older form the tracker still answers is ALE and twelve digits.
|
|
13
|
+
const NUMBER = /^(?:A[0D][0-9A-Z]{8}|ALE\d{12})$/;
|
|
14
|
+
const MAX_ROWS = 300;
|
|
15
|
+
const MAX_EVENTS = 100;
|
|
16
|
+
/** An Allegro One number; anything else is a number Allegro One does not issue. */
|
|
17
|
+
export function normalizeAllegroOneNumber(raw) {
|
|
18
|
+
const number = raw.length <= 64 ? normalizeTrackingNumber(raw) : '';
|
|
19
|
+
if (!NUMBER.test(number)) {
|
|
20
|
+
throw new InvalidInputError(PROVIDER, 'Allegro One numbers are A0 or AD and eight letters and digits, or ALE and twelve digits');
|
|
21
|
+
}
|
|
22
|
+
return number;
|
|
23
|
+
}
|
|
24
|
+
function projectScan(row, index) {
|
|
25
|
+
if (!isRecord(row) || typeof row.eventTimestamp !== 'string' || typeof row.description !== 'string') {
|
|
26
|
+
throw new SchemaError(PROVIDER, 'Allegro One returned an invalid scan');
|
|
27
|
+
}
|
|
28
|
+
const time = explicitOffsetTime(row.eventTimestamp);
|
|
29
|
+
if (!time)
|
|
30
|
+
throw new SchemaError(PROVIDER, 'Allegro One returned a scan without an offset time');
|
|
31
|
+
const description = clean(row.description, 300);
|
|
32
|
+
if (!description)
|
|
33
|
+
throw new SchemaError(PROVIDER, 'Allegro One returned a scan without wording');
|
|
34
|
+
const stage = allegroOneStage(description);
|
|
35
|
+
return {
|
|
36
|
+
event: { time: time.iso, description, ...(stage ? { stage, stage_source: 'carrier_map' } : {}) },
|
|
37
|
+
at: time.timestamp,
|
|
38
|
+
index,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The reply of `GET /one/tracking?packageNo={number}` on Allegro's edge
|
|
43
|
+
* gateway: `{ "status": [...] }`, each scan a UTC instant and a wording. It
|
|
44
|
+
* does not echo the number: the scans are those of the number in the request.
|
|
45
|
+
* An empty or null list is what the tracker shows as "not found". Every A0
|
|
46
|
+
* number seen has scans from its label on, so an empty list is not found for
|
|
47
|
+
* them; every real AD number seen had an empty list, so for AD and the older
|
|
48
|
+
* ALE form it stays inconclusive.
|
|
49
|
+
*/
|
|
50
|
+
export function parseAllegroOne(payload, raw) {
|
|
51
|
+
const number = normalizeAllegroOneNumber(raw);
|
|
52
|
+
if (!isRecord(payload) || !('status' in payload) || (payload.status !== null && !Array.isArray(payload.status))) {
|
|
53
|
+
throw new SchemaError(PROVIDER, 'Allegro One returned an unexpected reply');
|
|
54
|
+
}
|
|
55
|
+
const rows = (payload.status ?? []);
|
|
56
|
+
if (rows.length > MAX_ROWS)
|
|
57
|
+
throw new SchemaError(PROVIDER, 'Allegro One returned an oversized history');
|
|
58
|
+
if (!rows.length) {
|
|
59
|
+
if (number.startsWith('A0'))
|
|
60
|
+
throw new NotFoundError(PROVIDER);
|
|
61
|
+
throw new NoHistoryError(PROVIDER, 'Allegro One has no scans for this number');
|
|
62
|
+
}
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
const scans = [];
|
|
65
|
+
rows.forEach((row, index) => {
|
|
66
|
+
const scan = projectScan(row, index);
|
|
67
|
+
const key = `${scan.event.time}\n${scan.event.description}`;
|
|
68
|
+
if (seen.has(key))
|
|
69
|
+
return;
|
|
70
|
+
seen.add(key);
|
|
71
|
+
scans.push(scan);
|
|
72
|
+
});
|
|
73
|
+
// The reply lists scans newest first and the tracker sorts them so too.
|
|
74
|
+
// Instants order them; equal ones keep the reply's order.
|
|
75
|
+
scans.sort((left, right) => right.at - left.at || left.index - right.index);
|
|
76
|
+
const newest = scans[0].event;
|
|
77
|
+
const current = newest.stage
|
|
78
|
+
? { stage: newest.stage, source: 'carrier_map' }
|
|
79
|
+
: classifyWording(newest.description, 'in_transit');
|
|
80
|
+
const stage = current.source !== 'none' ? current.stage : undefined;
|
|
81
|
+
return {
|
|
82
|
+
status: stage ? languageStageStatus(stage) : 'unknown',
|
|
83
|
+
...(stage ? { current_stage: stage, current_stage_source: current.source } : {}),
|
|
84
|
+
last_status_text: newest.description,
|
|
85
|
+
last_update: newest.time,
|
|
86
|
+
expected_delivery: null,
|
|
87
|
+
...(stage === 'delivered' ? { delivered_at: newest.time } : {}),
|
|
88
|
+
events: scans.slice(0, MAX_EVENTS).map((scan) => scan.event),
|
|
89
|
+
...(scans.length > MAX_EVENTS ? { history_truncated: true } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|