universal-parcel-scraper 0.969.1-main.5255 → 0.969.3-main.5259

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 CHANGED
@@ -163,7 +163,7 @@ the command line's `track --source ParcelsApp` does the same.
163
163
  <!-- GENERATED:stages -->
164
164
  <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">
165
165
 
166
- The carrier folders record 18,923 such statuses, each filed under one stage.
166
+ The carrier folders record 18,928 such statuses, each filed under one stage.
167
167
  <!-- /GENERATED:stages -->
168
168
 
169
169
  Wording nobody recorded yet goes through a classifier that reads seven European languages.
package/data/catalog.json CHANGED
@@ -3133,6 +3133,7 @@
3133
3133
  "BRT Bartolini",
3134
3134
  "BRT Bartolini (Parcel ID)",
3135
3135
  "BRT Bartolini (DPD)",
3136
+ "BRT Italy (DPD Group)",
3136
3137
  "myBRT.it"
3137
3138
  ],
3138
3139
  "countries": [
@@ -2881,6 +2881,7 @@ var lt = {
2881
2881
  "BRT Bartolini",
2882
2882
  "BRT Bartolini (Parcel ID)",
2883
2883
  "BRT Bartolini (DPD)",
2884
+ "BRT Italy (DPD Group)",
2884
2885
  "myBRT.it"
2885
2886
  ],
2886
2887
  countries: ["IT"],
@@ -5,6 +5,7 @@
5
5
  "BRT Bartolini",
6
6
  "BRT Bartolini (Parcel ID)",
7
7
  "BRT Bartolini (DPD)",
8
+ "BRT Italy (DPD Group)",
8
9
  "myBRT.it"
9
10
  ],
