universal-parcel-scraper 0.1003.1-main.5421 → 0.1003.3-main.5425

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 19,226 such statuses, each filed under one stage.
166
+ The carrier folders record 19,229 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.
@@ -132,6 +132,9 @@
132
132
  {"code": "DDF", "wording": "DELIVERY ATTEMPTED: 1ST DELIVERY ATTEMPT", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The delivering courier's missed delivery, at the second of the courier's own scan for it."},
133
133
  {"code": "DDF", "wording": "DELIVERY ATTEMPTED: 2ND DELIVERY ATTEMPT", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The courier's second missed delivery."},
134
134
  {"code": "DDE", "wording": "DELIVERY ATTEMPTED: 3RD DELIVERY ATTEMPT", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The courier's third missed delivery, under the code JS Express also gives forwards, at the second of the courier's own scan saying the recipient must book a delivery."},
135
- {"code": "OIT", "wording": "RECEIVED AT LOCAL DEPOT", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The depot that delivers, minutes before each delivery round and again on the evening after a missed one: the courier's delivery depot, not a collection point."}
135
+ {"code": "OIT", "wording": "RECEIVED AT LOCAL DEPOT", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The depot that delivers, minutes before each delivery round and again on the evening after a missed one: the courier's delivery depot, not a collection point."},
136
+ {"code": "DDE", "wording": "DELIVERY EXCEPTION", "stage": "exception", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-07", "note": "The delivering courier's exception at its transit hub, before any delivery round: no attempt, so an exception."},
137
+ {"code": "DDE", "wording": "DELIVERY EXCEPTION, WAIT FOR INFORMATION CONFIRMATION", "stage": "exception", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-07", "note": "The courier holding the parcel until its information is confirmed, listed after its warehouse arrival of the same second."},
138
+ {"code": "OIT", "wording": "ARRIVE AT THE WAREHOUSE", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-07", "note": "The delivering courier's warehouse taking the parcel back, listed before its exception of the same second, which keeps the parcel's stage."}
136
139
  ]
137
140
  }
@@ -1,8 +1,10 @@
1
1
  const STATES = {
2
2
  '101': { description: 'Shipment registered', stage: 'registered', type: 'line_haul' },
3
- '199': { description: 'Shipment accepted', stage: 'in_transit', type: 'line_haul' },
3
+ // The first scan after the order's creation: X-Cargo takes the shipment in, before it is put in a container.
4
+ '199': { description: 'Shipment accepted', stage: 'accepted', type: 'line_haul' },
4
5
  '130': { description: 'Container closed', stage: 'in_transit', type: 'line_haul' },
5
6
  '131': { description: 'Container opened', stage: 'in_transit', type: 'line_haul' },
7
+ // A station's receipt after the container leg, like Big Smart's own code 200, so the parcel is already in transit.
6
8
  '200': { description: 'Received at station', stage: 'in_transit', type: 'line_haul' },
7
9
  '201': { description: 'At station', stage: 'in_transit', type: 'line_haul' },
8
10
  '210': { description: 'At last-mile station', stage: 'in_transit', type: 'last_mile' },
@@ -11,9 +11,10 @@
11
11
  {
12
12
  "code": "199",
13
13
  "wording": "Aceptación de envío",
14
- "stage": "in_transit",
14
+ "stage": "accepted",
15
15
  "language": "es",
16
- "confirmedBy": "portal"
16
+ "confirmedBy": "portal",
17
+ "note": "The line-haul scan after the order's creation and before containerization: X-Cargo takes the shipment in."
17
18
  },
18
19
  {
19
20
  "code": "130",
@@ -27,7 +28,8 @@
27
28
  "wording": "Recibido",
28
29
  "stage": "in_transit",
29
30
  "language": "es",
30
- "confirmedBy": "portal"
31
+ "confirmedBy": "portal",
32
+ "note": "A station's receipt after the container leg, as Big Smart's platform uses code 200; the parcel is already in X-Cargo's hands."
31
33
  },
32
34
  {
33
35
  "code": "201",
@@ -160,9 +160,18 @@ function dayOnlyNetworks(entries, minutes = false) {
160
160
  const walls = new Map();
161
161
  for (const { name, date, offset } of entries)
162
162
  walls.set(nameKey(name), [...walls.get(nameKey(name)) ?? [], offset ? null : mislabeledWallTime(date)]);
163
- return new Set([...walls].filter(([, list]) => list.length >= 3 && dayClock(list, minutes)
164
- && new Set(list.map((wall) => wall.slice(0, 10))).size >= 2).map(([key]) => key));
163
+ return new Set([...walls].filter(([key, list]) => STEP_COUNTED_NETWORKS.has(key)
164
+ ? list.every((wall) => wall !== null && STEP_COUNT.test(wall))
165
+ : list.length >= 3 && dayClock(list, minutes) && new Set(list.map((wall) => wall.slice(0, 10))).size >= 2).map(([key]) => key));
165
166
  }
167
+ /**
168
+ * Networks whose steps ParcelsApp always dates by their day alone, at the top of an hour with
169
+ * seconds that count the steps, however few the reply has: myBRT.it's progress bar ("Spedizione
170
+ * consegnata a BRT" at 01:00:01, "In viaggio" at 01:00:02 the next day), steps BRT's own
171
+ * tracker does not list. Read as instants, they came seconds after midnight.
172
+ */
173
+ const STEP_COUNTED_NETWORKS = new Set(['mybrtit']);
174
+ const STEP_COUNT = /T\d{2}:00(?::0\d(?:\.0+)?)?$/;
166
175
  /**
167
176
  * Whether a network's wall clocks, in the reply's order, stand for no time:
168
177
  * all at 00:00:00, or all on one minute with whole seconds that step by one
@@ -200,6 +200,10 @@ function sourceEventStage(description, includeBroadMovement = true) {
200
200
  // A sender's parcel left at a pick-up point for a courier to collect: an acceptance, not a parcel awaiting its recipient.
201
201
  if (/^the (?:parcel|package) has been delivered to the pick-?up point\. a courier will pick it up\b/i.test(description))
202
202
  return 'accepted';
203
+ // myBRT's progress bar, as ParcelsApp relays it: the shipment handed to BRT, which BRT's own page calls its collection
204
+ // (RITIRATA). "Consegnata" names the hand-over to BRT, not the delivery.
205
+ if (/^spedizione consegnata a brt[.!]?$/i.test(description))
206
+ return 'accepted';
203
207
  // Posti's handling labels survive in aggregator histories. Registration is
204
208
  // a repeated physical scan here, and the reason can describe future customs
205
209
  // or delivery without making either the event's stage.
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.1003.1-main.5421"
5
+ "version": "0.1003.3-main.5425"
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.1003.1-main.5421",
3
+ "version": "0.1003.3-main.5425",
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",