universal-parcel-scraper 0.3.2-main.314 → 0.3.4-main.317
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 +4 -4
- package/data/coverage-summary.json +6 -6
- package/dist/browser/scraper.js +256 -112
- package/dist/carriers/delhivery/parser.js +21 -9
- package/dist/carriers/delhivery/status.js +2 -0
- package/dist/carriers/delhivery/statuses.json +6 -0
- package/dist/carriers/packeta/adapter.js +22 -8
- package/dist/providers/coverage.json +139 -18
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The engine behind [Peek](https://peektracker.com) ([GitHub](https://github.com/p
|
|
|
15
15
|
<!-- GENERATED:summary -->
|
|
16
16
|
**3,500+ carriers** through **85 dedicated adapters** and **5 universal fallbacks**
|
|
17
17
|
|
|
18
|
-
<sub>105
|
|
18
|
+
<sub>105 catalog entries · 58 countries represented</sub>
|
|
19
19
|
<!-- /GENERATED:summary -->
|
|
20
20
|
|
|
21
21
|
[Try it](#try-it) · [Ways to run it](#ways-to-run-it) · [Benchmark](#benchmark) · [Carriers](carriers/) · [Add a carrier](CONTRIBUTING.md)
|
|
@@ -33,9 +33,9 @@ its own as a command, a Node library or an HTTP server.
|
|
|
33
33
|
## Benchmark
|
|
34
34
|
|
|
35
35
|
<!-- GENERATED:coverage -->
|
|
36
|
-
|
|
36
|
+
In a curated benchmark of 100 carriers, dedicated adapters return tracking history for **62**, rising to **75** with all fallbacks enabled. The best single aggregator returns 52.
|
|
37
37
|
|
|
38
|
-
<img src="docs/assets/coverage.svg" alt="Carriers with tracking history: This project, all fallbacks enabled
|
|
38
|
+
<img src="docs/assets/coverage.svg" alt="Carriers with tracking history: This project, all fallbacks enabled 75, This project, dedicated adapters alone 62, ParcelsApp 52, Postal Ninja 43, 17TRACK 40, Ship24 41, UPU 12." width="760">
|
|
39
39
|
<!-- /GENERATED:coverage -->
|
|
40
40
|
|
|
41
41
|
[Method and results per carrier](providers/COVERAGE.md)
|
|
@@ -149,7 +149,7 @@ createTracker({ providers: ['ParcelsApp', 'Ship24', '17TRACK', 'Postal Ninja', '
|
|
|
149
149
|
<!-- GENERATED:stages -->
|
|
150
150
|
<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">
|
|
151
151
|
|
|
152
|
-
The carrier folders record 1,
|
|
152
|
+
The carrier folders record 1,673 such statuses, each filed under one stage.
|
|
153
153
|
<!-- /GENERATED:stages -->
|
|
154
154
|
|
|
155
155
|
Wording nobody recorded yet goes through a classifier that reads seven European languages.
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
"countries": 58,
|
|
5
5
|
"referenceCarriers": 100,
|
|
6
6
|
"comparison": {
|
|
7
|
-
"combined":
|
|
8
|
-
"direct":
|
|
7
|
+
"combined": 75,
|
|
8
|
+
"direct": 62,
|
|
9
9
|
"providers": {
|
|
10
|
-
"ParcelsApp":
|
|
11
|
-
"Postal Ninja":
|
|
10
|
+
"ParcelsApp": 52,
|
|
11
|
+
"Postal Ninja": 43,
|
|
12
12
|
"17TRACK": 40,
|
|
13
|
-
"Ship24":
|
|
14
|
-
"UPU":
|
|
13
|
+
"Ship24": 41,
|
|
14
|
+
"UPU": 12
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
}
|
package/dist/browser/scraper.js
CHANGED
|
@@ -8111,20 +8111,36 @@ var fs = { carriers: [
|
|
|
8111
8111
|
{
|
|
8112
8112
|
carrier: "bpost",
|
|
8113
8113
|
direct: "Yes",
|
|
8114
|
-
sample: "
|
|
8115
|
-
references: [
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8114
|
+
sample: "✓ 11",
|
|
8115
|
+
references: [
|
|
8116
|
+
{
|
|
8117
|
+
note: "A domestic parcel",
|
|
8118
|
+
results: {
|
|
8119
|
+
Ship24: "no_history",
|
|
8120
|
+
ParcelsApp: "postcode_prompt",
|
|
8121
|
+
"17TRACK": "no_history",
|
|
8122
|
+
"Postal Ninja": "no_history",
|
|
8123
|
+
UPU: "n/a"
|
|
8124
|
+
}
|
|
8125
|
+
},
|
|
8126
|
+
{
|
|
8127
|
+
results: {
|
|
8128
|
+
Ship24: "no_history",
|
|
8129
|
+
ParcelsApp: "postcode_prompt",
|
|
8130
|
+
"17TRACK": "no_history",
|
|
8131
|
+
"Postal Ninja": "no_history",
|
|
8132
|
+
UPU: "n/a"
|
|
8133
|
+
},
|
|
8134
|
+
note: "Another comparison reference"
|
|
8135
|
+
},
|
|
8136
|
+
{
|
|
8137
|
+
note: "An S10 item through Landmark Global",
|
|
8138
|
+
results: {
|
|
8139
|
+
ParcelsApp: 20,
|
|
8140
|
+
"17TRACK": 14
|
|
8141
|
+
}
|
|
8126
8142
|
}
|
|
8127
|
-
|
|
8143
|
+
]
|
|
8128
8144
|
},
|
|
8129
8145
|
{
|
|
8130
8146
|
carrier: "austrian-post",
|
|
@@ -8171,25 +8187,41 @@ var fs = { carriers: [
|
|
|
8171
8187
|
{
|
|
8172
8188
|
carrier: "aramex",
|
|
8173
8189
|
direct: "Yes",
|
|
8174
|
-
sample: "
|
|
8175
|
-
references: [
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8190
|
+
sample: "✓ 16",
|
|
8191
|
+
references: [
|
|
8192
|
+
{
|
|
8193
|
+
note: "A domestic shipment",
|
|
8194
|
+
results: {
|
|
8195
|
+
Ship24: "no_history",
|
|
8196
|
+
ParcelsApp: "no_history",
|
|
8197
|
+
"17TRACK": "no_history",
|
|
8198
|
+
"Postal Ninja": 16,
|
|
8199
|
+
UPU: "n/a"
|
|
8200
|
+
}
|
|
8182
8201
|
},
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8202
|
+
{
|
|
8203
|
+
results: {
|
|
8204
|
+
Ship24: "no_history",
|
|
8205
|
+
ParcelsApp: "no_history",
|
|
8206
|
+
"17TRACK": "no_history",
|
|
8207
|
+
"Postal Ninja": {
|
|
8208
|
+
rows: 16,
|
|
8209
|
+
partial: "undated rows"
|
|
8210
|
+
},
|
|
8211
|
+
UPU: "n/a"
|
|
8212
|
+
},
|
|
8213
|
+
note: "Another comparison reference"
|
|
8214
|
+
},
|
|
8215
|
+
{
|
|
8216
|
+
note: "An international shipment",
|
|
8217
|
+
results: {
|
|
8218
|
+
Ship24: 26,
|
|
8219
|
+
ParcelsApp: "no_history",
|
|
8220
|
+
"17TRACK": 26,
|
|
8221
|
+
"Postal Ninja": 26
|
|
8222
|
+
}
|
|
8191
8223
|
}
|
|
8192
|
-
|
|
8224
|
+
]
|
|
8193
8225
|
},
|
|
8194
8226
|
{
|
|
8195
8227
|
carrier: "yunexpress",
|
|
@@ -8317,14 +8349,26 @@ var fs = { carriers: [
|
|
|
8317
8349
|
{
|
|
8318
8350
|
carrier: "correios-br",
|
|
8319
8351
|
direct: "Yes (local OCR)",
|
|
8320
|
-
sample: "
|
|
8321
|
-
references: [{
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8352
|
+
sample: "✓ 8",
|
|
8353
|
+
references: [{
|
|
8354
|
+
note: "A Brazilian postal item",
|
|
8355
|
+
results: {
|
|
8356
|
+
Ship24: 8,
|
|
8357
|
+
ParcelsApp: "no_history",
|
|
8358
|
+
"17TRACK": 8,
|
|
8359
|
+
"Postal Ninja": "error",
|
|
8360
|
+
UPU: "no_history"
|
|
8361
|
+
}
|
|
8362
|
+
}, {
|
|
8363
|
+
results: {
|
|
8364
|
+
Ship24: 8,
|
|
8365
|
+
ParcelsApp: "no_history",
|
|
8366
|
+
"17TRACK": 8,
|
|
8367
|
+
"Postal Ninja": 8,
|
|
8368
|
+
UPU: "no_history"
|
|
8369
|
+
},
|
|
8370
|
+
note: "Another comparison reference"
|
|
8371
|
+
}]
|
|
8328
8372
|
},
|
|
8329
8373
|
{
|
|
8330
8374
|
carrier: "singapore-post",
|
|
@@ -8359,23 +8403,39 @@ var fs = { carriers: [
|
|
|
8359
8403
|
{
|
|
8360
8404
|
carrier: "korea-post",
|
|
8361
8405
|
direct: "Yes (international)",
|
|
8362
|
-
sample: "
|
|
8363
|
-
references: [
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8406
|
+
sample: "✓ 20",
|
|
8407
|
+
references: [
|
|
8408
|
+
{
|
|
8409
|
+
note: "An EMS item",
|
|
8410
|
+
results: {
|
|
8411
|
+
Ship24: 36,
|
|
8412
|
+
ParcelsApp: 41,
|
|
8413
|
+
"17TRACK": 28,
|
|
8414
|
+
"Postal Ninja": 27,
|
|
8415
|
+
UPU: 1
|
|
8416
|
+
}
|
|
8417
|
+
},
|
|
8418
|
+
{
|
|
8419
|
+
results: {
|
|
8420
|
+
Ship24: "no_history",
|
|
8421
|
+
ParcelsApp: "no_history",
|
|
8422
|
+
"17TRACK": "no_history",
|
|
8423
|
+
"Postal Ninja": "no_history",
|
|
8424
|
+
UPU: "n/a"
|
|
8425
|
+
},
|
|
8426
|
+
note: "Another comparison reference"
|
|
8427
|
+
},
|
|
8428
|
+
{
|
|
8429
|
+
note: "An EMS item",
|
|
8430
|
+
results: {
|
|
8431
|
+
Ship24: "history",
|
|
8432
|
+
ParcelsApp: "history",
|
|
8433
|
+
"17TRACK": "history",
|
|
8434
|
+
"Postal Ninja": "no_history",
|
|
8435
|
+
UPU: "history"
|
|
8436
|
+
}
|
|
8377
8437
|
}
|
|
8378
|
-
|
|
8438
|
+
]
|
|
8379
8439
|
},
|
|
8380
8440
|
{
|
|
8381
8441
|
carrier: "planzer",
|
|
@@ -8461,14 +8521,26 @@ var fs = { carriers: [
|
|
|
8461
8521
|
{
|
|
8462
8522
|
carrier: "delhivery",
|
|
8463
8523
|
direct: "Yes",
|
|
8464
|
-
sample: "
|
|
8465
|
-
references: [{
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8524
|
+
sample: "✓ 2, partial (one undated scan and a status snapshot)",
|
|
8525
|
+
references: [{
|
|
8526
|
+
note: "A domestic parcel",
|
|
8527
|
+
results: {
|
|
8528
|
+
Ship24: "no_history",
|
|
8529
|
+
ParcelsApp: "no_history",
|
|
8530
|
+
"17TRACK": "no_history",
|
|
8531
|
+
"Postal Ninja": "no_history",
|
|
8532
|
+
UPU: "n/a"
|
|
8533
|
+
}
|
|
8534
|
+
}, {
|
|
8535
|
+
results: {
|
|
8536
|
+
Ship24: "no_history",
|
|
8537
|
+
ParcelsApp: "no_history",
|
|
8538
|
+
"17TRACK": "no_history",
|
|
8539
|
+
"Postal Ninja": "no_history",
|
|
8540
|
+
UPU: "n/a"
|
|
8541
|
+
},
|
|
8542
|
+
note: "Another comparison reference"
|
|
8543
|
+
}]
|
|
8472
8544
|
},
|
|
8473
8545
|
{
|
|
8474
8546
|
carrier: "blue-dart",
|
|
@@ -8485,14 +8557,26 @@ var fs = { carriers: [
|
|
|
8485
8557
|
{
|
|
8486
8558
|
carrier: "dtdc",
|
|
8487
8559
|
direct: "Yes",
|
|
8488
|
-
sample: "
|
|
8489
|
-
references: [{
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8560
|
+
sample: "✓ 9",
|
|
8561
|
+
references: [{
|
|
8562
|
+
note: "A domestic parcel",
|
|
8563
|
+
results: {
|
|
8564
|
+
Ship24: "no_history",
|
|
8565
|
+
ParcelsApp: 37,
|
|
8566
|
+
"17TRACK": "no_history",
|
|
8567
|
+
"Postal Ninja": "no_history",
|
|
8568
|
+
UPU: "n/a"
|
|
8569
|
+
}
|
|
8570
|
+
}, {
|
|
8571
|
+
results: {
|
|
8572
|
+
Ship24: "no_history",
|
|
8573
|
+
ParcelsApp: 35,
|
|
8574
|
+
"17TRACK": "no_history",
|
|
8575
|
+
"Postal Ninja": "refused",
|
|
8576
|
+
UPU: "n/a"
|
|
8577
|
+
},
|
|
8578
|
+
note: "Another comparison reference"
|
|
8579
|
+
}]
|
|
8496
8580
|
},
|
|
8497
8581
|
{
|
|
8498
8582
|
carrier: "ninja-van",
|
|
@@ -8509,8 +8593,17 @@ var fs = { carriers: [
|
|
|
8509
8593
|
{
|
|
8510
8594
|
carrier: "packeta",
|
|
8511
8595
|
direct: "Yes",
|
|
8512
|
-
sample: "
|
|
8596
|
+
sample: "✓ 6",
|
|
8513
8597
|
references: [{
|
|
8598
|
+
note: "A parcel handed to a delivery partner",
|
|
8599
|
+
results: {
|
|
8600
|
+
Ship24: 11,
|
|
8601
|
+
ParcelsApp: "no_history",
|
|
8602
|
+
"17TRACK": "no_history",
|
|
8603
|
+
"Postal Ninja": 12,
|
|
8604
|
+
UPU: "n/a"
|
|
8605
|
+
}
|
|
8606
|
+
}, {
|
|
8514
8607
|
stale: !0,
|
|
8515
8608
|
results: {
|
|
8516
8609
|
Ship24: "no_history",
|
|
@@ -8518,7 +8611,8 @@ var fs = { carriers: [
|
|
|
8518
8611
|
"17TRACK": "no_history",
|
|
8519
8612
|
"Postal Ninja": "no_history",
|
|
8520
8613
|
UPU: "n/a"
|
|
8521
|
-
}
|
|
8614
|
+
},
|
|
8615
|
+
note: "Another comparison reference"
|
|
8522
8616
|
}]
|
|
8523
8617
|
},
|
|
8524
8618
|
{
|
|
@@ -8542,14 +8636,26 @@ var fs = { carriers: [
|
|
|
8542
8636
|
{
|
|
8543
8637
|
carrier: "bring-posten",
|
|
8544
8638
|
direct: "Yes",
|
|
8545
|
-
sample: "
|
|
8546
|
-
references: [{
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8639
|
+
sample: "✓ 12",
|
|
8640
|
+
references: [{
|
|
8641
|
+
note: "A Norwegian postal item",
|
|
8642
|
+
results: {
|
|
8643
|
+
Ship24: 12,
|
|
8644
|
+
ParcelsApp: 15,
|
|
8645
|
+
"17TRACK": 19,
|
|
8646
|
+
"Postal Ninja": 16,
|
|
8647
|
+
UPU: 3
|
|
8648
|
+
}
|
|
8649
|
+
}, {
|
|
8650
|
+
results: {
|
|
8651
|
+
Ship24: 12,
|
|
8652
|
+
ParcelsApp: 15,
|
|
8653
|
+
"17TRACK": 19,
|
|
8654
|
+
"Postal Ninja": 16,
|
|
8655
|
+
UPU: 3
|
|
8656
|
+
},
|
|
8657
|
+
note: "Another comparison reference"
|
|
8658
|
+
}]
|
|
8553
8659
|
},
|
|
8554
8660
|
{
|
|
8555
8661
|
carrier: "posti",
|
|
@@ -8602,14 +8708,26 @@ var fs = { carriers: [
|
|
|
8602
8708
|
{
|
|
8603
8709
|
carrier: "brt",
|
|
8604
8710
|
direct: "Yes",
|
|
8605
|
-
sample: "
|
|
8606
|
-
references: [{
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8711
|
+
sample: "✓ 9",
|
|
8712
|
+
references: [{
|
|
8713
|
+
note: "An international parcel",
|
|
8714
|
+
results: {
|
|
8715
|
+
Ship24: "no_history",
|
|
8716
|
+
ParcelsApp: 23,
|
|
8717
|
+
"17TRACK": "no_history",
|
|
8718
|
+
"Postal Ninja": 5,
|
|
8719
|
+
UPU: "n/a"
|
|
8720
|
+
}
|
|
8721
|
+
}, {
|
|
8722
|
+
results: {
|
|
8723
|
+
Ship24: "no_history",
|
|
8724
|
+
ParcelsApp: 23,
|
|
8725
|
+
"17TRACK": "no_history",
|
|
8726
|
+
"Postal Ninja": 5,
|
|
8727
|
+
UPU: "n/a"
|
|
8728
|
+
},
|
|
8729
|
+
note: "Another comparison reference"
|
|
8730
|
+
}]
|
|
8613
8731
|
},
|
|
8614
8732
|
{
|
|
8615
8733
|
carrier: "seur",
|
|
@@ -8638,38 +8756,64 @@ var fs = { carriers: [
|
|
|
8638
8756
|
{
|
|
8639
8757
|
carrier: "mrw",
|
|
8640
8758
|
direct: "Yes",
|
|
8641
|
-
sample: "
|
|
8759
|
+
sample: "✓ 14",
|
|
8760
|
+
references: [
|
|
8761
|
+
{
|
|
8762
|
+
note: "A delivered parcel",
|
|
8763
|
+
results: {
|
|
8764
|
+
Ship24: 14,
|
|
8765
|
+
ParcelsApp: 14,
|
|
8766
|
+
"17TRACK": "no_history",
|
|
8767
|
+
"Postal Ninja": 14,
|
|
8768
|
+
UPU: "n/a"
|
|
8769
|
+
}
|
|
8770
|
+
},
|
|
8771
|
+
{
|
|
8772
|
+
stale: !0,
|
|
8773
|
+
results: {
|
|
8774
|
+
Ship24: "no_history",
|
|
8775
|
+
ParcelsApp: "no_history",
|
|
8776
|
+
"17TRACK": 1,
|
|
8777
|
+
"Postal Ninja": "no_history",
|
|
8778
|
+
UPU: "n/a"
|
|
8779
|
+
},
|
|
8780
|
+
note: "Another comparison reference"
|
|
8781
|
+
},
|
|
8782
|
+
{
|
|
8783
|
+
note: "A September parcel",
|
|
8784
|
+
results: {
|
|
8785
|
+
Ship24: 14,
|
|
8786
|
+
ParcelsApp: 14,
|
|
8787
|
+
"17TRACK": "no_history",
|
|
8788
|
+
"Postal Ninja": 14
|
|
8789
|
+
}
|
|
8790
|
+
}
|
|
8791
|
+
]
|
|
8792
|
+
},
|
|
8793
|
+
{
|
|
8794
|
+
carrier: "nacex",
|
|
8795
|
+
direct: "Yes",
|
|
8796
|
+
sample: "✓ 13",
|
|
8642
8797
|
references: [{
|
|
8643
|
-
|
|
8798
|
+
note: "An agency/shipment reference",
|
|
8644
8799
|
results: {
|
|
8645
|
-
Ship24: "
|
|
8646
|
-
ParcelsApp: "
|
|
8647
|
-
"17TRACK":
|
|
8648
|
-
"Postal Ninja": "
|
|
8800
|
+
Ship24: "refused",
|
|
8801
|
+
ParcelsApp: "refused",
|
|
8802
|
+
"17TRACK": "refused",
|
|
8803
|
+
"Postal Ninja": "refused",
|
|
8649
8804
|
UPU: "n/a"
|
|
8650
8805
|
}
|
|
8651
8806
|
}, {
|
|
8652
|
-
note: "A September parcel",
|
|
8653
8807
|
results: {
|
|
8654
|
-
Ship24:
|
|
8655
|
-
ParcelsApp:
|
|
8656
|
-
"17TRACK": "
|
|
8657
|
-
"Postal Ninja":
|
|
8658
|
-
|
|
8808
|
+
Ship24: "error",
|
|
8809
|
+
ParcelsApp: "error",
|
|
8810
|
+
"17TRACK": "error",
|
|
8811
|
+
"Postal Ninja": "error",
|
|
8812
|
+
UPU: "n/a"
|
|
8813
|
+
},
|
|
8814
|
+
note: "Another comparison reference"
|
|
8659
8815
|
}]
|
|
8660
8816
|
},
|
|
8661
|
-
{
|
|
8662
|
-
carrier: "nacex",
|
|
8663
|
-
direct: "Yes",
|
|
8664
|
-
sample: "Not tested; alternate ✓ 14",
|
|
8665
|
-
references: [{ results: {
|
|
8666
|
-
Ship24: "error",
|
|
8667
|
-
ParcelsApp: "error",
|
|
8668
|
-
"17TRACK": "error",
|
|
8669
|
-
"Postal Ninja": "error",
|
|
8670
|
-
UPU: "n/a"
|
|
8671
|
-
} }]
|
|
8672
|
-
},
|
|
8673
8817
|
{
|
|
8674
8818
|
carrier: "colis-prive",
|
|
8675
8819
|
direct: "Yes (postcode)",
|
|
@@ -22,7 +22,8 @@ export function parseDelhivery(payload, number) {
|
|
|
22
22
|
throw new SchemaError('Delhivery');
|
|
23
23
|
if (!payload.data.length && payload.message === 'invalid AWB or very old package')
|
|
24
24
|
throw new NotFoundError('Delhivery');
|
|
25
|
-
const matches = payload.data.filter(isRecord).filter(item =>
|
|
25
|
+
const matches = payload.data.filter(isRecord).filter(item => (typeof item.awb === 'string' ? item.awb.trim()
|
|
26
|
+
: typeof item.awb === 'number' && Number.isSafeInteger(item.awb) ? String(item.awb) : '') === requested);
|
|
26
27
|
if (matches.length !== 1)
|
|
27
28
|
throw new SchemaError('Delhivery', 'Delhivery did not return one matching shipment');
|
|
28
29
|
const item = matches[0];
|
|
@@ -31,7 +32,14 @@ export function parseDelhivery(payload, number) {
|
|
|
31
32
|
const description = clean(item.status.status, 200);
|
|
32
33
|
if (!description)
|
|
33
34
|
throw new IndeterminateError('Delhivery', 'Delhivery returned no current status');
|
|
34
|
-
const
|
|
35
|
+
const returning = item.status.statusType === 'RT' || item.currentFlow === 'Returned' || item.currentFlow === 'Reverse';
|
|
36
|
+
// RETURNED names a flow, including parcels still travelling to the sender.
|
|
37
|
+
// The separate shipment status establishes movement or completed delivery.
|
|
38
|
+
const snapshotDescription = returning && description.toUpperCase() === 'RETURNED'
|
|
39
|
+
? clean(item.hqStatus, 200) : description;
|
|
40
|
+
const current = classifyDelhiveryStatus(snapshotDescription);
|
|
41
|
+
const mapped = returning && current?.stage === 'delivered'
|
|
42
|
+
? classifyDelhiveryStatus('RTO DELIVERED') : current;
|
|
35
43
|
const updated = scanTime(item.status.statusDateTime);
|
|
36
44
|
const currentTimeText = clean(item.status.statusDateTime, 64);
|
|
37
45
|
const events = [];
|
|
@@ -56,21 +64,25 @@ export function parseDelhivery(payload, number) {
|
|
|
56
64
|
const time = scanTime(scan.scanDateTime);
|
|
57
65
|
const timeText = clean(scan.scanDateTime, 64);
|
|
58
66
|
const location = clean(scan.cityLocation, 160);
|
|
59
|
-
const
|
|
67
|
+
const returnScan = scan.scanType === 'RT' || item.currentFlow === 'Reverse';
|
|
68
|
+
const key = `${time?.timestamp ?? timeText}\u0000${label}\u0000${location}\u0000${returnScan ? 'return' : ''}`;
|
|
60
69
|
if (seen.has(key))
|
|
61
70
|
continue;
|
|
62
71
|
seen.add(key);
|
|
63
|
-
const
|
|
72
|
+
const scanStatus = classifyDelhiveryStatus(label);
|
|
73
|
+
const classified = returnScan && scanStatus?.stage === 'delivered'
|
|
74
|
+
? classifyDelhiveryStatus('RTO DELIVERED') : scanStatus;
|
|
64
75
|
events.push({ description: label, ...(time ? { time: time.iso } : timeText ? { provider_time_text: timeText } : {}), ...(location ? { location } : {}),
|
|
65
|
-
...(classified ? { stage: classified.stage } : {}) });
|
|
76
|
+
...(classified ? { stage: classified.stage } : {}), ...(returnScan ? { provider_leg: 'return' } : {}) });
|
|
66
77
|
}
|
|
67
78
|
}
|
|
68
79
|
// Repeated scan labels cannot bind a summary timestamp to a particular
|
|
69
80
|
// historical location. Keep the dated status snapshot separate.
|
|
70
|
-
if (!updated || !events.some(event => event.time && Date.parse(event.time) === updated.timestamp
|
|
71
|
-
&& event.description?.toUpperCase() ===
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
if (snapshotDescription && (!updated || !events.some(event => event.time && Date.parse(event.time) === updated.timestamp
|
|
82
|
+
&& event.description?.toUpperCase() === snapshotDescription.toUpperCase() && event.stage === mapped?.stage
|
|
83
|
+
&& (!returning || event.provider_leg === 'return')))) {
|
|
84
|
+
events.unshift({ description: snapshotDescription, ...(updated ? { time: updated.iso } : currentTimeText ? { provider_time_text: currentTimeText } : {}),
|
|
85
|
+
...(mapped ? { stage: mapped.stage } : {}), ...(returning ? { provider_leg: 'return' } : {}), summary_snapshot: true });
|
|
74
86
|
}
|
|
75
87
|
const dated = events.filter(event => event.time).sort((a, b) => Date.parse(b.time) - Date.parse(a.time));
|
|
76
88
|
const undated = events.filter(event => !event.time);
|
|
@@ -2,9 +2,11 @@ const statuses = {
|
|
|
2
2
|
DELIVERED: { status: 'delivered', stage: 'delivered' },
|
|
3
3
|
'OUT FOR DELIVERY': { status: 'out_for_delivery', stage: 'out_for_delivery' },
|
|
4
4
|
'IN TRANSIT': { status: 'in_transit', stage: 'in_transit' },
|
|
5
|
+
'IN TRANSIT FOR RETURN': { status: 'in_transit', stage: 'in_transit' },
|
|
5
6
|
'PICKED UP': { status: 'in_transit', stage: 'accepted' },
|
|
6
7
|
MANIFESTED: { status: 'pending', stage: 'registered' },
|
|
7
8
|
'RTO DELIVERED': { status: 'exception', stage: 'returned' },
|
|
9
|
+
DELIVERED_SELLER: { status: 'exception', stage: 'returned' },
|
|
8
10
|
};
|
|
9
11
|
export function classifyDelhiveryStatus(value) {
|
|
10
12
|
return statuses[value.toUpperCase()];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { lookupBudget } from '../../core/adapter/index.js';
|
|
2
|
-
import { InvalidInputError, NotFoundError, SchemaError } from '../../core/errors/index.js';
|
|
2
|
+
import { IndeterminateError, InvalidInputError, NotFoundError, SchemaError } from '../../core/errors/index.js';
|
|
3
3
|
import { zonedTime } from '../../core/time/index.js';
|
|
4
4
|
import { clean, fetchBounded, parseJsonBytes, UpstreamHttpError, userAgentOf } from '../../core/transport/index.js';
|
|
5
5
|
import { isRecord } from '../../core/types.js';
|
|
@@ -52,18 +52,22 @@ export function parsePacketaTrackingResponse(payload, trackingNumber) {
|
|
|
52
52
|
if (!isRecord(payload))
|
|
53
53
|
throw new SchemaError('Packeta', 'Packeta returned an invalid tracking response');
|
|
54
54
|
const item = payload.item;
|
|
55
|
-
//
|
|
56
|
-
//
|
|
55
|
+
// Only the named notFound error establishes absence; other API errors say
|
|
56
|
+
// nothing about the requested parcel.
|
|
57
57
|
if (!isRecord(item)) {
|
|
58
|
-
if (
|
|
58
|
+
if (payload.error === 'notFound')
|
|
59
59
|
throw new NotFoundError('Packeta');
|
|
60
|
+
if (typeof payload.error === 'string')
|
|
61
|
+
throw new IndeterminateError('Packeta', 'Packeta returned a tracking error');
|
|
60
62
|
throw new SchemaError('Packeta', 'Packeta returned an invalid tracking response');
|
|
61
63
|
}
|
|
62
|
-
const returned =
|
|
64
|
+
const returned = typeof item.barcode === 'string'
|
|
65
|
+
? item.barcode.toLocaleUpperCase('en-US').replace(/[\s.-]/g, '') : '';
|
|
63
66
|
if (!returned)
|
|
64
67
|
throw new SchemaError('Packeta', 'Packeta did not return a shipment identifier');
|
|
65
|
-
if (returned !== requested)
|
|
68
|
+
if (returned !== requested && returned !== requested.slice(1)) {
|
|
66
69
|
throw new SchemaError('Packeta', 'Packeta returned a different shipment');
|
|
70
|
+
}
|
|
67
71
|
const code = clean(item.packetStatusId, 16);
|
|
68
72
|
const classified = classifyPacketaStatus(code);
|
|
69
73
|
const rawDetails = item.trackingDetails;
|
|
@@ -157,8 +161,18 @@ export class PacketaTracker {
|
|
|
157
161
|
allowHttpError: true,
|
|
158
162
|
fetcher: this.fetcher,
|
|
159
163
|
});
|
|
160
|
-
if (response.status === 404)
|
|
161
|
-
|
|
164
|
+
if (response.status === 404) {
|
|
165
|
+
let payload;
|
|
166
|
+
try {
|
|
167
|
+
payload = parseJsonBytes(bytes, 'Packeta tracking');
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
throw new UpstreamHttpError('Packeta tracking', response.status);
|
|
171
|
+
}
|
|
172
|
+
if (isRecord(payload) && payload.error === 'notFound')
|
|
173
|
+
throw new NotFoundError('Packeta');
|
|
174
|
+
throw new UpstreamHttpError('Packeta tracking', response.status);
|
|
175
|
+
}
|
|
162
176
|
if (!response.ok)
|
|
163
177
|
throw new UpstreamHttpError('Packeta tracking', response.status);
|
|
164
178
|
return parsePacketaTrackingResponse(parseJsonBytes(bytes, 'Packeta tracking'), trackingNumber);
|
|
@@ -531,9 +531,10 @@
|
|
|
531
531
|
{
|
|
532
532
|
"carrier": "bpost",
|
|
533
533
|
"direct": "Yes",
|
|
534
|
-
"sample": "
|
|
534
|
+
"sample": "✓ 11",
|
|
535
535
|
"references": [
|
|
536
536
|
{
|
|
537
|
+
"note": "A domestic parcel",
|
|
537
538
|
"results": {
|
|
538
539
|
"Ship24": "no_history",
|
|
539
540
|
"ParcelsApp": "postcode_prompt",
|
|
@@ -542,6 +543,16 @@
|
|
|
542
543
|
"UPU": "n/a"
|
|
543
544
|
}
|
|
544
545
|
},
|
|
546
|
+
{
|
|
547
|
+
"results": {
|
|
548
|
+
"Ship24": "no_history",
|
|
549
|
+
"ParcelsApp": "postcode_prompt",
|
|
550
|
+
"17TRACK": "no_history",
|
|
551
|
+
"Postal Ninja": "no_history",
|
|
552
|
+
"UPU": "n/a"
|
|
553
|
+
},
|
|
554
|
+
"note": "Another comparison reference"
|
|
555
|
+
},
|
|
545
556
|
{
|
|
546
557
|
"note": "An S10 item through Landmark Global",
|
|
547
558
|
"results": {
|
|
@@ -608,8 +619,18 @@
|
|
|
608
619
|
{
|
|
609
620
|
"carrier": "aramex",
|
|
610
621
|
"direct": "Yes",
|
|
611
|
-
"sample": "
|
|
622
|
+
"sample": "✓ 16",
|
|
612
623
|
"references": [
|
|
624
|
+
{
|
|
625
|
+
"note": "A domestic shipment",
|
|
626
|
+
"results": {
|
|
627
|
+
"Ship24": "no_history",
|
|
628
|
+
"ParcelsApp": "no_history",
|
|
629
|
+
"17TRACK": "no_history",
|
|
630
|
+
"Postal Ninja": 16,
|
|
631
|
+
"UPU": "n/a"
|
|
632
|
+
}
|
|
633
|
+
},
|
|
613
634
|
{
|
|
614
635
|
"results": {
|
|
615
636
|
"Ship24": "no_history",
|
|
@@ -620,7 +641,8 @@
|
|
|
620
641
|
"partial": "undated rows"
|
|
621
642
|
},
|
|
622
643
|
"UPU": "n/a"
|
|
623
|
-
}
|
|
644
|
+
},
|
|
645
|
+
"note": "Another comparison reference"
|
|
624
646
|
},
|
|
625
647
|
{
|
|
626
648
|
"note": "An international shipment",
|
|
@@ -799,16 +821,27 @@
|
|
|
799
821
|
{
|
|
800
822
|
"carrier": "correios-br",
|
|
801
823
|
"direct": "Yes (local OCR)",
|
|
802
|
-
"sample": "
|
|
824
|
+
"sample": "✓ 8",
|
|
803
825
|
"references": [
|
|
804
826
|
{
|
|
827
|
+
"note": "A Brazilian postal item",
|
|
805
828
|
"results": {
|
|
806
829
|
"Ship24": 8,
|
|
807
830
|
"ParcelsApp": "no_history",
|
|
808
831
|
"17TRACK": 8,
|
|
809
|
-
"Postal Ninja":
|
|
832
|
+
"Postal Ninja": "error",
|
|
810
833
|
"UPU": "no_history"
|
|
811
834
|
}
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
"results": {
|
|
838
|
+
"Ship24": 8,
|
|
839
|
+
"ParcelsApp": "no_history",
|
|
840
|
+
"17TRACK": 8,
|
|
841
|
+
"Postal Ninja": 8,
|
|
842
|
+
"UPU": "no_history"
|
|
843
|
+
},
|
|
844
|
+
"note": "Another comparison reference"
|
|
812
845
|
}
|
|
813
846
|
]
|
|
814
847
|
},
|
|
@@ -853,8 +886,18 @@
|
|
|
853
886
|
{
|
|
854
887
|
"carrier": "korea-post",
|
|
855
888
|
"direct": "Yes (international)",
|
|
856
|
-
"sample": "
|
|
889
|
+
"sample": "✓ 20",
|
|
857
890
|
"references": [
|
|
891
|
+
{
|
|
892
|
+
"note": "An EMS item",
|
|
893
|
+
"results": {
|
|
894
|
+
"Ship24": 36,
|
|
895
|
+
"ParcelsApp": 41,
|
|
896
|
+
"17TRACK": 28,
|
|
897
|
+
"Postal Ninja": 27,
|
|
898
|
+
"UPU": 1
|
|
899
|
+
}
|
|
900
|
+
},
|
|
858
901
|
{
|
|
859
902
|
"results": {
|
|
860
903
|
"Ship24": "no_history",
|
|
@@ -862,7 +905,8 @@
|
|
|
862
905
|
"17TRACK": "no_history",
|
|
863
906
|
"Postal Ninja": "no_history",
|
|
864
907
|
"UPU": "n/a"
|
|
865
|
-
}
|
|
908
|
+
},
|
|
909
|
+
"note": "Another comparison reference"
|
|
866
910
|
},
|
|
867
911
|
{
|
|
868
912
|
"note": "An EMS item",
|
|
@@ -982,9 +1026,10 @@
|
|
|
982
1026
|
{
|
|
983
1027
|
"carrier": "delhivery",
|
|
984
1028
|
"direct": "Yes",
|
|
985
|
-
"sample": "
|
|
1029
|
+
"sample": "✓ 2, partial (one undated scan and a status snapshot)",
|
|
986
1030
|
"references": [
|
|
987
1031
|
{
|
|
1032
|
+
"note": "A domestic parcel",
|
|
988
1033
|
"results": {
|
|
989
1034
|
"Ship24": "no_history",
|
|
990
1035
|
"ParcelsApp": "no_history",
|
|
@@ -992,6 +1037,16 @@
|
|
|
992
1037
|
"Postal Ninja": "no_history",
|
|
993
1038
|
"UPU": "n/a"
|
|
994
1039
|
}
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
"results": {
|
|
1043
|
+
"Ship24": "no_history",
|
|
1044
|
+
"ParcelsApp": "no_history",
|
|
1045
|
+
"17TRACK": "no_history",
|
|
1046
|
+
"Postal Ninja": "no_history",
|
|
1047
|
+
"UPU": "n/a"
|
|
1048
|
+
},
|
|
1049
|
+
"note": "Another comparison reference"
|
|
995
1050
|
}
|
|
996
1051
|
]
|
|
997
1052
|
},
|
|
@@ -1014,8 +1069,18 @@
|
|
|
1014
1069
|
{
|
|
1015
1070
|
"carrier": "dtdc",
|
|
1016
1071
|
"direct": "Yes",
|
|
1017
|
-
"sample": "
|
|
1072
|
+
"sample": "✓ 9",
|
|
1018
1073
|
"references": [
|
|
1074
|
+
{
|
|
1075
|
+
"note": "A domestic parcel",
|
|
1076
|
+
"results": {
|
|
1077
|
+
"Ship24": "no_history",
|
|
1078
|
+
"ParcelsApp": 37,
|
|
1079
|
+
"17TRACK": "no_history",
|
|
1080
|
+
"Postal Ninja": "no_history",
|
|
1081
|
+
"UPU": "n/a"
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1019
1084
|
{
|
|
1020
1085
|
"results": {
|
|
1021
1086
|
"Ship24": "no_history",
|
|
@@ -1023,7 +1088,8 @@
|
|
|
1023
1088
|
"17TRACK": "no_history",
|
|
1024
1089
|
"Postal Ninja": "refused",
|
|
1025
1090
|
"UPU": "n/a"
|
|
1026
|
-
}
|
|
1091
|
+
},
|
|
1092
|
+
"note": "Another comparison reference"
|
|
1027
1093
|
}
|
|
1028
1094
|
]
|
|
1029
1095
|
},
|
|
@@ -1046,8 +1112,18 @@
|
|
|
1046
1112
|
{
|
|
1047
1113
|
"carrier": "packeta",
|
|
1048
1114
|
"direct": "Yes",
|
|
1049
|
-
"sample": "
|
|
1115
|
+
"sample": "✓ 6",
|
|
1050
1116
|
"references": [
|
|
1117
|
+
{
|
|
1118
|
+
"note": "A parcel handed to a delivery partner",
|
|
1119
|
+
"results": {
|
|
1120
|
+
"Ship24": 11,
|
|
1121
|
+
"ParcelsApp": "no_history",
|
|
1122
|
+
"17TRACK": "no_history",
|
|
1123
|
+
"Postal Ninja": 12,
|
|
1124
|
+
"UPU": "n/a"
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1051
1127
|
{
|
|
1052
1128
|
"stale": true,
|
|
1053
1129
|
"results": {
|
|
@@ -1056,7 +1132,8 @@
|
|
|
1056
1132
|
"17TRACK": "no_history",
|
|
1057
1133
|
"Postal Ninja": "no_history",
|
|
1058
1134
|
"UPU": "n/a"
|
|
1059
|
-
}
|
|
1135
|
+
},
|
|
1136
|
+
"note": "Another comparison reference"
|
|
1060
1137
|
}
|
|
1061
1138
|
]
|
|
1062
1139
|
},
|
|
@@ -1086,9 +1163,10 @@
|
|
|
1086
1163
|
{
|
|
1087
1164
|
"carrier": "bring-posten",
|
|
1088
1165
|
"direct": "Yes",
|
|
1089
|
-
"sample": "
|
|
1166
|
+
"sample": "✓ 12",
|
|
1090
1167
|
"references": [
|
|
1091
1168
|
{
|
|
1169
|
+
"note": "A Norwegian postal item",
|
|
1092
1170
|
"results": {
|
|
1093
1171
|
"Ship24": 12,
|
|
1094
1172
|
"ParcelsApp": 15,
|
|
@@ -1096,6 +1174,16 @@
|
|
|
1096
1174
|
"Postal Ninja": 16,
|
|
1097
1175
|
"UPU": 3
|
|
1098
1176
|
}
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
"results": {
|
|
1180
|
+
"Ship24": 12,
|
|
1181
|
+
"ParcelsApp": 15,
|
|
1182
|
+
"17TRACK": 19,
|
|
1183
|
+
"Postal Ninja": 16,
|
|
1184
|
+
"UPU": 3
|
|
1185
|
+
},
|
|
1186
|
+
"note": "Another comparison reference"
|
|
1099
1187
|
}
|
|
1100
1188
|
]
|
|
1101
1189
|
},
|
|
@@ -1166,9 +1254,10 @@
|
|
|
1166
1254
|
{
|
|
1167
1255
|
"carrier": "brt",
|
|
1168
1256
|
"direct": "Yes",
|
|
1169
|
-
"sample": "
|
|
1257
|
+
"sample": "✓ 9",
|
|
1170
1258
|
"references": [
|
|
1171
1259
|
{
|
|
1260
|
+
"note": "An international parcel",
|
|
1172
1261
|
"results": {
|
|
1173
1262
|
"Ship24": "no_history",
|
|
1174
1263
|
"ParcelsApp": 23,
|
|
@@ -1176,6 +1265,16 @@
|
|
|
1176
1265
|
"Postal Ninja": 5,
|
|
1177
1266
|
"UPU": "n/a"
|
|
1178
1267
|
}
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
"results": {
|
|
1271
|
+
"Ship24": "no_history",
|
|
1272
|
+
"ParcelsApp": 23,
|
|
1273
|
+
"17TRACK": "no_history",
|
|
1274
|
+
"Postal Ninja": 5,
|
|
1275
|
+
"UPU": "n/a"
|
|
1276
|
+
},
|
|
1277
|
+
"note": "Another comparison reference"
|
|
1179
1278
|
}
|
|
1180
1279
|
]
|
|
1181
1280
|
},
|
|
@@ -1214,8 +1313,18 @@
|
|
|
1214
1313
|
{
|
|
1215
1314
|
"carrier": "mrw",
|
|
1216
1315
|
"direct": "Yes",
|
|
1217
|
-
"sample": "
|
|
1316
|
+
"sample": "✓ 14",
|
|
1218
1317
|
"references": [
|
|
1318
|
+
{
|
|
1319
|
+
"note": "A delivered parcel",
|
|
1320
|
+
"results": {
|
|
1321
|
+
"Ship24": 14,
|
|
1322
|
+
"ParcelsApp": 14,
|
|
1323
|
+
"17TRACK": "no_history",
|
|
1324
|
+
"Postal Ninja": 14,
|
|
1325
|
+
"UPU": "n/a"
|
|
1326
|
+
}
|
|
1327
|
+
},
|
|
1219
1328
|
{
|
|
1220
1329
|
"stale": true,
|
|
1221
1330
|
"results": {
|
|
@@ -1224,7 +1333,8 @@
|
|
|
1224
1333
|
"17TRACK": 1,
|
|
1225
1334
|
"Postal Ninja": "no_history",
|
|
1226
1335
|
"UPU": "n/a"
|
|
1227
|
-
}
|
|
1336
|
+
},
|
|
1337
|
+
"note": "Another comparison reference"
|
|
1228
1338
|
},
|
|
1229
1339
|
{
|
|
1230
1340
|
"note": "A September parcel",
|
|
@@ -1240,8 +1350,18 @@
|
|
|
1240
1350
|
{
|
|
1241
1351
|
"carrier": "nacex",
|
|
1242
1352
|
"direct": "Yes",
|
|
1243
|
-
"sample": "
|
|
1353
|
+
"sample": "✓ 13",
|
|
1244
1354
|
"references": [
|
|
1355
|
+
{
|
|
1356
|
+
"note": "An agency/shipment reference",
|
|
1357
|
+
"results": {
|
|
1358
|
+
"Ship24": "refused",
|
|
1359
|
+
"ParcelsApp": "refused",
|
|
1360
|
+
"17TRACK": "refused",
|
|
1361
|
+
"Postal Ninja": "refused",
|
|
1362
|
+
"UPU": "n/a"
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1245
1365
|
{
|
|
1246
1366
|
"results": {
|
|
1247
1367
|
"Ship24": "error",
|
|
@@ -1249,7 +1369,8 @@
|
|
|
1249
1369
|
"17TRACK": "error",
|
|
1250
1370
|
"Postal Ninja": "error",
|
|
1251
1371
|
"UPU": "n/a"
|
|
1252
|
-
}
|
|
1372
|
+
},
|
|
1373
|
+
"note": "Another comparison reference"
|
|
1253
1374
|
}
|
|
1254
1375
|
]
|
|
1255
1376
|
},
|
package/dist/server/openapi.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-parcel-scraper",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4-main.317",
|
|
4
4
|
"description": "Self-hosted parcel tracking: carrier detection, dedicated scrapers and optional universal providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|