10
11
  "region": {
@@ -31,8 +31,12 @@ const uspsSentenceStage = (normalized) => {
31
31
  const status = USPS_SENTENCE.exec(normalized)?.[1];
32
32
  return status ? WORDINGS.stage(status) : undefined;
33
33
  };
34
+ // The destination courier's waybill with its reference after the wording ("Waybill Generated for …") takes the
35
+ // entry recorded without one.
36
+ const WAYBILL_REFERENCE = /^(waybill generated for) [a-z]{0,4}\d{6,30}$/;
34
37
  // iMile's scans as its destination courier, which open with the centre's name in 【】, take iMile's reading.
35
- const mappedStage = (normalized) => WORDINGS.stage(normalized) ?? (FORWARDED.test(normalized) ? 'in_transit' : undefined) ?? uspsSentenceStage(normalized)
38
+ const mappedStage = (normalized) => WORDINGS.stage(normalized.replace(WAYBILL_REFERENCE, '$1')) ?? (FORWARDED.test(normalized) ? 'in_transit' : undefined)
39
+ ?? uspsSentenceStage(normalized)
36
40
  ?? chinaPostRelayedStage(normalized)
37
41
  ?? imileRelayedStage(normalized);
38
42
  /** The stage JS Express's map gives a row's wording. */
@@ -92,7 +92,7 @@
92
92
  {"code": "OIT", "wording": "Arrived to HU HUB", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-03", "note": "The Hungarian partner's hub after the hand-over to the local courier."},
93
93
  {"code": "OIT", "wording": "Linehaul Transit", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-03", "note": "The same partner's line haul between its hub and a sorting centre."},
94
94
  {"code": "OIT", "wording": "Shipment arrived to sorting centre / Shipment in the sorting centre", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-03", "note": "The same partner's sorting centre."},
95
- {"code": "OIT", "wording": "Waybill Generated for", "stage": "registered", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The destination courier's waybill, made while JS Express's warehouse in China still has the parcel."},
95
+ {"code": "OIT", "wording": "Waybill Generated for", "stage": "registered", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The destination courier's waybill, made while JS Express's warehouse in China still has the parcel; the waybill's reference can follow the wording."},
96
96
  {"code": "OIT", "wording": "Arrive at transit center", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "JS Express's transit centre in China, between its sorting centre's outbound scan and the departure from origin."},
97
97
  {"code": "OIT", "wording": "ARRIVE TRANSITHUB", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The destination courier's transit hub, after the import release."},
98
98
  {"code": "OIT", "wording": "TASK ASSIGNED", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The destination courier's delivery task, after the hand-over to it and before its sorting."},
@@ -100,6 +100,7 @@
100
100
  {"code": "OIT", "wording": "OUTBOUND SCANNED", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The destination courier's depot scanning the parcel out, before its delivery round."},
101
101
  {"code": "OIT", "wording": "PREFERENCE APPLIED", "stage": "pending", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The destination courier's record of a delivery preference the recipient chose, days before it has the parcel. It moves nothing."},
102
102
  {"code": "OIT", "wording": "Quick Pickup", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "SF Express's pickup at JS Express's Chinese hub, after the departure from origin: the line's carrier takes the parcel, not its acceptance."},
103
+ {"code": "OIT", "wording": "Local courier has received the parcel", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-05", "note": "The destination courier's receipt at the depot that delivers, between its sorting and its outbound scan, days after the hand-over to it."},
103
104
  {"code": "OIT", "wording": "Operation center Weighing", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "SF Express weighing the parcel at its operation centre, beside its collection."},
104
105
  {"code": "OIT", "wording": "The shipment has been collected by SF Express", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The line's carrier took the parcel from JS Express's hub, after the departure from origin."},
105
106
  {"code": "OIT", "wording": "Shipment loaded, prepare to send to next station.", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04"},
@@ -16,6 +16,8 @@
16
16
  {"wording": "货物在清关【Customs clearance in progress】", "language": "zh", "stage": "customs", "confirmedBy": "live", "note": "In the destination's customs, after the landing."},
17
17
  {"wording": "货物已完成清关【The goods have completed customs clearance】", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "note": "Released by the destination's customs, hours after its clearance began."},
18
18
  {"wording": "货物已交服务商【The shipment has been transferred to the logistics partner.】", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The hand-over to the delivery partner after the import release."},
19
+ {"wording": "First parcel scan along Poste Italiane’s logistic network", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-05", "note": "Poste Italiane's first scan as the delivery partner, a day after the hand-over to it, relayed in capitals: the parcel is already on its way."},
20
+ {"wording": "In transit along Poste Italiane’s logistic network", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-05", "note": "Poste Italiane moving the parcel towards the office that delivers it."},
19
21
  {"wording": "货物电子信息已经收到", "language": "zh", "stage": "registered", "confirmedBy": "prior-art", "note": "The platform writes this row itself for an order without scans, on its creation time."}
20
22
  ]
21
23
  }
@@ -2,6 +2,7 @@
2
2
  "carrier": "pengyou",
3
3
  "entries": [
4
4
  {"wording": "Shenzhen Processing Center has received it", "language": "en", "stage": "accepted", "confirmedBy": "live", "note": "The first scan in Pengyou's hands, with the city after it in brackets."},
5
+ {"wording": "Parcel has been sorted and is ready to be sent", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-05", "note": "The processing centre sorting the parcel for the next leg, a day after its receipt, with the city after it in brackets."},
5
6
  {"wording": "Delivered", "language": "en", "stage": "delivered", "confirmedBy": "live", "note": "The last-mile delivery, with the town after it, unbracketed, and its two-letter state or province when it gives one."}
6
7
  ]
7
8
  }
@@ -12,6 +12,6 @@ export declare function uniuniDropOffStage(text: string): ClassifiedStatus | und
12
12
  /**
13
13
  * What the map says about one scan: its state code, as the adapter reads it, and a Uni Store
14
14
  * drop-off, which has none, by its wording. 202 is left open: the stored row does not say whether
15
- * it is UniUni's own scan or a cross-border shipper's.
15
+ * it is UniUni's own scan or a cross-border shipper's, except for the shipper's notice.
16
16
  */
17
17
  export declare const statusMap: CarrierStatusMap;
@@ -15,6 +15,11 @@ const STATES = new Map([
15
15
  export function uniuniStatus(code) {
16
16
  return STATES.get(code);
17
17
  }
18
+ /**
19
+ * A cross-border shipper's notice that its data reached UniUni, which does not have the parcel yet: "on its
20
+ * way to UniUni". UniUni's own scans never word it so, and its own "Order received" (190) is registered too.
21
+ */
22
+ const SHIPPER_NOTICE = /^shipping information received\. your parcel is on its way to uniuni\b/i;
18
23
  /**
19
24
  * A scan without UniUni's English summary is read by its own code, except 202:
20
25
  * cross-border shippers' scans carry it on every row until delivery, so there
@@ -23,7 +28,7 @@ export function uniuniStatus(code) {
23
28
  export function uniuniPartnerStatus(code, text) {
24
29
  if (code !== 202)
25
30
  return uniuniStatus(code);
26
- const stage = wordingStage(text);
31
+ const stage = SHIPPER_NOTICE.test(text.trim()) ? 'registered' : wordingStage(text);
27
32
  return { status: languageStageStatus(stage), stage };
28
33
  }
29
34
  /** A Uni Store drop-off carries no status code, only this wording. */
@@ -33,16 +38,18 @@ export function uniuniDropOffStage(text) {
33
38
  /**
34
39
  * What the map says about one scan: its state code, as the adapter reads it, and a Uni Store
35
40
  * drop-off, which has none, by its wording. 202 is left open: the stored row does not say whether
36
- * it is UniUni's own scan or a cross-border shipper's.
41
+ * it is UniUni's own scan or a cross-border shipper's, except for the shipper's notice.
37
42
  */
38
43
  export const statusMap = {
39
44
  stage: (code, wording) => {
40
45
  if (code === null)
41
46
  return uniuniDropOffStage(wording)?.stage;
42
- return /^\d{1,9}$/.test(code) && code !== '202' ? uniuniStatus(Number(code))?.stage : undefined;
47
+ if (code === '202')
48
+ return SHIPPER_NOTICE.test(wording) ? 'registered' : undefined;
49
+ return /^\d{1,9}$/.test(code) ? uniuniStatus(Number(code))?.stage : undefined;
43
50
  },
44
51
  gaps: [{
45
52
  code: '202',
46
- note: "Out for delivery in UniUni's own scans; a cross-border shipper's scans carry it on every row until delivery, and their wording decides. The stored row does not say whose it is.",
53
+ note: "Out for delivery in UniUni's own scans; a cross-border shipper's scans carry it on every row until delivery, and their wording decides. The stored row does not say whose it is, except the shipper's notice that its data reached UniUni.",
47
54
  }],
48
55
  };
@@ -36,6 +36,15 @@
36
36
  "stage": "out_for_delivery",
37
37
  "confirmedBy": "live"
38
38
  },
39
+ {
40
+ "code": "202",
41
+ "wording": "Shipping information received. Your parcel is on its way to UniUni for delivery to you",
42
+ "language": "en",
43
+ "stage": "registered",
44
+ "confirmedBy": "live",
45
+ "firstSeen": "2026-11-05",
46
+ "note": "A cross-border shipper's notice under 202, which its rows carry until delivery: its data reached UniUni, which does not have the parcel yet, as with UniUni's own Order received."
47
+ },
39
48
  {
40
49
  "code": "211",
41
50
  "wording": "Failed delivery attempt, returning to the warehouse",
@@ -3133,6 +3133,7 @@
3133
3133
  "BRT Bartolini",
3134
3134
  "BRT Bartolini (Parcel ID)",
3135
3135
  "BRT Bartolini (DPD)",
3136
+ "BRT Italy (DPD Group)",
3136
3137
  "myBRT.it"
3137
3138
  ],
3138
3139
  "countries": [
@@ -3134,6 +3134,7 @@ export const CARRIER_CATALOG = {
3134
3134
  "BRT Bartolini",
3135
3135
  "BRT Bartolini (Parcel ID)",
3136
3136
  "BRT Bartolini (DPD)",
3137
+ "BRT Italy (DPD Group)",
3137
3138
  "myBRT.it"
3138
3139
  ],
3139
3140
  "countries": [
@@ -655,6 +655,10 @@ function providerWordingStage(description) {
655
655
  // YunExpress's handler taking the parcel from the airline's cargo terminal after its flight, as its own map reads it.
656
656
  if (/^collected at cargo terminal[.!]?$/i.test(value))
657
657
  return 'in_transit';
658
+ // 4PX's operation point packing a parcel for the carrier that takes it in next, before that carrier's acceptance,
659
+ // as ParcelsApp relays 4PX's row in English and Spanish: a warehouse packing the order, as Cainiao's is.
660
+ if (/^packing the operating point[.!]?$|^empacando el punto de operacion[.!]?$/i.test(value))
661
+ return 'registered';
658
662
  // Maersk's leg as ParcelsApp relays it after the first carrier's: the manifest announcing the parcel, then
659
663
  // Maersk taking it over in the destination country, the step the first carrier calls its local delivery
660
664
  // company's receipt. A parcel relabelled at a facility on the way moves on.
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.969.1-main.5255"
5
+ "version": "0.969.3-main.5259"
6
6
  },
7
7
  "paths": {
8
8
  "/health": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-parcel-scraper",
3
- "version": "0.969.1-main.5255",
3
+ "version": "0.969.3-main.5259",
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",