universal-parcel-scraper 0.993.2-main.5350 → 0.993.4-main.5358
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 +1 -1
- package/dist/browser/scraper.js +1 -1
- package/dist/carriers/dhlink/status.js +2 -0
- package/dist/carriers/dhlink/statuses.json +1 -0
- package/dist/carriers/dsv/status.js +5 -0
- package/dist/carriers/dsv/statuses.json +3 -0
- package/dist/carriers/ekart/status.js +2 -0
- package/dist/carriers/ekart/statuses.json +16 -0
- package/dist/carriers/epost-global/parser.js +13 -8
- package/dist/carriers/epost-global/status.d.ts +2 -0
- package/dist/carriers/epost-global/status.js +8 -0
- package/dist/carriers/gzyl/dhl-rows.d.ts +18 -0
- package/dist/carriers/gzyl/dhl-rows.js +39 -0
- package/dist/carriers/huaya-express/adapter.d.ts +3 -1
- package/dist/carriers/huaya-express/adapter.js +4 -1
- package/dist/carriers/huaya-express/parser.d.ts +3 -1
- package/dist/carriers/huaya-express/parser.js +13 -4
- package/dist/carriers/huaya-express/status.d.ts +6 -0
- package/dist/carriers/huaya-express/status.js +13 -1
- package/dist/carriers/huaya-express/statuses.json +19 -1
- package/dist/carriers/imile/statuses.json +1 -0
- package/dist/carriers/jrt/parser.d.ts +2 -9
- package/dist/carriers/jrt/parser.js +3 -38
- package/dist/carriers/js-express/statuses.json +6 -1
- package/dist/carriers/mondial-relay/adapter.js +5 -2
- package/dist/carriers/mondial-relay/status.d.ts +1 -0
- package/dist/carriers/mondial-relay/status.js +8 -1
- package/dist/carriers/mondial-relay/statuses.json +1 -1
- package/dist/carriers/wanbexpress/status.js +2 -0
- package/dist/carriers/wanbexpress/statuses.json +10 -2
- package/dist/core/status/language.js +1 -1
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
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,
|
|
166
|
+
The carrier folders record 19,161 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/dist/browser/scraper.js
CHANGED
|
@@ -46348,7 +46348,7 @@ function lu(e) {
|
|
|
46348
46348
|
if (bl(t) || El(t) || /\b(?:en|em) (?:proceso de |processo de )?devolu(?:cion|cao)\b|^devolu(?:cion|cao)$|\bdevolu(?:cion|cao) (?:en curso|em curso|iniciada|solicitada|en transito|em transito)\b|\b(?:devolu(?:cion|cao)|retorno) (?:al|ao|a la|a) (?:remitente|remetente|origen|origem)\b(?! (?:completad|finalizad|concluid|entregad|entregue|realizad|efectuad|efetuad))|\ben camino (?:de vuelta |de regreso )?(?:al|hacia el) remitente\b|\b(?:a caminho (?:de volta )?(?:do|ao)|em transito para o) remetente\b|\b(?:siendo|sendo|a ser) (?:(?:devuelt|devolvid)[oa]s?|entregad[oa]s? al remitente|entregues? ao remetente)\b|\b(?:no|nao) (?:(?:ha|han|se|fue|foi|foram|sido|ser|sera|puede|pode|pudo|podido|todavia|aun|ainda|ya|ja) )*(?:(?:devuelt|devolvid)[oa]s?|entregad[oa]s? al remitente|entregues? ao remetente)\b|\b(?:sera|seran|serao|va a ser|vai ser|puede ser|pode ser|debe ser|deve ser|devera ser) (?:(?:pronto|proximamente|en breve|em breve|brevemente) )?(?:(?:devuelt|devolvid)[oa]s?|entregad[oa]s? al remitente|entregues? ao remetente)\b|\bse (?:le )?devolvera\b/.test(t)) return "exception";
|
|
46349
46349
|
if (Tl.test(t) || /\breturn (?:was |has been )?(?:successfully )?(?:handed (?:over|back)|delivered) to (?:the )?(?:sender|shipper)\b/.test(t) || /returned to (?:the )?sender|retour(?:ne)? a l'expediteur|zuruck an (?:den )?absender|an (?:den )?absender zuruck|retour a l'expediteur|reso al mittente|restituit[oa] al mittente|delivered (?:back )?to (?:the )?(?:sender|shipper)/.test(t) || /^(?:(?:envio|paquete|pedido|objeto|encomenda|pacote|remessa) )?(?:devuelt|devolvid)[oa]$|\b(?:devuelt|devolvid|retornad)[oa]s? (?:al|ao|a la|a) (?:remitente|remetente|origen|origem)\b|\bentregad[oa]s? al remitente\b|\bentregues? ao remetente\b|\bdevolu(?:cion|cao) (?:(?:al|ao|a la|a) (?:remitente|remetente|origen|origem) )?(?:completad|finalizad|concluid|entregad|entregue|realizad|efectuad|efetuad)/.test(t)) return "returned";
|
|
46350
46350
|
if (/^(?:a )?(?:new|next|another|further) (?:delivery )?attempt\b/.test(t) && !/\b(?:not|unable|fail(?:ed|ure)?|unsuccessful|missed|absent|closed|refused|nobody|no one)\b|n't\b/.test(t)) return "in_transit";
|
|
46351
|
-
if (/not (?:yet )?delivered|\bun ?delivered\b|(?:could not|couldn't).*deliver|\bfailed to be delivered\b|\bdelivered to [^.;,]*\bfailure$|\bcan(?: ?not|'t) be delivered\b|unable to deliver|delivery (?:attempt|failed)|\bdelivery (?:was |is )?(?:not possible|impossible)\b|non livre|n'(?:a|avons) (?:pas )?pu.*(?:remis|remettre)|n'a pas pu etre (?:distribue|livre)|\bne (?:peut|pourra|pourrait|pouvait) (?:pas )?etre (?:distribue|livre)|livraison (?:impossible|echouee)|\bechec (?:de (?:la )?)?livraison|tentative de livraison|nicht zugestellt|nicht zugestellt werden|zustellung.*(?:fehlgeschlagen|nicht moglich)|zustellversuch|non consegnat[oa]|\bmancata consegna\b|non e stato possibile (?:consegnare|effettuare la consegna)|consegna (?:fallita|non riuscita)|tentativo di consegna/.test(t) || /\bno (?:(?:ha|han|hemos|se|le|lo|la|fue|sido|ser|es|era|pudo|puede|podido|posible|todavia|aun) )*(?:entregad[oa]s?|entregar(?:lo|la|le|se)?|repartid[oa]s?|realizar la entrega|efectuar la entrega)\b|\bsin entregar\b|\b(?:entrega|reparto) (?:fallid[oa]|imposible|no (?:(?:ha|han|se|fue|sido|ser|pudo|puede|podido) )*(?:realizad[oa]|efectuad[oa]|completad[oa]|posible|conseguid[oa]))\b|\bimposible (?:entregar|realizar la entrega|efectuar la entrega)\b|\bfallo (?:en la|de) entrega\b|\bintento fallido\b|\bintento de (?:entrega|reparto)\b(?! (?:programad|previst|para (?:hoy|manana|el)|manana|hoy))|\bnao (?:(?:foi|foram|ser|sera|pode|podemos|conseguimos|conseguiu|se|possivel|e|ainda|tem sido|esta|estava) )*(?:entregues?|entregar|efetuar a entrega|efectuar a entrega|realizar a entrega)\b|\bentrega (?:falhada|sem sucesso|impossivel|nao (?:(?:foi|pode|ser|sera|tem sido) )*(?:efetuada|efectuada|realizada|conseguida|concretizada|possivel))\b|\bfalha (?:na|de) entrega\b|\btentativa (?:de entrega|sem sucesso)\b(?! (?:agendada|prevista|programada|para (?:hoje|amanha)|amanha|hoje))|\bcarteiro nao atendido\b/.test(t) || !/\bpick ?up (?:attempt|was attempted|unsuccessful|failed)|attempted (?:to )?pick ?up/.test(t) && /(?:attempted|tried) to deliver|\bcard(?:ed| left)\b|\bwe(?: have|'ve)? missed (?:you|each other)\b(?![^.;!?]*\bready for (?:pick ?up|collection)\b)|\battempted delivery\b(?! (?:is )?(?:scheduled|planned|expected|tomorrow|will)\b)|unsuccessful delivery|delivery (?:was )?(?:un|not )successful|unable to complete (?:the |your |this )?delivery|(?<!\bif (?:the |your )?)\b(?:recipient|addressee|consignee|customer|receiver|user)(?: was| is)? (?:not (?:at home|home|available|present)|unavailable|absent)\b|absence of (?:the )?(?:recipient|addressee|consignee)|\bbusiness (?:was |is )?closed\b|\bpremises (?:(?:was|were) )?closed\b|destinataire absent|(?<!\ben cas d')\babsence du destinataire|(?:entreprise|societe) (?:etait |est )?fermee?\b|nicht angetroffen|(?:firma|betrieb|geschaft) (?:war |ist )?geschlossen|destinatario assente|(?:attivita|azienda|ditta) (?:era |e )?chiusa/.test(t) || !/\bintento de recogida\b|\brecogida (?:fallida|no realizada|no efectuada)\b|\btentativa de (?:recolha|coleta)\b|\b(?:recolha|coleta) (?:falhada|sem sucesso|nao (?:realizada|efetuada|efectuada))\b/.test(t) && !/\b(?:si|caso)\b[^.;:]*\b(?:ausen|nadie|ninguem|cerrad|fechad|no (?:esta|se encuentra)|nao (?:esta|se encontra))|\bse (?:o |a )?(?:destinatari[oa] |cliente )?(?:estiver|for|nao estiver)\b/.test(t) && /\bausen(?:te|tes|cia)\b|\b(?:destinatari[oa]|cliente|consignatari[oa]) (?:no (?:esta|estaba|se encuentra|se encontraba|disponible|presente)|nao (?:esta|estava|se encontra|se encontrava|disponivel|presente))\b|\bnadie en (?:casa|el domicilio)\b|\bninguem em casa\b|\b(?:establecimiento|negocio|empresa|comercio|tienda) (?:esta |estaba |se encontraba )?cerrad[oa]\b|\b(?:estabelecimento|empresa|loja|comercio|negocio) (?:esta |estava |se encontrava )?fechad[oa]\b/.test(t)) return "failed_attempt";
|
|
46351
|
+
if (/not (?:yet )?delivered|\bun ?delivered\b|undelivered|notdelivered|(?:could not|couldn't).*deliver|\bfailed to be delivered\b|\bdelivered to [^.;,]*\bfailure$|\bcan(?: ?not|'t) be delivered\b|unable to deliver|delivery (?:attempt|failed)|\bdelivery (?:was |is )?(?:not possible|impossible)\b|non livre|n'(?:a|avons) (?:pas )?pu.*(?:remis|remettre)|n'a pas pu etre (?:distribue|livre)|\bne (?:peut|pourra|pourrait|pouvait) (?:pas )?etre (?:distribue|livre)|livraison (?:impossible|echouee)|\bechec (?:de (?:la )?)?livraison|tentative de livraison|nicht zugestellt|nicht zugestellt werden|zustellung.*(?:fehlgeschlagen|nicht moglich)|zustellversuch|non consegnat[oa]|\bmancata consegna\b|non e stato possibile (?:consegnare|effettuare la consegna)|consegna (?:fallita|non riuscita)|tentativo di consegna/.test(t) || /\bno (?:(?:ha|han|hemos|se|le|lo|la|fue|sido|ser|es|era|pudo|puede|podido|posible|todavia|aun) )*(?:entregad[oa]s?|entregar(?:lo|la|le|se)?|repartid[oa]s?|realizar la entrega|efectuar la entrega)\b|\bsin entregar\b|\b(?:entrega|reparto) (?:fallid[oa]|imposible|no (?:(?:ha|han|se|fue|sido|ser|pudo|puede|podido) )*(?:realizad[oa]|efectuad[oa]|completad[oa]|posible|conseguid[oa]))\b|\bimposible (?:entregar|realizar la entrega|efectuar la entrega)\b|\bfallo (?:en la|de) entrega\b|\bintento fallido\b|\bintento de (?:entrega|reparto)\b(?! (?:programad|previst|para (?:hoy|manana|el)|manana|hoy))|\bnao (?:(?:foi|foram|ser|sera|pode|podemos|conseguimos|conseguiu|se|possivel|e|ainda|tem sido|esta|estava) )*(?:entregues?|entregar|efetuar a entrega|efectuar a entrega|realizar a entrega)\b|\bentrega (?:falhada|sem sucesso|impossivel|nao (?:(?:foi|pode|ser|sera|tem sido) )*(?:efetuada|efectuada|realizada|conseguida|concretizada|possivel))\b|\bfalha (?:na|de) entrega\b|\btentativa (?:de entrega|sem sucesso)\b(?! (?:agendada|prevista|programada|para (?:hoje|amanha)|amanha|hoje))|\bcarteiro nao atendido\b/.test(t) || !/\bpick ?up (?:attempt|was attempted|unsuccessful|failed)|attempted (?:to )?pick ?up/.test(t) && /(?:attempted|tried) to deliver|\bcard(?:ed| left)\b|\bwe(?: have|'ve)? missed (?:you|each other)\b(?![^.;!?]*\bready for (?:pick ?up|collection)\b)|\battempted delivery\b(?! (?:is )?(?:scheduled|planned|expected|tomorrow|will)\b)|unsuccessful delivery|delivery (?:was )?(?:un|not )successful|unable to complete (?:the |your |this )?delivery|(?<!\bif (?:the |your )?)\b(?:recipient|addressee|consignee|customer|receiver|user)(?: was| is)? (?:not (?:at home|home|available|present)|unavailable|absent)\b|absence of (?:the )?(?:recipient|addressee|consignee)|\bbusiness (?:was |is )?closed\b|\bpremises (?:(?:was|were) )?closed\b|destinataire absent|(?<!\ben cas d')\babsence du destinataire|(?:entreprise|societe) (?:etait |est )?fermee?\b|nicht angetroffen|(?:firma|betrieb|geschaft) (?:war |ist )?geschlossen|destinatario assente|(?:attivita|azienda|ditta) (?:era |e )?chiusa/.test(t) || !/\bintento de recogida\b|\brecogida (?:fallida|no realizada|no efectuada)\b|\btentativa de (?:recolha|coleta)\b|\b(?:recolha|coleta) (?:falhada|sem sucesso|nao (?:realizada|efetuada|efectuada))\b/.test(t) && !/\b(?:si|caso)\b[^.;:]*\b(?:ausen|nadie|ninguem|cerrad|fechad|no (?:esta|se encuentra)|nao (?:esta|se encontra))|\bse (?:o |a )?(?:destinatari[oa] |cliente )?(?:estiver|for|nao estiver)\b/.test(t) && /\bausen(?:te|tes|cia)\b|\b(?:destinatari[oa]|cliente|consignatari[oa]) (?:no (?:esta|estaba|se encuentra|se encontraba|disponible|presente)|nao (?:esta|estava|se encontra|se encontrava|disponivel|presente))\b|\bnadie en (?:casa|el domicilio)\b|\bninguem em casa\b|\b(?:establecimiento|negocio|empresa|comercio|tienda) (?:esta |estaba |se encontraba )?cerrad[oa]\b|\b(?:estabelecimento|empresa|loja|comercio|negocio) (?:esta |estava |se encontrava )?fechad[oa]\b/.test(t)) return "failed_attempt";
|
|
46352
46352
|
if (Zl.test(t)) return Ql.test(t) ? "ready_for_pickup" : "in_transit";
|
|
46353
46353
|
if (/\ba request was made to change (?:this|the|your) delivery date\b/.test(t) || /^(?:the )?delivery (?:details|information) (?:(?:has|have) been |were |was )?updated$/.test(t) || /^(?:package|parcel) shipment$/.test(t)) return "in_transit";
|
|
46354
46354
|
if (/\b(?:damaged|broken in transit)\b|endommag|avarie|deterior|beschadigt|danneggiat|danad[oa]|danificad|uszkodzon/.test(t) || /\blost (?:in transit|package|parcel|shipment)?\b|colis perdu|envoi perdu|egare|verloren|verlust der sendung|smarrit|(?:paquete|envio) perdid|extraviad|zagubion|zaginion/.test(t) || /refused by (?:the )?(?:recipient|consignee)|\brefused\b|rejected by (?:the )?recipient|refus(?:e|ee)? par le destinataire|refus du destinataire|(?:colis|envoi|pli) refuse|annahme verweigert|verweigert|rifiutat|rechazad|recusad|odmowa przyjecia|odmowiono przyjecia/.test(t) || /address (?:incomplete|incorrect|invalid|insufficient|unknown)|(?:incorrect|incomplete|insufficient|wrong|invalid|non[- ]?existent) addres|(?:updated|correct(?:ed)?|complete) (?:delivery )?address (?:is )?(?:required|needed)|address(?:ee)? (?:unknown|cannot be located)|recipient unknown|adresse (?:incorrecte|incomplete|erronee|invalide|inconnue)|destinataire inconnu|(?:adresse|anschrift) (?:unvollstandig|falsch|unbekannt)|empfanger unbekannt|indirizzo (?:errato|incompleto|insufficiente|sconosciuto)|destinatario sconosciuto|direccion (?:incorrecta|incompleta|erronea|desconocida)|destinatario desconocido|endereco (?:incorreto|incompleto|errado|desconhecido)|destinatario desconhecido|adres (?:niepelny|nieprawidlowy|bledny)|nieznany adresat/.test(t)) return "exception";
|
|
@@ -84,6 +84,8 @@ const WORDINGS = new Map([
|
|
|
84
84
|
['arrived at warehouse', 'in_transit'],
|
|
85
85
|
// The flight landed in the destination country.
|
|
86
86
|
['package arrived at destination country', 'in_transit'],
|
|
87
|
+
// After the landing, the airport's cargo handling running late: held up, not back at the order.
|
|
88
|
+
['cargo handling delay', 'in_transit'],
|
|
87
89
|
// The last-mile partner's depot taking in the parcel the line handed over.
|
|
88
90
|
['shipment received in depot', 'in_transit'],
|
|
89
91
|
// The last-mile carrier's pickup on a row without DHLink's node, then its local facility's receipt.
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
{"wording": "SHIPMENT RECEIVED IN DEPOT", "stage": "in_transit", "language": "en", "confirmedBy": "live", "note": "The last-mile partner's depot, after the line handed the parcel over at its destination warehouse."},
|
|
89
89
|
{"wording": "出口申报完成", "stage": "in_transit", "language": "zh", "confirmedBy": "live", "note": "China's export declaration is done, before the flight."},
|
|
90
90
|
{"wording": "Package arrived at destination country", "stage": "in_transit", "language": "en", "confirmedBy": "live", "note": "The flight landed in the destination country."},
|
|
91
|
+
{"wording": "Cargo handling delay", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-10-29", "note": "A day after the landing: the airport's cargo handling is running late. The parcel is held up, not back at the order."},
|
|
91
92
|
{"wording": "您的物品已到达华东仓", "stage": "in_transit", "language": "zh", "confirmedBy": "live", "firstSeen": "2026-10-29", "note": "Arrived at DHLink's East China warehouse, a day after its warehouse receipt and departure; any warehouse."},
|
|
92
93
|
{"wording": "您的物品已离开华东仓", "stage": "in_transit", "language": "zh", "confirmedBy": "live", "firstSeen": "2026-10-29", "note": "Left that warehouse, before the hand-over to the airline; any warehouse."},
|
|
93
94
|
{"wording": "Your item arrived at US IL FRANKLIN PARK Chicago warehouse", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-10-29", "note": "DHLink's US warehouse after import clearance, before the hand-over to the last-mile carrier; any warehouse."},
|
|
@@ -12,6 +12,11 @@ const CODES = {
|
|
|
12
12
|
// The cargo reached DSV, at a terminal or from the pickup.
|
|
13
13
|
IRP: 'accepted',
|
|
14
14
|
ACTUAL_PICKUP: 'accepted',
|
|
15
|
+
// The pickup done, and the cargo checked in at DSV's freight station or warehouse after it.
|
|
16
|
+
PCF: 'accepted',
|
|
17
|
+
CCI: 'accepted',
|
|
18
|
+
// Handed to the airline or line of the main carriage, before its departure.
|
|
19
|
+
HNV: 'in_transit',
|
|
15
20
|
DEP: 'in_transit',
|
|
16
21
|
ATD: 'in_transit',
|
|
17
22
|
ARV: 'in_transit',
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
{ "code": "CREATION", "wording": "Creation", "language": "en", "stage": "registered", "confirmedBy": "live", "note": "The shipment was created in DSV's road system, which the page shows as booked." },
|
|
6
6
|
{ "code": "IRP", "wording": "Cargo received", "language": "en", "stage": "accepted", "confirmedBy": "live", "note": "The cargo reached DSV at a terminal or from the pickup." },
|
|
7
7
|
{ "code": "ACTUAL_PICKUP", "wording": "Actual pickup", "language": "en", "stage": "accepted", "confirmedBy": "bundle", "note": "One of the date types the tracking page's filters name." },
|
|
8
|
+
{ "code": "PCF", "wording": "Actual pickup", "language": "en", "stage": "accepted", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The pickup from the shipper, after the booking." },
|
|
9
|
+
{ "code": "CCI", "wording": "Cargo check-in (CFS/Warehouse)", "language": "en", "stage": "accepted", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The cargo checked in at DSV's freight station or warehouse, after the pickup." },
|
|
10
|
+
{ "code": "HNV", "wording": "Handed over to carrier", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Handed to the carrier of the main carriage, before its departure: not the end of DSV's service." },
|
|
8
11
|
{ "code": "DEP", "wording": "Actual Departure", "language": "en", "stage": "in_transit", "confirmedBy": "live", "note": "A departure from a port or airport of the main carriage." },
|
|
9
12
|
{ "code": "ATD", "wording": "Actual departure - Origin Port", "language": "en", "stage": "in_transit", "confirmedBy": "bundle", "note": "One of the date types the tracking page's filters name." },
|
|
10
13
|
{ "code": "ARV", "wording": "Actual arrival", "language": "en", "stage": "in_transit", "confirmedBy": "live", "note": "An arrival at a port or airport of the main carriage." },
|
|
@@ -11,6 +11,8 @@ const STAGES = new Map(Object.entries({
|
|
|
11
11
|
inscannedatdh: 'in_transit', primarydhinscan: 'in_transit',
|
|
12
12
|
outfordelivery: 'out_for_delivery', delivered: 'delivered',
|
|
13
13
|
undeliveredattempted: 'failed_attempt', undeliveredunattempted: 'failed_attempt',
|
|
14
|
+
// The attempt's code beside its hub, and the update Ekart writes with it at the same instant.
|
|
15
|
+
shipmentundeliveredattempted: 'failed_attempt', undeliveredupdate: 'failed_attempt',
|
|
14
16
|
pickupcancel: 'exception', shipmentrtoconfirmed: 'exception',
|
|
15
17
|
rtoexpected: 'in_transit', rtoreceived: 'in_transit', returninscanned: 'in_transit',
|
|
16
18
|
}));
|
|
@@ -154,6 +154,22 @@
|
|
|
154
154
|
"firstSeen": "2026-10-08",
|
|
155
155
|
"note": "The delivery run ended without an attempt."
|
|
156
156
|
},
|
|
157
|
+
{
|
|
158
|
+
"code": "ShipmentUndeliveredAttempted",
|
|
159
|
+
"language": "en",
|
|
160
|
+
"stage": "failed_attempt",
|
|
161
|
+
"confirmedBy": "live",
|
|
162
|
+
"firstSeen": "2026-11-03",
|
|
163
|
+
"note": "An attempt that did not deliver, beside its hub. The shared rules read the word run together as a delivery."
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"code": "undelivered_update",
|
|
167
|
+
"language": "en",
|
|
168
|
+
"stage": "failed_attempt",
|
|
169
|
+
"confirmedBy": "live",
|
|
170
|
+
"firstSeen": "2026-11-03",
|
|
171
|
+
"note": "Written beside its hub at the same instant as the failed attempt."
|
|
172
|
+
},
|
|
157
173
|
{
|
|
158
174
|
"code": "PickupCancel",
|
|
159
175
|
"language": "en",
|
|
@@ -9,7 +9,7 @@ import { firstOccurrence, usStateTimeZone } from '../../core/time/index.js';
|
|
|
9
9
|
import { cleanScalar } from '../../core/transport/index.js';
|
|
10
10
|
import { isRecord } from '../../core/types.js';
|
|
11
11
|
import { postnlRecordZone } from '../spring-gds/adapter.js';
|
|
12
|
-
import { epostGlobalFacilityScan, epostGlobalStage } from './status.js';
|
|
12
|
+
import { epostGlobalFacilityScan, epostGlobalOwnRow, epostGlobalStage } from './status.js';
|
|
13
13
|
export const PROVIDER = 'ePost Global';
|
|
14
14
|
// A delivered parcel's reply is up to a hundred kilobytes: its history twice,
|
|
15
15
|
// as data and as rendered rows.
|
|
@@ -31,6 +31,9 @@ const SPLIT_STATES = new Set(['AK', 'FL', 'ID', 'IN', 'KS', 'KY', 'MI', 'ND', 'N
|
|
|
31
31
|
// Partners whose names the catalog does not know. Spring's PostNL service is
|
|
32
32
|
// tracked by spring-gds under the same number.
|
|
33
33
|
const VENDORS = new Map([['SPRING POST NL', 'spring-gds']]);
|
|
34
|
+
// GLS's European network, whose tracking runs on Central European time wherever the depot is.
|
|
35
|
+
const GLS_EUROPE = /^GLS ?EU(?:ROPE)?$/i;
|
|
36
|
+
const GLS_ZONE = 'Europe/Berlin';
|
|
34
37
|
const MAX_ROWS = 500;
|
|
35
38
|
const MAX_EVENTS = 100;
|
|
36
39
|
/** An ePost Global number; anything else is a number the tracker is not asked for. */
|
|
@@ -73,15 +76,15 @@ function facilityZone(description, place) {
|
|
|
73
76
|
return state && !SPLIT_STATES.has(state) ? usStateTimeZone(state) : null;
|
|
74
77
|
}
|
|
75
78
|
/**
|
|
76
|
-
* A scan's clock: an instant in the center's zone, or in
|
|
77
|
-
* its own records, else the wall clock as stamped.
|
|
79
|
+
* A scan's clock: an instant in the center's zone, or in the partner's for a
|
|
80
|
+
* row on its clock (PostNL's own records, GLS's rows), else the wall clock as stamped.
|
|
78
81
|
*/
|
|
79
|
-
function clockOf(stamp, zone,
|
|
82
|
+
function clockOf(stamp, zone, partner) {
|
|
80
83
|
const wall = WALL_CLOCK.exec(stamp)?.[1];
|
|
81
84
|
if (!wall || !DateTime.fromISO(wall, { zone: 'utc' }).isValid) {
|
|
82
85
|
throw new SchemaError(PROVIDER, 'ePost Global returned an invalid scan time');
|
|
83
86
|
}
|
|
84
|
-
const reading = zone ??
|
|
87
|
+
const reading = zone ?? partner;
|
|
85
88
|
if (!reading)
|
|
86
89
|
return { local_time: wall };
|
|
87
90
|
const time = firstOccurrence(DateTime.fromISO(wall, { zone: reading })).toISO({ suppressMilliseconds: true });
|
|
@@ -96,7 +99,7 @@ function clockOf(stamp, zone, postnl) {
|
|
|
96
99
|
function postnlZone(code, place, origin) {
|
|
97
100
|
return origin && place === origin && /^\d{1,8}$/.test(code) ? postnlRecordZone(code) : null;
|
|
98
101
|
}
|
|
99
|
-
function projectScan(row, origin) {
|
|
102
|
+
function projectScan(row, origin, partnerZone) {
|
|
100
103
|
if (!isRecord(row))
|
|
101
104
|
throw new SchemaError(PROVIDER, 'ePost Global returned an invalid scan');
|
|
102
105
|
const description = text(row, 'Event');
|
|
@@ -107,7 +110,7 @@ function projectScan(row, origin) {
|
|
|
107
110
|
const code = text(row, 'ECode', 32);
|
|
108
111
|
const staged = epostGlobalStage(description, code);
|
|
109
112
|
const event = {
|
|
110
|
-
...clockOf(stamp, facilityZone(description, place), postnlZone(code, place, origin)),
|
|
113
|
+
...clockOf(stamp, facilityZone(description, place), postnlZone(code, place, origin) ?? (partnerZone && !epostGlobalOwnRow(description) ? partnerZone : null)),
|
|
111
114
|
description,
|
|
112
115
|
...(place ? { location: place } : {}),
|
|
113
116
|
...(code ? { provider_code: code } : {}),
|
|
@@ -183,10 +186,12 @@ export function parseEpostGlobal(html, raw) {
|
|
|
183
186
|
// The shipper's country, where ePost Global places the records Spring's PostNL service keeps without one.
|
|
184
187
|
const shipper = text(details, 'OCt', 8).toUpperCase();
|
|
185
188
|
const origin = VENDORS.get(text(details, 'Vendor', 80).toUpperCase()) === 'spring-gds' && /^[A-Z]{2}$/.test(shipper) ? shipper : null;
|
|
189
|
+
// GLS's rows, even one placed at its gateway in the United States, on GLS's clock.
|
|
190
|
+
const partnerZone = GLS_EUROPE.test(text(details, 'Vendor', 80)) ? GLS_ZONE : null;
|
|
186
191
|
const seen = new Set();
|
|
187
192
|
const events = [];
|
|
188
193
|
for (const row of rows) {
|
|
189
|
-
const scan = projectScan(row, origin);
|
|
194
|
+
const scan = projectScan(row, origin, partnerZone);
|
|
190
195
|
if (!scan || seen.has(scan.key))
|
|
191
196
|
continue;
|
|
192
197
|
seen.add(scan.key);
|
|
@@ -2,6 +2,8 @@ import { type ClassifiedWording, type Stage } from '../../core/status/index.js';
|
|
|
2
2
|
import { type CarrierStatusMap } from '../../core/status/statusMap.js';
|
|
3
3
|
/** The stage the map gives a row relayed from OSM Worldwide by its code, else the wording, if it knows it. */
|
|
4
4
|
export declare function epostGlobalScanStage(wording: string, code?: string): Stage | undefined;
|
|
5
|
+
/** A row ePost Global writes itself, not one it relays from the partner. */
|
|
6
|
+
export declare function epostGlobalOwnRow(wording: string): boolean;
|
|
5
7
|
/** A scan at one of ePost Global's processing centers. */
|
|
6
8
|
export declare function epostGlobalFacilityScan(wording: string): boolean;
|
|
7
9
|
/**
|
|
@@ -34,6 +34,9 @@ const STAGES = new Map([
|
|
|
34
34
|
const HANDED_OVER = /^in transit (?:to|via) \S.* tracking ?#:/;
|
|
35
35
|
// A row relayed from OSM Worldwide, under its code.
|
|
36
36
|
const OSM_CODE = /^OSM\d{2}$/;
|
|
37
|
+
// ePost Global's own rows: its data, label, processing centers and handover. Every other row is a partner's.
|
|
38
|
+
const OWN = new Set(['data received', 'shipment in transit to the epost global processing center', 'parcel received and processing',
|
|
39
|
+
'parcel shipped', 'in transit']);
|
|
37
40
|
// The rows ePost Global's processing centers scan, stamped on the center's clock.
|
|
38
41
|
const FACILITY_SCANS = new Set(['parcel received and processing', 'parcel shipped']);
|
|
39
42
|
/** The stage the map gives a row relayed from OSM Worldwide by its code, else the wording, if it knows it. */
|
|
@@ -44,6 +47,11 @@ export function epostGlobalScanStage(wording, code = '') {
|
|
|
44
47
|
const normalized = normalizeStatusWording(wording);
|
|
45
48
|
return HANDED_OVER.test(normalized) ? 'in_transit' : STAGES.get(normalized);
|
|
46
49
|
}
|
|
50
|
+
/** A row ePost Global writes itself, not one it relays from the partner. */
|
|
51
|
+
export function epostGlobalOwnRow(wording) {
|
|
52
|
+
const normalized = normalizeStatusWording(wording);
|
|
53
|
+
return OWN.has(normalized) || HANDED_OVER.test(normalized);
|
|
54
|
+
}
|
|
47
55
|
/** A scan at one of ePost Global's processing centers. */
|
|
48
56
|
export function epostGlobalFacilityScan(wording) {
|
|
49
57
|
return FACILITY_SCANS.has(normalizeStatusWording(wording));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CarrierResult } from '../../core/result/index.js';
|
|
2
|
+
/** Which rows of a tenant's record are DHL's, and which of those DHL wrote in Germany. */
|
|
3
|
+
export interface DhlRows {
|
|
4
|
+
/** Whether a row is one the tracker copies from DHL, on DHL's wall clock where it scanned the parcel. */
|
|
5
|
+
readonly isRow: (wording: string) => boolean;
|
|
6
|
+
/** Whether a DHL row is one from Germany, before the parcel leaves for the destination country. */
|
|
7
|
+
readonly isGermanRow: (wording: string) => boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The result with the rows a Hualei tenant copies from DHL dated, each keeping
|
|
11
|
+
* its wall clock: a row naming a town on that town's clock; else DHL's rows
|
|
12
|
+
* from Germany on DHL's German clock, the others on the destination's, where
|
|
13
|
+
* DHL or its partner delivers, else on DHL's. A country's code beside a row, as
|
|
14
|
+
* the tracker writes the destination's, names no town. The newest row's
|
|
15
|
+
* instant, when it has one, is the last update. The tenant's clock option keeps
|
|
16
|
+
* those rows as wall clocks.
|
|
17
|
+
*/
|
|
18
|
+
export declare function dateDhlRows(result: CarrierResult, rows: DhlRows): CarrierResult;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
|
+
import { firstOccurrence } from '../../core/time/index.js';
|
|
3
|
+
import { locatePlace, placeTimeZone } from '../../places/index.js';
|
|
4
|
+
import { mainZone } from '../../places/zones.js';
|
|
5
|
+
// DHL's clock in Germany, where its rows on the lines into Europe start.
|
|
6
|
+
const DHL_ZONE = 'Europe/Berlin';
|
|
7
|
+
/** The clock of a town DHL names, as "hagen" or "frankfurt, hessian, de", in the destination or Germany. */
|
|
8
|
+
function townZone(location, country) {
|
|
9
|
+
const place = location.trim() ? locatePlace(location, { countries: country ? [country, 'DE'] : ['DE'] }) : null;
|
|
10
|
+
const zone = place?.precision === 'city' ? placeTimeZone(place) : null;
|
|
11
|
+
return zone ? [zone, 'place'] : null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The result with the rows a Hualei tenant copies from DHL dated, each keeping
|
|
15
|
+
* its wall clock: a row naming a town on that town's clock; else DHL's rows
|
|
16
|
+
* from Germany on DHL's German clock, the others on the destination's, where
|
|
17
|
+
* DHL or its partner delivers, else on DHL's. A country's code beside a row, as
|
|
18
|
+
* the tracker writes the destination's, names no town. The newest row's
|
|
19
|
+
* instant, when it has one, is the last update. The tenant's clock option keeps
|
|
20
|
+
* those rows as wall clocks.
|
|
21
|
+
*/
|
|
22
|
+
export function dateDhlRows(result, rows) {
|
|
23
|
+
const country = /^[A-Z]{2}$/.test(result.destination_country ?? '') ? result.destination_country : null;
|
|
24
|
+
const destination = country ? mainZone(country) : null;
|
|
25
|
+
const events = result.events?.map((event) => {
|
|
26
|
+
const description = event.description ?? '';
|
|
27
|
+
if (event.time || !event.local_time || !rows.isRow(description))
|
|
28
|
+
return event;
|
|
29
|
+
const [zone, basis] = townZone(event.location ?? '', country)
|
|
30
|
+
?? (rows.isGermanRow(description) || !destination ? [DHL_ZONE, 'carrier'] : [destination, 'destination']);
|
|
31
|
+
const time = firstOccurrence(DateTime.fromISO(event.local_time, { zone })).toISO({ suppressMilliseconds: true });
|
|
32
|
+
return time ? { ...event, time, time_zone: zone, time_zone_basis: basis } : event;
|
|
33
|
+
});
|
|
34
|
+
if (!events)
|
|
35
|
+
return result;
|
|
36
|
+
const newest = events[0];
|
|
37
|
+
const { last_update_local: local, ...rest } = result;
|
|
38
|
+
return { ...rest, last_update: newest?.time ?? result.last_update, ...(newest?.time || !local ? {} : { last_update_local: local }), events };
|
|
39
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { AdapterFactory } from '../../core/adapter/index.js';
|
|
1
|
+
import type { AdapterFactory, TrackingContext } from '../../core/adapter/index.js';
|
|
2
|
+
import type { CarrierResult } from '../../core/result/index.js';
|
|
2
3
|
import { HualeiTracker } from '../gzyl/hualei.js';
|
|
3
4
|
export declare class HuayaTracker extends HualeiTracker {
|
|
4
5
|
constructor(options?: ConstructorParameters<typeof HualeiTracker>[1]);
|
|
6
|
+
fetch(raw: string, context?: TrackingContext): Promise<CarrierResult>;
|
|
5
7
|
}
|
|
6
8
|
export declare const adapter: AdapterFactory;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { HualeiTracker } from '../gzyl/hualei.js';
|
|
2
|
-
import { HUAYA } from './parser.js';
|
|
2
|
+
import { dateHuayaDhlRows, HUAYA } from './parser.js';
|
|
3
3
|
export class HuayaTracker extends HualeiTracker {
|
|
4
4
|
constructor(options = {}) { super(HUAYA, options); }
|
|
5
|
+
async fetch(raw, context = {}) {
|
|
6
|
+
return dateHuayaDhlRows(await super.fetch(raw, context));
|
|
7
|
+
}
|
|
5
8
|
}
|
|
6
9
|
export const adapter = (environment) => {
|
|
7
10
|
const tracker = new HuayaTracker({ fetcher: environment.fetcher, recorder: environment.recorder, userAgent: environment.userAgent });
|
|
@@ -3,6 +3,8 @@ import { type HualeiTenant } from '../gzyl/hualei.js';
|
|
|
3
3
|
export declare const PROVIDER = "Huaya Express";
|
|
4
4
|
export declare const HUAYA_ORIGIN = "http://193.112.176.17:8082";
|
|
5
5
|
export declare const HUAYA: HualeiTenant;
|
|
6
|
+
/** The result with the rows the tracker copies from DHL dated, each keeping its wall clock. */
|
|
7
|
+
export declare const dateHuayaDhlRows: (result: CarrierResult) => CarrierResult;
|
|
6
8
|
/** The track interface's reply, or null when it holds no row for the number. */
|
|
7
|
-
export declare
|
|
9
|
+
export declare function parseHuayaTrack(payload: unknown, number: string, readAt?: number): CarrierResult | null;
|
|
8
10
|
export declare const parseHuayaPage: (html: string, number: string, readAt?: number) => CarrierResult;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { dateDhlRows } from '../gzyl/dhl-rows.js';
|
|
1
2
|
import { parseHualeiPage, parseHualeiTrack } from '../gzyl/hualei.js';
|
|
2
|
-
import { huayaStage } from './status.js';
|
|
3
|
+
import { huayaStage, isHuayaDhlGermanRow, isHuayaDhlRow } from './status.js';
|
|
3
4
|
export const PROVIDER = 'Huaya Express';
|
|
4
5
|
export const HUAYA_ORIGIN = 'http://193.112.176.17:8082';
|
|
5
|
-
export const HUAYA = {
|
|
6
|
+
export const HUAYA = {
|
|
7
|
+
carrier: 'huaya-express', provider: PROVIDER, origin: HUAYA_ORIGIN, stage: huayaStage,
|
|
8
|
+
clock: ({ content }) => (isHuayaDhlRow(content) ? 'wall' : 'platform'),
|
|
9
|
+
};
|
|
10
|
+
/** The result with the rows the tracker copies from DHL dated, each keeping its wall clock. */
|
|
11
|
+
export const dateHuayaDhlRows = (result) => dateDhlRows(result, { isRow: isHuayaDhlRow, isGermanRow: isHuayaDhlGermanRow });
|
|
6
12
|
/** The track interface's reply, or null when it holds no row for the number. */
|
|
7
|
-
export
|
|
8
|
-
|
|
13
|
+
export function parseHuayaTrack(payload, number, readAt) {
|
|
14
|
+
const result = parseHualeiTrack(payload, number, HUAYA, readAt);
|
|
15
|
+
return result && dateHuayaDhlRows(result);
|
|
16
|
+
}
|
|
17
|
+
export const parseHuayaPage = (html, number, readAt) => dateHuayaDhlRows(parseHualeiPage(html, number, HUAYA, readAt));
|
|
@@ -4,9 +4,15 @@ export declare const ENTRIES: readonly {
|
|
|
4
4
|
readonly wording: string;
|
|
5
5
|
readonly stage: string;
|
|
6
6
|
}[];
|
|
7
|
+
/** The key a row's `normalizeStatusWording` wording is mapped under: the flight's landing day becomes an ellipsis. */
|
|
8
|
+
export declare const huayaScanKey: (wording: string) => string;
|
|
7
9
|
/**
|
|
8
10
|
* The stage Huaya Express's map gives a row, by its `normalizeStatusWording` wording.
|
|
9
11
|
* A row's kind code, when it has one, groups many wordings and decides nothing.
|
|
10
12
|
*/
|
|
11
13
|
export declare function huayaStage(_code: string | null, wording: string): Stage | undefined;
|
|
14
|
+
/** Whether a row is one the tracker copies from DHL, on DHL's wall clock. */
|
|
15
|
+
export declare const isHuayaDhlRow: (wording: string) => boolean;
|
|
16
|
+
/** Whether a DHL row is one from Germany, before the parcel leaves for the destination country. */
|
|
17
|
+
export declare const isHuayaDhlGermanRow: (wording: string) => boolean;
|
|
12
18
|
export declare const statusMap: CarrierStatusMap;
|
|
@@ -2,11 +2,23 @@ import { normalizeStatusWording } from '../../core/status/statusMap.js';
|
|
|
2
2
|
import statuses from './statuses.json' with { type: 'json' };
|
|
3
3
|
export const ENTRIES = statuses.entries;
|
|
4
4
|
const WORDINGS = new Map(ENTRIES.map((entry) => [normalizeStatusWording(entry.wording), entry.stage]));
|
|
5
|
+
// The flight row names the day it is due to land: "航班起飞,预计航班落地时间(10.22)".
|
|
6
|
+
const LANDING_DAY = /^(航班起飞[,,] ?预计航班落地时间[((])\d{1,2}[./]\d{1,2}([))])$/;
|
|
7
|
+
/** The key a row's `normalizeStatusWording` wording is mapped under: the flight's landing day becomes an ellipsis. */
|
|
8
|
+
export const huayaScanKey = (wording) => wording.replace(LANDING_DAY, '$1…$2');
|
|
5
9
|
/**
|
|
6
10
|
* The stage Huaya Express's map gives a row, by its `normalizeStatusWording` wording.
|
|
7
11
|
* A row's kind code, when it has one, groups many wordings and decides nothing.
|
|
8
12
|
*/
|
|
9
13
|
export function huayaStage(_code, wording) {
|
|
10
|
-
return WORDINGS.get(wording);
|
|
14
|
+
return WORDINGS.get(huayaScanKey(wording));
|
|
11
15
|
}
|
|
16
|
+
// DHL's sentences the tracker copies on the line into Germany, on DHL's wall clock where it scanned the parcel.
|
|
17
|
+
const DHL_SENTENCE = /^(?:the (?:international )?shipment\b|the instruction data for this shipment\b|unfortunately\b|the recipient could not be found\b)/i;
|
|
18
|
+
// DHL's rows from Germany before the parcel leaves for the destination country, and the shipment announced to it.
|
|
19
|
+
const DHL_GERMAN_ROW = /^the instruction data\b|\b(?:of origin|export parcel center|transported to the destination country|in transit to dhl)\b/i;
|
|
20
|
+
/** Whether a row is one the tracker copies from DHL, on DHL's wall clock. */
|
|
21
|
+
export const isHuayaDhlRow = (wording) => DHL_SENTENCE.test(wording.trim());
|
|
22
|
+
/** Whether a DHL row is one from Germany, before the parcel leaves for the destination country. */
|
|
23
|
+
export const isHuayaDhlGermanRow = (wording) => DHL_GERMAN_ROW.test(wording);
|
|
12
24
|
export const statusMap = { stage: huayaStage, gaps: [] };
|
|
@@ -12,6 +12,24 @@
|
|
|
12
12
|
{"wording": "Package arrived at destination country", "language": "en", "stage": "in_transit", "confirmedBy": "source", "note": "As a universal provider relays it."},
|
|
13
13
|
{"wording": "Customs import clearance completed, awaiting pickup by local carrier", "language": "en", "stage": "in_transit", "confirmedBy": "source", "note": "As a universal provider relays it; the local carrier has not taken the parcel yet."},
|
|
14
14
|
{"wording": "It has been handed over to the service provider.", "language": "en", "stage": "in_transit", "confirmedBy": "source", "note": "The hand-over to the local carrier in the destination country, as a universal provider relays it; the row names neither."},
|
|
15
|
-
{"wording": "Arrived at Origin Hub", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-05", "note": "The US delivery partner's first hub, by Chicago O'Hare, once the parcel has flown there, as DHLink's and SwiftX's maps read it: in transit, not Huaya's acceptance."}
|
|
15
|
+
{"wording": "Arrived at Origin Hub", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-05", "note": "The US delivery partner's first hub, by Chicago O'Hare, once the parcel has flown there, as DHLink's and SwiftX's maps read it: in transit, not Huaya's acceptance."},
|
|
16
|
+
{"wording": "货物发往机场途中", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "On the way to the airport, before China's export clearance."},
|
|
17
|
+
{"wording": "货物到达机场", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "At the airport, before China's export clearance."},
|
|
18
|
+
{"wording": "国内清关完成", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "China's export clearance done."},
|
|
19
|
+
{"wording": "安排航班中", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Waiting for a flight to be booked."},
|
|
20
|
+
{"wording": "航班起飞,预计航班落地时间(…)", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The flight left, with the day it is due to land (\"(10.22)\"), which the map reads as an ellipsis."},
|
|
21
|
+
{"wording": "航班落地,清关中", "language": "zh", "stage": "customs", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Landed; import clearance is under way."},
|
|
22
|
+
{"wording": "清关完成", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "After the landing: import clearance done, before the hand-over to the delivering carrier."},
|
|
23
|
+
{"wording": "已交仓", "language": "zh", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Handed to the delivering carrier's warehouse, before its first scan."},
|
|
24
|
+
{"wording": "The instruction data for this shipment have been provided by the sender to DHL electronically", "language": "en", "stage": "registered", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL on the line into Germany: the parcel announced to DHL, on DHL's German clock."},
|
|
25
|
+
{"wording": "The shipment is in transit to DHL", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL, before the flight: on its way to DHL."},
|
|
26
|
+
{"wording": "The shipment has been processed in the parcel center of origin", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL. After a return it is the parcel on its way back."},
|
|
27
|
+
{"wording": "The shipment arrived in the region of recipient and will be transported to the delivery base in the next step.", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL. After a return it is the parcel on its way back."},
|
|
28
|
+
{"wording": "The shipment has been loaded onto the delivery vehicle.", "language": "en", "stage": "out_for_delivery", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL."},
|
|
29
|
+
{"wording": "The shipment has been loaded onto the delivery vehicle. Delivery is expected to take place today.", "language": "en", "stage": "out_for_delivery", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL."},
|
|
30
|
+
{"wording": "The recipient could not be found at the address on the shipment.", "language": "en", "stage": "failed_attempt", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL: the courier's reason for a failed round, as DHL's own map reads it."},
|
|
31
|
+
{"wording": "Unfortunately delivery of the shipment was not possible. The shipment is returned to the sender.", "language": "en", "stage": "returned", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL: the round failed and the parcel goes back. The rows after it are the way back, and the delivery among them is the return's."},
|
|
32
|
+
{"wording": "The shipment has been sent directly from the parcel center to the business customer.", "language": "en", "stage": "delivered", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL: delivered from the parcel centre to a business recipient, \"ab Paketzentrum dem Geschäftskunden zugestellt\" in German (https://www.giga.de/artikel/die-sendung-wurde-ab-paketzentrum-dem-geschaeftskunden-zugestellt-was-bedeutet-das/). After a return it is the return delivered to the sender."},
|
|
33
|
+
{"wording": "Unfortunately, the shipment could not be delivered. This may be for the following reasons: no name on doorbell; recipient has moved; recipient could not be found based on the address details provided on the shipment.", "language": "en", "stage": "failed_attempt", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Copied from DHL: a failed round, with the reasons DHL lists for it."}
|
|
16
34
|
]
|
|
17
35
|
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
{"code": "1003", "wording": "【Sample DS】Shipment out for delivery.", "stage": "out_for_delivery", "language": "en", "confirmedBy": "live", "note": "The delivery run as a station in the United States words it, after the station's name."},
|
|
33
33
|
{"code": "1003", "wording": "Shipment is scheduled to [2026-08-01].", "language": "en", "confirmedBy": "live", "unmapped": true, "note": "A delivery day booked, after a failed attempt the new round; moves nothing."},
|
|
34
34
|
{"code": "1003", "wording": "The driver failed to deliver due to shop(building) closure, we will try to deliver again.", "stage": "failed_attempt", "language": "en", "confirmedBy": "live"},
|
|
35
|
+
{"code": "1003", "wording": "Failed to schedule due to [NoAnswer]。", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-04", "note": "The driver could not book the delivery with the recipient; the reason in brackets varies and the opening decides."},
|
|
35
36
|
{"code": "1003", "wording": "Customer Wants to Change Location", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "note": "A driver's reason, followed by the attempt's end."},
|
|
36
37
|
{"code": "1003", "wording": "Route not completed", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "note": "A driver's reason, followed by the attempt's end."},
|
|
37
38
|
{"code": "1003", "wording": "Traffic, Strike, Bad weather", "stage": "failed_attempt", "language": "en", "confirmedBy": "live", "firstSeen": "2026-10-20", "note": "A driver's reason on the delivery run, followed by the attempt's end and the return to the centre."},
|
|
@@ -3,15 +3,8 @@ import { type HualeiTenant } from '../gzyl/hualei.js';
|
|
|
3
3
|
export declare const PROVIDER = "JRT";
|
|
4
4
|
export declare const JRT_ORIGIN = "http://106.53.64.22:8082";
|
|
5
5
|
export declare const JRT: HualeiTenant;
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
* wall clock: a row naming a town on that town's clock; else DHL's rows from
|
|
9
|
-
* Germany on DHL's German clock, the others on the destination's, where DHL or
|
|
10
|
-
* its partner delivers, else on DHL's. A country's code beside a row, as the
|
|
11
|
-
* tracker writes the destination's, names no town. The newest row's instant,
|
|
12
|
-
* when it has one, is the last update.
|
|
13
|
-
*/
|
|
14
|
-
export declare function dateJrtDhlRows(result: CarrierResult): CarrierResult;
|
|
6
|
+
/** The result with the rows the tracker copies from DHL dated, each keeping its wall clock. */
|
|
7
|
+
export declare const dateJrtDhlRows: (result: CarrierResult) => CarrierResult;
|
|
15
8
|
export declare const normalizeJrtNumber: (raw: string) => string;
|
|
16
9
|
/** The track interface's reply, or null when it holds no row for the number. */
|
|
17
10
|
export declare function parseJrtTrack(payload: unknown, number: string, readAt?: number): CarrierResult | null;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { firstOccurrence } from '../../core/time/index.js';
|
|
3
|
-
import { locatePlace, placeTimeZone } from '../../places/index.js';
|
|
4
|
-
import { mainZone } from '../../places/zones.js';
|
|
1
|
+
import { dateDhlRows } from '../gzyl/dhl-rows.js';
|
|
5
2
|
import { normalizeHualeiNumber, parseHualeiPage, parseHualeiTrack } from '../gzyl/hualei.js';
|
|
6
3
|
import { isJrtDhlGermanRow, isJrtDhlRow, isJrtUpsRow, jrtEnglish, jrtStage } from './status.js';
|
|
7
4
|
export const PROVIDER = 'JRT';
|
|
@@ -14,40 +11,8 @@ export const JRT = {
|
|
|
14
11
|
clock: ({ content, location }) => (isJrtUpsRow(content, location) || isJrtDhlRow(content) ? 'wall' : 'platform'),
|
|
15
12
|
ownNumber: /^JRT\d{10}[A-Z]{2}$/, unlabelledPartners: ['dhl'], productPartners: [[/\bFEDEX\b/i, 'fedex']],
|
|
16
13
|
};
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
/** The clock of a town DHL names, as "hagen" or "frankfurt, hessian, de", in the destination or Germany. */
|
|
20
|
-
function townZone(location, country) {
|
|
21
|
-
const place = location.trim() ? locatePlace(location, { countries: country ? [country, 'DE'] : ['DE'] }) : null;
|
|
22
|
-
const zone = place?.precision === 'city' ? placeTimeZone(place) : null;
|
|
23
|
-
return zone ? [zone, 'place'] : null;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* The result with the rows the tracker copies from DHL dated, each keeping its
|
|
27
|
-
* wall clock: a row naming a town on that town's clock; else DHL's rows from
|
|
28
|
-
* Germany on DHL's German clock, the others on the destination's, where DHL or
|
|
29
|
-
* its partner delivers, else on DHL's. A country's code beside a row, as the
|
|
30
|
-
* tracker writes the destination's, names no town. The newest row's instant,
|
|
31
|
-
* when it has one, is the last update.
|
|
32
|
-
*/
|
|
33
|
-
export function dateJrtDhlRows(result) {
|
|
34
|
-
const country = /^[A-Z]{2}$/.test(result.destination_country ?? '') ? result.destination_country : null;
|
|
35
|
-
const destination = country ? mainZone(country) : null;
|
|
36
|
-
const events = result.events?.map((event) => {
|
|
37
|
-
const description = event.description ?? '';
|
|
38
|
-
if (event.time || !event.local_time || !isJrtDhlRow(description))
|
|
39
|
-
return event;
|
|
40
|
-
const [zone, basis] = townZone(event.location ?? '', country)
|
|
41
|
-
?? (isJrtDhlGermanRow(description) || !destination ? [DHL_ZONE, 'carrier'] : [destination, 'destination']);
|
|
42
|
-
const time = firstOccurrence(DateTime.fromISO(event.local_time, { zone })).toISO({ suppressMilliseconds: true });
|
|
43
|
-
return time ? { ...event, time, time_zone: zone, time_zone_basis: basis } : event;
|
|
44
|
-
});
|
|
45
|
-
if (!events)
|
|
46
|
-
return result;
|
|
47
|
-
const newest = events[0];
|
|
48
|
-
const { last_update_local: local, ...rest } = result;
|
|
49
|
-
return { ...rest, last_update: newest?.time ?? result.last_update, ...(newest?.time || !local ? {} : { last_update_local: local }), events };
|
|
50
|
-
}
|
|
14
|
+
/** The result with the rows the tracker copies from DHL dated, each keeping its wall clock. */
|
|
15
|
+
export const dateJrtDhlRows = (result) => dateDhlRows(result, { isRow: isJrtDhlRow, isGermanRow: isJrtDhlGermanRow });
|
|
51
16
|
export const normalizeJrtNumber = (raw) => normalizeHualeiNumber(raw, PROVIDER);
|
|
52
17
|
/** The track interface's reply, or null when it holds no row for the number. */
|
|
53
18
|
export function parseJrtTrack(payload, number, readAt) {
|
|
@@ -118,6 +118,11 @@
|
|
|
118
118
|
{"code": "OIT", "wording": "Shipment information sent to FedEx", "stage": "registered", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "FedEx's label for the last leg, written before the order reaches JS Express's warehouse."},
|
|
119
119
|
{"code": "OIT", "wording": "Domestic pick up", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "After the departure from origin and before export customs release the parcel: the pick-up for the flight."},
|
|
120
120
|
{"code": "DCF", "wording": "Item Customs clearance completed", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "Import customs released the parcel after the flight landed."},
|
|
121
|
-
{"code": "OIT", "wording": "Arrived at Sort Facility", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The destination's sorting facility after import clearance, before the local courier takes the parcel."}
|
|
121
|
+
{"code": "OIT", "wording": "Arrived at Sort Facility", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "The destination's sorting facility after import clearance, before the local courier takes the parcel."},
|
|
122
|
+
{"code": "OIT", "wording": "Arrived at FedEx location", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "FedEx's hub on the line it delivers, relayed under JS Express's catch-all code."},
|
|
123
|
+
{"code": "OIT", "wording": "Left FedEx origin facility", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "FedEx's departure from its first facility, relayed under the same code."},
|
|
124
|
+
{"code": "OIT", "wording": "The instruction data for this shipment have been provided by the sender to DHL electronically", "stage": "registered", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "DHL's record of the sender's data on the line it delivers in Germany: no scan."},
|
|
125
|
+
{"code": "OIT", "wording": "The shipment is in transit to DHL", "stage": "in_transit", "language": "en", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "DHL's line for a parcel on its way to DHL's network from abroad."},
|
|
126
|
+
{"code": "OIT", "wording": "Die Sendung wurde im Briefzentrum bearbeitet.", "stage": "in_transit", "language": "de", "confirmedBy": "live", "firstSeen": "2026-11-06", "note": "DHL's mail centre in Germany sorting a small parcel, in German."}
|
|
122
127
|
]
|
|
123
128
|
}
|
|
@@ -14,7 +14,7 @@ import { cleanScalar, TRAWL_TRANSPORT_ALLOWANCE_MS, trawlBody } from '../../core
|
|
|
14
14
|
import { isRecord } from '../../core/types.js';
|
|
15
15
|
import { MondialRelayAppClient } from './app.js';
|
|
16
16
|
import { inpostSpainTrackingUrl, isInpostSpainNumber, readInpostSpain } from './inpostSpain.js';
|
|
17
|
-
import { classifyScan, classifyStatus, milestoneNumberStatus, scanSite, scanWording } from './status.js';
|
|
17
|
+
import { classifyScan, classifyStatus, isMerchantDelivery, milestoneNumberStatus, scanSite, scanWording } from './status.js';
|
|
18
18
|
// Protocol provenance (inspected 2026-08-30):
|
|
19
19
|
// https://www.mondialrelay.fr/versioned-assets/2nMAiuVI9Rv9J3kZacblPYCfCABwzS-qZ3m7eFBQn4A/Scripts/vue/tracking/js/app.js
|
|
20
20
|
// SHA-256: da73008ae548f51bfd27791969c6e53d809f080070cd2faa6779bb7850509f80
|
|
@@ -304,7 +304,10 @@ function parseTrackingResponse(payload, credential) {
|
|
|
304
304
|
const events = parsedEvents.map(({ event }) => event);
|
|
305
305
|
const contextual = plainText(expedition.SuiviContextuel);
|
|
306
306
|
const statusText = contextual || events[0]?.description || 'Tracking information received';
|
|
307
|
-
const
|
|
307
|
+
const headline = classifyStatus(statusText);
|
|
308
|
+
// The merchant's receipt of a parcel whose history started a return ends that return.
|
|
309
|
+
const contextualStatus = headline.stage === 'delivered' && isMerchantDelivery(statusText)
|
|
310
|
+
&& parsedEvents.some(({ classified }) => classified.stage === 'returned') ? { status: 'exception', stage: 'returned' } : headline;
|
|
308
311
|
const current = contextualStatus.status !== 'unknown'
|
|
309
312
|
? contextualStatus
|
|
310
313
|
: parsedEvents.find((event) => event.classified.status !== 'unknown')?.classified
|
|
@@ -24,6 +24,7 @@ export declare const MONDIAL_RELAY_WORDING: readonly {
|
|
|
24
24
|
}[];
|
|
25
25
|
/** The status and stage a Mondial Relay label denotes. */
|
|
26
26
|
export declare function classifyStatus(description: string): ClassifiedStatus;
|
|
27
|
+
export declare const isMerchantDelivery: (description: string) => boolean;
|
|
27
28
|
/**
|
|
28
29
|
* A scan's status, given the instants of the reached milestones whose label
|
|
29
30
|
* reads out for delivery. On a home delivery the carrier dates that milestone
|
|
@@ -46,7 +46,7 @@ export const MONDIAL_RELAY_WORDING = [
|
|
|
46
46
|
phrases: [
|
|
47
47
|
'retire par le destinataire', 'retrait effectue', 'remis au destinataire',
|
|
48
48
|
'livraison effectuee au destinataire', 'colis livre au destinataire',
|
|
49
|
-
//
|
|
49
|
+
// The merchant received the parcel: "Votre colis a été livré à l'enseigne." See `MERCHANT_DELIVERY`.
|
|
50
50
|
'livre a l enseigne',
|
|
51
51
|
],
|
|
52
52
|
classified: { status: 'delivered', stage: 'delivered' },
|
|
@@ -108,6 +108,13 @@ export function classifyStatus(description) {
|
|
|
108
108
|
}
|
|
109
109
|
return { status: 'unknown', stage: 'in_transit' };
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The merchant receiving the parcel. A return has its own number, addressed to the merchant, and
|
|
113
|
+
* on it this is the delivery; on a parcel whose own history started a return it is the parcel
|
|
114
|
+
* back with its sender.
|
|
115
|
+
*/
|
|
116
|
+
const MERCHANT_DELIVERY = 'livre a l enseigne';
|
|
117
|
+
export const isMerchantDelivery = (description) => comparableText(description).includes(MERCHANT_DELIVERY);
|
|
111
118
|
const LAST_LEG = 'colis en route vers le point de livraison';
|
|
112
119
|
const OUT_FOR_DELIVERY = { status: 'out_for_delivery', stage: 'out_for_delivery' };
|
|
113
120
|
/**
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
{ "wording": "Colis expédié depuis le site", "language": "fr", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-09-20", "note": "Followed by the name of the logistics site." },
|
|
48
48
|
{ "wording": "Colis en cours de traitement sur le site", "language": "fr", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-09-20", "note": "Followed by the name of the logistics site." },
|
|
49
49
|
{ "wording": "Colis en route vers le point de livraison", "language": "fr", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-09-20", "note": "Read as out for delivery when the out-for-delivery milestone is dated at it, as on a home delivery." },
|
|
50
|
-
{ "wording": "Votre colis a été livré à l'enseigne.", "language": "fr", "stage": "delivered", "confirmedBy": "live", "firstSeen": "2026-10-10", "note": "Headline of a return the merchant
|
|
50
|
+
{ "wording": "Votre colis a été livré à l'enseigne.", "language": "fr", "stage": "delivered", "confirmedBy": "live", "firstSeen": "2026-10-10", "note": "Headline of a parcel the merchant received: a return's own number, addressed to the merchant, is delivered. On a parcel whose history started a return it reads as returned." },
|
|
51
51
|
{ "wording": "Sollicitation Client pour replace colis", "language": "fr", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-10", "note": "The relay could not take the parcel and the customer is asked to choose another; it then goes on to that relay." },
|
|
52
52
|
{ "wording": "Relais de substitution choisi", "language": "fr", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-10", "note": "Follows the request for another relay." },
|
|
53
53
|
{ "wording": "Colis pris en charge en Locker", "language": "fr", "stage": "accepted", "confirmedBy": "live", "firstSeen": "2026-10-10", "note": "The sender's drop-off at a locker; \"en Point Relais\" for a relay." },
|
|
@@ -14,6 +14,8 @@ const UNCODED = new Map([
|
|
|
14
14
|
['in processing at transit facility.', 'in_transit'],
|
|
15
15
|
['in transit to next facility.', 'in_transit'],
|
|
16
16
|
['in transit to next facility', 'in_transit'],
|
|
17
|
+
// USPS's notice of a parcel still moving but behind its expected day.
|
|
18
|
+
['in transit, arriving late', 'in_transit'],
|
|
17
19
|
['it is currently in transit to the next facility', 'in_transit'],
|
|
18
20
|
['the package is being transferred to the next facility', 'in_transit'],
|
|
19
21
|
]);
|
|
@@ -109,6 +109,14 @@
|
|
|
109
109
|
"confirmedBy": "live",
|
|
110
110
|
"note": "The same move, sent without the full stop."
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
"wording": "In Transit, Arriving Late",
|
|
114
|
+
"language": "en",
|
|
115
|
+
"stage": "in_transit",
|
|
116
|
+
"confirmedBy": "live",
|
|
117
|
+
"firstSeen": "2026-11-06",
|
|
118
|
+
"note": "USPS's notice that the parcel is still moving but behind its expected day, relayed without a code: no failed attempt or problem."
|
|
119
|
+
},
|
|
112
120
|
{
|
|
113
121
|
"wording": "It is currently in transit to the next facility",
|
|
114
122
|
"language": "en",
|
|
@@ -187,11 +195,11 @@
|
|
|
187
195
|
"note": "Back at the delivery station after a run that did not deliver it."
|
|
188
196
|
},
|
|
189
197
|
{
|
|
190
|
-
"wording": "Scheduled to
|
|
198
|
+
"wording": "Scheduled to <…> .",
|
|
191
199
|
"language": "en",
|
|
192
200
|
"unmapped": true,
|
|
193
201
|
"confirmedBy": "live",
|
|
194
|
-
"note": "The day booked for the next run after the parcel came back to the station, as iMile writes its \"Shipment is scheduled to\" line: moves nothing, so the failed attempt stands."
|
|
202
|
+
"note": "The day booked for the next run after the parcel came back to the station, as iMile writes its \"Shipment is scheduled to\" line: moves nothing, so the failed attempt stands. Its pattern covers any day."
|
|
195
203
|
}
|
|
196
204
|
]
|
|
197
205
|
}
|
|
@@ -302,7 +302,7 @@ export function trackingLanguageStage(description) {
|
|
|
302
302
|
if (/^(?:a )?(?:new|next|another|further) (?:delivery )?attempt\b/.test(text)
|
|
303
303
|
&& !/\b(?:not|unable|fail(?:ed|ure)?|unsuccessful|missed|absent|closed|refused|nobody|no one)\b|n't\b/.test(text))
|
|
304
304
|
return 'in_transit';
|
|
305
|
-
if (/not (?:yet )?delivered|\bun ?delivered\b|(?:could not|couldn't).*deliver|\bfailed to be delivered\b|\bdelivered to [^.;,]*\bfailure$|\bcan(?: ?not|'t) be delivered\b|unable to deliver|delivery (?:attempt|failed)|\bdelivery (?:was |is )?(?:not possible|impossible)\b|non livre|n'(?:a|avons) (?:pas )?pu.*(?:remis|remettre)|n'a pas pu etre (?:distribue|livre)|\bne (?:peut|pourra|pourrait|pouvait) (?:pas )?etre (?:distribue|livre)|livraison (?:impossible|echouee)|\bechec (?:de (?:la )?)?livraison|tentative de livraison|nicht zugestellt|nicht zugestellt werden|zustellung.*(?:fehlgeschlagen|nicht moglich)|zustellversuch|non consegnat[oa]|\bmancata consegna\b|non e stato possibile (?:consegnare|effettuare la consegna)|consegna (?:fallita|non riuscita)|tentativo di consegna/.test(text))
|
|
305
|
+
if (/not (?:yet )?delivered|\bun ?delivered\b|undelivered|notdelivered|(?:could not|couldn't).*deliver|\bfailed to be delivered\b|\bdelivered to [^.;,]*\bfailure$|\bcan(?: ?not|'t) be delivered\b|unable to deliver|delivery (?:attempt|failed)|\bdelivery (?:was |is )?(?:not possible|impossible)\b|non livre|n'(?:a|avons) (?:pas )?pu.*(?:remis|remettre)|n'a pas pu etre (?:distribue|livre)|\bne (?:peut|pourra|pourrait|pouvait) (?:pas )?etre (?:distribue|livre)|livraison (?:impossible|echouee)|\bechec (?:de (?:la )?)?livraison|tentative de livraison|nicht zugestellt|nicht zugestellt werden|zustellung.*(?:fehlgeschlagen|nicht moglich)|zustellversuch|non consegnat[oa]|\bmancata consegna\b|non e stato possibile (?:consegnare|effettuare la consegna)|consegna (?:fallita|non riuscita)|tentativo di consegna/.test(text))
|
|
306
306
|
return 'failed_attempt';
|
|
307
307
|
if (/\bno (?:(?:ha|han|hemos|se|le|lo|la|fue|sido|ser|es|era|pudo|puede|podido|posible|todavia|aun) )*(?:entregad[oa]s?|entregar(?:lo|la|le|se)?|repartid[oa]s?|realizar la entrega|efectuar la entrega)\b|\bsin entregar\b|\b(?:entrega|reparto) (?:fallid[oa]|imposible|no (?:(?:ha|han|se|fue|sido|ser|pudo|puede|podido) )*(?:realizad[oa]|efectuad[oa]|completad[oa]|posible|conseguid[oa]))\b|\bimposible (?:entregar|realizar la entrega|efectuar la entrega)\b|\bfallo (?:en la|de) entrega\b|\bintento fallido\b|\bintento de (?:entrega|reparto)\b(?! (?:programad|previst|para (?:hoy|manana|el)|manana|hoy))|\bnao (?:(?:foi|foram|ser|sera|pode|podemos|conseguimos|conseguiu|se|possivel|e|ainda|tem sido|esta|estava) )*(?:entregues?|entregar|efetuar a entrega|efectuar a entrega|realizar a entrega)\b|\bentrega (?:falhada|sem sucesso|impossivel|nao (?:(?:foi|pode|ser|sera|tem sido) )*(?:efetuada|efectuada|realizada|conseguida|concretizada|possivel))\b|\bfalha (?:na|de) entrega\b|\btentativa (?:de entrega|sem sucesso)\b(?! (?:agendada|prevista|programada|para (?:hoje|amanha)|amanha|hoje))|\bcarteiro nao atendido\b/.test(text))
|
|
308
308
|
return 'failed_attempt';
|
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.993.
|
|
3
|
+
"version": "0.993.4-main.5358",
|
|
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",
|