universal-parcel-scraper 0.25.1-main.364 → 0.25.3-main.366
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/data/detection-golden.json +21 -0
- package/dist/browser/scraper.js +3 -1
- package/dist/carriers/dhl-express/clock.d.ts +8 -3
- package/dist/carriers/dhl-express/clock.js +54 -10
- package/dist/carriers/dhl-express/numbers.json +22 -0
- package/dist/core/time/index.d.ts +5 -2
- package/dist/core/time/index.js +5 -2
- package/dist/generated/regionTowns.d.ts +4 -1
- package/dist/generated/regionTowns.js +6 -1
- package/dist/providers/parcelsapp/adapter.js +19 -8
- package/dist/providers/seventeentrack/adapter.d.ts +0 -10
- package/dist/providers/seventeentrack/adapter.js +33 -2
- package/dist/providers/shared/facilityClock.d.ts +25 -0
- package/dist/providers/shared/facilityClock.js +36 -0
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
|
@@ -2612,6 +2612,27 @@
|
|
|
2612
2612
|
"dpd-fr"
|
|
2613
2613
|
]
|
|
2614
2614
|
},
|
|
2615
|
+
{
|
|
2616
|
+
"input": "2522622410",
|
|
2617
|
+
"carrier": "unknown",
|
|
2618
|
+
"confidence": "low",
|
|
2619
|
+
"candidates": [
|
|
2620
|
+
"dhl-express",
|
|
2621
|
+
"dhl",
|
|
2622
|
+
"mondial-relay",
|
|
2623
|
+
"relais-colis",
|
|
2624
|
+
"tipsa",
|
|
2625
|
+
"estafeta"
|
|
2626
|
+
],
|
|
2627
|
+
"preferred": [
|
|
2628
|
+
"dhl-express"
|
|
2629
|
+
],
|
|
2630
|
+
"asked": [
|
|
2631
|
+
"dhl-express",
|
|
2632
|
+
"relais-colis",
|
|
2633
|
+
"tipsa"
|
|
2634
|
+
]
|
|
2635
|
+
},
|
|
2615
2636
|
{
|
|
2616
2637
|
"input": "25631818",
|
|
2617
2638
|
"carrier": "unknown",
|
package/dist/browser/scraper.js
CHANGED
|
@@ -7666,7 +7666,9 @@ var co = {
|
|
|
7666
7666
|
IN: "aberdeen|akron|albany|albion|alexandria|anderson|andrews|angola|arcadia|argos|attica|auburn|aurora|austin|avilla|avon|bargersville|basslake|batesville|battleground|bedford|beechgrove|berne|bicknell|bloomfield|bloomington|bluffton|boonville|bourbon|brazil|bremen|bright|bristol|broadripple|brooklyn|brookston|brookville|brownsburg|brownstown|burnsharbor|butler|cambridgecity|cannelton|carmel|cayuga|cedarlake|centerville|chandler|charlestown|chesterfield|chesterton|churubusco|cicero|clarksville|clermont|clinton|cloverdale|columbiacity|columbus|connersville|converse|cordrysweetwaterlakes|corydon|countrysquirelakes|covington|crawfordsville|crothersville|crownpoint|culver|cumberland|dale|daleville|danville|darmstadt|dayton|decatur|delphi|demotte|dillsboro|dunkirk|dunlap|dyer|eastchicago|eaton|edgewood|edinburgh|elkhart|ellettsville|elwood|english|evansville|fairfieldheights|fairmount|fairviewpark|farmersburg|farmland|ferdinand|fishers|fishlake|flora|fortville|fowler|frankfort|franklin|frankton|fremont|frenchlick|ftbranch|ftwayne|galena|galveston|garrett|gary|gascity|geneva|georgetown|goodland|goshen|grabill|granger|greencastle|greendale|greenfield|greensburg|greentown|greenwood|griffith|grissomairforcebase|gulivoirepark|hagerstown|hamilton|hammond|hanover|harlan|hartfordcity|haubstadt|hebron|henryville|heritagelake|hiddenvalley|highland|hobart|hope|hudsonlake|huntertown|huntingburg|huntington|indianapolis|indianheights|ingalls|jasonville|jasper|jeffersonville|jonesboro|kendallville|kentland|kingsfordheights|knightstown|knox|kokomo|koontzlake|kouts|lafayette|lagrange|lakedalecarlia|lakesofthefourseasons|lakestation|lapel|laporte|lawrence|lawrenceburg|lebanon|leocedarville|liberty|ligonier|linton|logansport|longbeach|loogootee|lowell|lynn|madison|marion|markle|martinsville|mccordsville|melodyhill|meridianhills|merrillville|michigancity|middlebury|middletown|milan|milford|mishawaka|mitchell|monon|monroeville|monrovia|monticello|montpelier|mooresville|morocco|morristown|mtvernon|mulberry|muncie|munster|nappanee|nashville|newalbany|newburgh|newcarlisle|newcastle|newchicago|newhaven|newpalestine|newparis|newpekin|newport|newwhiteland|noblesville|northjudson|northliberty|northmadison|northmanchester|northterrehaute|northvernon|northwebster|notredame|oaklandcity|oakpark|odon|ogdendunes|oolitic|orleans|osceola|osgood|ossian|otis|otterbein|owensville|oxford|paoli|parkercity|pendleton|peru|petersburg|pierceton|pittsboro|plainfield|plymouth|portage|porter|portland|poseyville|princeslakes|princeton|redkey|remington|rensselaer|richmond|risingsun|roanoke|rochester|rockport|rockville|romecity|roselawn|rossville|rushville|russiaville|santaclaus|schererville|scottsburg|seelyville|sellersburg|seymour|shadeland|shelburn|shelbyville|sheridan|shoals|shorewoodforest|simontonlake|smithvillesanders|southbend|southhaven|southport|southwhitley|speedway|spencer|stjohn|stpaul|sullivan|sunman|sweetser|syracuse|tellcity|terrehaute|thorntown|tipton|topeka|trafalgar|trailcreek|trilakes|unioncity|upland|valparaiso|veedersburg|versailles|vevay|vincennes|wabash|wakarusa|walkerton|walton|wanatah|warren|warrenpark|warsaw|washington|waterloo|westfield|westlafayette|westport|westterrehaute|westville|whiteland|whitestown|whiting|williamsport|winamac|winchester|winfield|winonalake|wolcottville|woodburn|worthington|yorktown|zionsville",
|
|
7667
7667
|
MT: "absarokee|anaconda|baker|belgrade|bigfork|bigsky|bigtimber|billings|bonnerwestriverside|boulder|bozeman|broadus|browning|butte|chester|chinook|choteau|circle|clancy|clinton|colstrip|columbiafalls|columbus|conrad|crowagency|cutbank|deerlodge|dillon|easthelena|eastmissoula|ekalaka|eureka|evergreen|forsyth|fourcorners|frenchtown|ftbelknapagency|ftbenton|glasgow|glendive|greatfalls|hamilton|hardin|harlowton|havre|helena|helenavalleynortheast|helenavalleynorthwest|helenavalleysoutheast|helenavalleywestcentral|helenawestside|hysham|jordan|kalispell|lakeside|lamedeer|laurel|lewistown|libby|lincoln|livingston|lockwood|lolo|malmstromairforcebase|malta|manhattan|milescity|missoula|montanacity|northbrowning|orchardhomes|pablo|philipsburg|plains|plentywood|polson|redlodge|ronan|roundup|ryegate|scobey|seeleylake|shelby|sidney|somers|southbrowning|stanford|stevensville|sunprairie|superior|terry|thompsonfalls|threeforks|townsend|virginiacity|warmsprings|westglendive|westyellowstone|whitefish|whitehall|whitesulphursprings|wibaux|winnett|wolfpoint",
|
|
7668
7668
|
NL: "airportheights|baieverte|ballyhaly|bannerman|baybulls|bayroberts|baystgeorgesouth|bellsturn|bishopsfalls|bonavista|botwood|burgeo|burin|carbonear|centrevillewarehamtrinity|channelportauxbasques|churchillpark|clarenville|clarenvilleshoalharbour|clarkesbeach|clovellytrails|conceptionbaysouth|cornerbrook|cowanheights|deerlake|donovans|downtownstjohns|eastmeadows|evergreenvillage|fairviewacres|flatrock|fogoisland|fortune|ftwilliam|gambo|gander|georgestown|glendale|glenridgecrescent|glovertown|goulds|grandbank|grandfallswindsor|happyvalleygoosebay|harbourbreton|harbourgrace|harbourmainchapelscovelakeview|holyrood|humberarmsouth|irishtownsummerside|karwoodestates|kenmountterrace|kilbride|kingwilliamestates|kippens|labradorcity|lewisporte|littlecanada|logybaymiddlecoveoutercove|marystown|masseydrive|mtcashel|mtpearl|mundypond|nain|newwesvalley|paradise|pasadena|pearlgate|placentia|pleasantville|portugalcovestphillips|pouchcove|quidividi|rabbittown|sesamepark|sheaheights|sheshatshiu|signalhill|southlands|spaniardsbay|springdale|sprucemeadows|stalbans|stanthony|stephenville|stephenvillecrossing|stgeorges|stjohns|stlawrence|torbay|trinitybaynorth|twillingate|upperislandcove|victoria|virginiapark|wabana|wabush|waterfordvalley|whitbourne|wishingwellpark|witlessbay",
|
|
7669
|
-
SK: "adelaidechurchill|albertpark|alritchie|arborcreek|arcolaeast|argylepark|aspenridge|assiniboia|avalon|balgonie|battleford|biggar|blairmore|blairmoreurbancentre|boothill|brevoortpark|briarwood|brighton|buenavista|buffalonarrows|canora|carlyle|caswellhill|cathedral|centralbusinessdistrict|centresquare|citypark|collegepark|collegeparkeast|confederation|confederationpark|confederationurbancentre|coreneighbourhoods|coronationpark|creighton|dalmeny|davidson|delisle|dewdneyeast|dieppewesterra|downtown|dundonald|eastview|erindale|esterhazy|estevan|evergreen|exhibition|fairhaven|foamlake|forestgrove|ftquappelle|gladmerpark|gravelbourg|greystoneheights|grosvenorpark|hamptonvillage|harbourlanding|haultain|heritage|hillsdale|holidaypark|holliston|holmwood|hudsonbay|hudsonbaypark|humboldt|indianhead|kamsack|kensington|kerrobert|kindersley|kinggeorge|lakeridge|lakeview|lakewood|lakewoodurbancentre|langenburg|langham|lanigan|laronge|lawson|lawsonheights|lawsonheightsurbancentre|lumsden|macklin|maplecreek|martensville|masseyplace|mcnab|meadowgreen|meadowlake|melfort|melville|montgomeryplace|moosejaw|moosomin|mtroyal|nipawin|normanview|normanviewwest|northbattleford|northcentral|northeastregina|northpark|nutana|nutanapark|nutanasector|nutanaurbancentre|outlook|oxbow|pacificheights|parkridge|pelicannarrows|pilotbutte|pleasanthill|prairieview|preeceville|princealbert|queenelizabeth|regentpark|regina|reginabeach|riverheights|riversdale|rosemontmtroyal|rosetown|rosewood|rosthern|saskatoon|shaunavon|shellbrook|sherwoodmccarthy|silverspring|silverwoodheights|stonebridge|sutherland|swiftcurrent|tisdale|twinlakes|unity|universityheights|universityheightsurbancentre|uofslandssouthmanagementarea|uplands|varsityview|wadena|waldheim|walshacreslakeridgegardenridge|warman|watrous|westmount|westview|weyburn|whitecity|whitmorepark|wildwood|wilkie|willowgrove|wynyard|yorkton"
|
|
7669
|
+
SK: "adelaidechurchill|albertpark|alritchie|arborcreek|arcolaeast|argylepark|aspenridge|assiniboia|avalon|balgonie|battleford|biggar|blairmore|blairmoreurbancentre|boothill|brevoortpark|briarwood|brighton|buenavista|buffalonarrows|canora|carlyle|caswellhill|cathedral|centralbusinessdistrict|centresquare|citypark|collegepark|collegeparkeast|confederation|confederationpark|confederationurbancentre|coreneighbourhoods|coronationpark|creighton|dalmeny|davidson|delisle|dewdneyeast|dieppewesterra|downtown|dundonald|eastview|erindale|esterhazy|estevan|evergreen|exhibition|fairhaven|foamlake|forestgrove|ftquappelle|gladmerpark|gravelbourg|greystoneheights|grosvenorpark|hamptonvillage|harbourlanding|haultain|heritage|hillsdale|holidaypark|holliston|holmwood|hudsonbay|hudsonbaypark|humboldt|indianhead|kamsack|kensington|kerrobert|kindersley|kinggeorge|lakeridge|lakeview|lakewood|lakewoodurbancentre|langenburg|langham|lanigan|laronge|lawson|lawsonheights|lawsonheightsurbancentre|lumsden|macklin|maplecreek|martensville|masseyplace|mcnab|meadowgreen|meadowlake|melfort|melville|montgomeryplace|moosejaw|moosomin|mtroyal|nipawin|normanview|normanviewwest|northbattleford|northcentral|northeastregina|northpark|nutana|nutanapark|nutanasector|nutanaurbancentre|outlook|oxbow|pacificheights|parkridge|pelicannarrows|pilotbutte|pleasanthill|prairieview|preeceville|princealbert|queenelizabeth|regentpark|regina|reginabeach|riverheights|riversdale|rosemontmtroyal|rosetown|rosewood|rosthern|saskatoon|shaunavon|shellbrook|sherwoodmccarthy|silverspring|silverwoodheights|stonebridge|sutherland|swiftcurrent|tisdale|twinlakes|unity|universityheights|universityheightsurbancentre|uofslandssouthmanagementarea|uplands|varsityview|wadena|waldheim|walshacreslakeridgegardenridge|warman|watrous|westmount|westview|weyburn|whitecity|whitmorepark|wildwood|wilkie|willowgrove|wynyard|yorkton",
|
|
7670
|
+
"ES-CN": "adeje|agaete|aguimes|agulo|alajero|antigua|arafo|arona|arrecife|artenara|arucas|barlovento|brenaalta|brenabaja|buenavistadelnorte|candelaria|carrizal|centro|corralejo|costacalma|costateguise|crucedearinaga|elcotillo|elhierro|elpaso|fasnia|firgas|frontera|fuencalientedelapalma|fuerteventura|galdar|garachico|garafia|granadilladeabona|grancanaria|guia|guiadeisora|guimar|haria|hermigua|icoddelosvinos|ingenio|lagomera|lagraciosa|laguancha|lalaguna|lamatanzadeacentejo|lanzarote|laoliva|laorotava|lapalma|laspalmas|laspalmasdegrancanaria|lasrosas|lavictoriadeacentejo|lomodearico|losgigantes|losllanosdearidane|losrealejos|lossilos|macher|maspalomas|mazo|mogan|nazaret|pajara|playablanca|playadelasamericas|playadelingles|playadesanjuan|puertodelacruz|puertodelcarmen|puertodelrosario|puertorico|puntademujeres|puntagorda|puntallana|realejoalto|sanbartolome|sanbartolomedetirajana|sanjuandelarambla|sanmigueldeabona|sannicolas|sansebastiandelagomera|santabrigida|santacruzdelapalma|santacruzdetenerife|santalucia|santaursula|santiagodelteide|sauzal|tacoronte|tamaraceite|tanque|tazacorte|tegueste|teguise|tejeda|telde|tenerife|teror|tias|tijarafe|tinajo|tuineje|vallehermosa|valleseco|valsequillodegrancanaria|vecindario|vegadesanmateo|vilaflor|yaiza",
|
|
7671
|
+
"PT-20": "aguadepau|angradoheroismo|angustias|arrifes|biscoitos|cabouco|caisdopico|corvo|fajadebaixo|fenaisdaajuda|fenaisdaluz|feteira|flores|fontebastardo|furnas|graciosa|horta|lajes|lajesdasflores|mosteiros|nordeste|pico|pontadelgada|pontagarca|portojudeu|portomartins|povoacao|praiadavitoria|rabodepeixe|relva|ribeiragrande|ribeiraseca|ribeirinha|rostodecao|santabarbara|santacruzdagraciosa|santacruzdasflores|santamaria|saomateus|saomiguel|saoroquedopico|saosebastiao|terceira|velas|vilafrancadocampo"
|
|
7670
7672
|
};
|
|
7671
7673
|
//#endregion
|
|
7672
7674
|
//#region core/transport/text.ts
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The zone of a DHL Express facility, from its "CITY - COUNTRY"
|
|
3
|
-
* the
|
|
4
|
-
*
|
|
2
|
+
* The zone of a DHL Express facility, from its location: "CITY - COUNTRY", and
|
|
3
|
+
* in the USA and Canada "CITY - REGION - COUNTRY" or "CITY, ST - COUNTRY". Null
|
|
4
|
+
* when the location does not settle one: a country with several clocks and no
|
|
5
|
+
* region DHL names, or a country it writes in a form this does not know.
|
|
6
|
+
*
|
|
7
|
+
* A US state or Canadian province with several clocks takes its majority zone.
|
|
8
|
+
* Spain and Portugal take their mainland's, except for a town or island of the
|
|
9
|
+
* Canary Islands or the Azores.
|
|
5
10
|
*/
|
|
6
11
|
export declare function facilityZone(location: string): string | null;
|
|
@@ -1,14 +1,58 @@
|
|
|
1
|
-
import { countryTimeZone } from '../../core/time/index.js';
|
|
1
|
+
import { canadaProvinceTimeZone, countryTimeZone, regionHasTown, usStateTimeZone } from '../../core/time/index.js';
|
|
2
|
+
// DHL names a US state in full ("Ohio", "New York") or with its code ("VIRGINIA,VA").
|
|
3
|
+
const US_STATES = {
|
|
4
|
+
ALABAMA: 'AL', ALASKA: 'AK', ARIZONA: 'AZ', ARKANSAS: 'AR', CALIFORNIA: 'CA', COLORADO: 'CO', CONNECTICUT: 'CT',
|
|
5
|
+
DELAWARE: 'DE', 'DISTRICT OF COLUMBIA': 'DC', FLORIDA: 'FL', GEORGIA: 'GA', HAWAII: 'HI', IDAHO: 'ID', ILLINOIS: 'IL',
|
|
6
|
+
INDIANA: 'IN', IOWA: 'IA', KANSAS: 'KS', KENTUCKY: 'KY', LOUISIANA: 'LA', MAINE: 'ME', MARYLAND: 'MD',
|
|
7
|
+
MASSACHUSETTS: 'MA', MICHIGAN: 'MI', MINNESOTA: 'MN', MISSISSIPPI: 'MS', MISSOURI: 'MO', MONTANA: 'MT', NEBRASKA: 'NE',
|
|
8
|
+
NEVADA: 'NV', 'NEW HAMPSHIRE': 'NH', 'NEW JERSEY': 'NJ', 'NEW MEXICO': 'NM', 'NEW YORK': 'NY', 'NORTH CAROLINA': 'NC',
|
|
9
|
+
'NORTH DAKOTA': 'ND', OHIO: 'OH', OKLAHOMA: 'OK', OREGON: 'OR', PENNSYLVANIA: 'PA', 'RHODE ISLAND': 'RI',
|
|
10
|
+
'SOUTH CAROLINA': 'SC', 'SOUTH DAKOTA': 'SD', TENNESSEE: 'TN', TEXAS: 'TX', UTAH: 'UT', VERMONT: 'VT', VIRGINIA: 'VA',
|
|
11
|
+
WASHINGTON: 'WA', 'WEST VIRGINIA': 'WV', WISCONSIN: 'WI', WYOMING: 'WY',
|
|
12
|
+
};
|
|
13
|
+
// A Canadian province comes as its name or its code: "ONTARIO" and "ON" on scans of one parcel.
|
|
14
|
+
const CANADA_PROVINCES = {
|
|
15
|
+
ALBERTA: 'AB', 'BRITISH COLUMBIA': 'BC', MANITOBA: 'MB', 'NEW BRUNSWICK': 'NB', 'NEWFOUNDLAND AND LABRADOR': 'NL',
|
|
16
|
+
'NOVA SCOTIA': 'NS', 'NORTHWEST TERRITORIES': 'NT', NUNAVUT: 'NU', ONTARIO: 'ON', 'PRINCE EDWARD ISLAND': 'PE',
|
|
17
|
+
QUEBEC: 'QC', SASKATCHEWAN: 'SK', YUKON: 'YT',
|
|
18
|
+
};
|
|
19
|
+
// DHL's own names for countries the English ones do not cover: the forms on its
|
|
20
|
+
// scans, and those of its country list for the countries whose zone is known.
|
|
21
|
+
const COUNTRIES = {
|
|
22
|
+
UK: 'GB', "PEOPLE'S REPUBLIC OF CHINA": 'CN', 'CHINA, PEOPLES REPUBLIC': 'CN', 'CZECH REPUBLIC': 'CZ',
|
|
23
|
+
'IRELAND, REPUBLIC OF': 'IE', 'KOREA, REPUBLIC OF (SOUTH K.)': 'KR', TURKEY: 'TR',
|
|
24
|
+
};
|
|
25
|
+
// Island groups an hour behind their mainland, which DHL files under the country.
|
|
26
|
+
const ISLANDS = {
|
|
27
|
+
'Europe/Madrid': { group: 'ES-CN', zone: 'Atlantic/Canary' },
|
|
28
|
+
'Europe/Lisbon': { group: 'PT-20', zone: 'Atlantic/Azores' },
|
|
29
|
+
};
|
|
30
|
+
function regionCode(region, names) {
|
|
31
|
+
const parts = region.toUpperCase().split(',').map((part) => part.trim());
|
|
32
|
+
return parts.find((part) => /^[A-Z]{2}$/.test(part)) ?? parts.map((part) => names[part]).find(Boolean) ?? '';
|
|
33
|
+
}
|
|
2
34
|
/**
|
|
3
|
-
* The zone of a DHL Express facility, from its "CITY - COUNTRY"
|
|
4
|
-
* the
|
|
5
|
-
*
|
|
35
|
+
* The zone of a DHL Express facility, from its location: "CITY - COUNTRY", and
|
|
36
|
+
* in the USA and Canada "CITY - REGION - COUNTRY" or "CITY, ST - COUNTRY". Null
|
|
37
|
+
* when the location does not settle one: a country with several clocks and no
|
|
38
|
+
* region DHL names, or a country it writes in a form this does not know.
|
|
39
|
+
*
|
|
40
|
+
* A US state or Canadian province with several clocks takes its majority zone.
|
|
41
|
+
* Spain and Portugal take their mainland's, except for a town or island of the
|
|
42
|
+
* Canary Islands or the Azores.
|
|
6
43
|
*/
|
|
7
44
|
export function facilityZone(location) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
45
|
+
const parts = location.split(' - ').map((part) => part.trim());
|
|
46
|
+
const country = parts.length > 1 ? parts.at(-1).toUpperCase() : '';
|
|
47
|
+
// The region is a part of its own, or the code that ends the city's.
|
|
48
|
+
const region = parts.length > 2 ? parts.at(-2) : /, ([A-Z]{2})$/i.exec(parts[0])?.[1] ?? '';
|
|
49
|
+
if (country === 'USA')
|
|
50
|
+
return usStateTimeZone(regionCode(region, US_STATES));
|
|
51
|
+
if (country === 'CANADA')
|
|
52
|
+
return canadaProvinceTimeZone(regionCode(region, CANADA_PROVINCES));
|
|
53
|
+
// DHL writes the article first or last: "THE PEOPLE'S REPUBLIC OF CHINA", "NETHERLANDS, THE".
|
|
54
|
+
const name = country.replace(/^THE /, '').replace(/, THE$/, '');
|
|
55
|
+
const zone = countryTimeZone(COUNTRIES[name] ?? name.replaceAll(',', ''));
|
|
56
|
+
const islands = zone ? ISLANDS[zone] : undefined;
|
|
57
|
+
return islands && regionHasTown(islands.group, parts[0]) ? islands.zone : zone;
|
|
14
58
|
}
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"carrier": "dhl-express",
|
|
3
3
|
"records": [
|
|
4
|
+
{
|
|
5
|
+
"number": "2522622410",
|
|
6
|
+
"role": "shipment",
|
|
7
|
+
"evidence": "open_source_example",
|
|
8
|
+
"source": {
|
|
9
|
+
"url": "https://github.com/iFoxz17/sc-delay-prediction/blob/b9e2f99fcdc157b4fb5ff3b0903e79f75f6b32cb/code/m4estro/LambdaTS/Carrier/sample.json",
|
|
10
|
+
"date": "2025-08-30"
|
|
11
|
+
},
|
|
12
|
+
"expect": {
|
|
13
|
+
"carrier": "unknown",
|
|
14
|
+
"confidence": "low",
|
|
15
|
+
"candidates": [
|
|
16
|
+
"dhl-express",
|
|
17
|
+
"dhl",
|
|
18
|
+
"mondial-relay",
|
|
19
|
+
"relais-colis",
|
|
20
|
+
"tipsa",
|
|
21
|
+
"estafeta"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"note": "A provider's reply for a DHL Express waybill, published as sample data with its scans. DHL has since issued the waybill to another shipment."
|
|
25
|
+
},
|
|
4
26
|
{
|
|
5
27
|
"number": "3408372365",
|
|
6
28
|
"role": "shipment",
|
|
@@ -72,8 +72,11 @@ export declare function townKey(name: string): string;
|
|
|
72
72
|
/**
|
|
73
73
|
* Whether a town of this name lies in the US state or Canadian province whose
|
|
74
74
|
* code also names a single-clock country: Chicago is in Illinois, not Israel,
|
|
75
|
-
* and Koeln is not in Delaware.
|
|
76
|
-
*
|
|
75
|
+
* and Koeln is not in Delaware. Also whether it lies in an island group that
|
|
76
|
+
* keeps another clock than its country, by ISO 3166-2 code: the Canary Islands
|
|
77
|
+
* (`ES-CN`) and the Azores (`PT-20`), without the names their mainland also
|
|
78
|
+
* has. Only DE, IL, IN, MT, NL, SK and these two have town lists (generated
|
|
79
|
+
* from GeoNames); false for any other code.
|
|
77
80
|
*/
|
|
78
81
|
export declare function regionHasTown(code: string, town: string): boolean;
|
|
79
82
|
/** Epoch milliseconds (numbers or numeric strings); zero and negatives are rejected. */
|
package/dist/core/time/index.js
CHANGED
|
@@ -212,8 +212,11 @@ const REGION_TOWN_SETS = new Map(Object.entries(REGION_TOWNS).map(([code, towns]
|
|
|
212
212
|
/**
|
|
213
213
|
* Whether a town of this name lies in the US state or Canadian province whose
|
|
214
214
|
* code also names a single-clock country: Chicago is in Illinois, not Israel,
|
|
215
|
-
* and Koeln is not in Delaware.
|
|
216
|
-
*
|
|
215
|
+
* and Koeln is not in Delaware. Also whether it lies in an island group that
|
|
216
|
+
* keeps another clock than its country, by ISO 3166-2 code: the Canary Islands
|
|
217
|
+
* (`ES-CN`) and the Azores (`PT-20`), without the names their mainland also
|
|
218
|
+
* has. Only DE, IL, IN, MT, NL, SK and these two have town lists (generated
|
|
219
|
+
* from GeoNames); false for any other code.
|
|
217
220
|
*/
|
|
218
221
|
export function regionHasTown(code, town) {
|
|
219
222
|
return REGION_TOWN_SETS.get(code.trim().toUpperCase())?.has(townKey(town)) ?? false;
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Town keys of the US states and Canadian provinces whose code also names a single-clock country,
|
|
3
|
+
* and of the island groups that keep another clock than their country.
|
|
4
|
+
*/
|
|
2
5
|
export declare const REGION_TOWNS: Readonly<Record<string, string>>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* This file is generated by scripts/generate-region-towns.mjs. Do not edit. */
|
|
2
2
|
// Towns from GeoNames (https://www.geonames.org), CC BY 4.0, through places.
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Town keys of the US states and Canadian provinces whose code also names a single-clock country,
|
|
5
|
+
* and of the island groups that keep another clock than their country.
|
|
6
|
+
*/
|
|
4
7
|
export const REGION_TOWNS = {
|
|
5
8
|
DE: 'bear|bellefonte|bethanybeach|blades|bridgeville|brookside|camden|cheswold|claymont|clayton|delawarecity|delmar|dover|doverbasehousing|edgemoor|elsmere|felton|georgetown|glasgow|greenville|greenwood|harrington|highlandacres|hockessin|kentacres|laurel|lewes|longneck|middletown|milford|millsboro|milton|newark|newcastle|newport|northstar|oceanview|pikecreek|pikecreekvalley|rehobothbeach|risingsunlebanon|riverview|rodneyvillage|seaford|selbyville|smyrna|townsend|wilmington|wilmingtonmanor|woodsideeast|wyoming',
|
|
6
9
|
IL: 'abingdon|addison|albanypark|albers|albion|aledo|algonquin|alorton|alsip|altamont|alton|amboy|andalusia|anna|antioch|arcola|arlingtonheights|arthur|ashburn|ashland|assumption|astoria|athens|atlanta|atwood|auburn|auburngresham|aurora|aviston|avondale|bannockburn|barrington|barringtonhills|barry|bartlett|bartonville|batavia|beachpark|beardstown|beckemeyer|beecher|belleville|bellevue|bellwood|belmontcragin|belvidere|bement|benld|bensenville|benton|berkeley|berwyn|bethalto|bethany|bigrock|bloomingdale|bloomington|blueisland|bluemound|bolingbrook|boulderhill|bourbonnais|bradley|braidwood|breese|bridgeport|bridgeview|brighton|brightonpark|broadview|brookfield|buffalogrove|bullvalley|bunkerhill|burbank|burnham|burrridge|bushnell|byron|cahokia|cairo|calumetcity|calumetpark|cambria|cambridge|camppoint|canton|capron|carboncliff|carbondale|carlinville|carlyle|carmi|carolstream|carpentersville|carriermills|carrollton|carterville|carthage|cary|casey|caseyville|catlin|centralcity|centralia|centreville|cerrogordo|champaign|channahon|channellake|charleston|chatham|chatsworth|chebanse|chenoa|cherryvalley|chester|chicago|chicagoheights|chicagolawn|chicagoloop|chicagoridge|chillicothe|chrisman|christopher|cicero|clarendonhills|clifton|clinton|coalcity|coalvalley|cobden|colchester|colfax|collinsville|colona|columbia|cortland|countryclubhills|countryside|crainville|cresthill|crestwood|crete|crevecoeur|crystallake|crystallawns|cuba|danvers|danville|darien|davisjunction|decatur|deerfield|deerpark|dekalb|delavan|depue|desoto|desplaines|diamond|divernon|dixmoor|dixon|dolton|douglas|downersgrove|dunlap|dupo|duquoin|durand|dwight|earlville|eastalton|eastdubuque|eastdundee|eastgarfieldpark|easthazelcrest|eastmoline|eastpeoria|eaststlouis|edgewater|edinburg|edwardsville|effingham|elburn|eldorado|elgin|elizabethtown|elkgrovevillage|elmhurst|elmwood|elmwoodpark|elpaso|elwood|energy|englewood|erie|eureka|evanston|evergreenpark|fairbury|fairfield|fairmont|fairmontcity|fairviewheights|farmercity|farmington|fisher|flanagan|flora|flossmoor|fordheights|forestlake|forestpark|forrest|forreston|forsyth|foxlake|foxlakehills|foxrivergrove|frankfort|frankfortsquare|franklinpark|freeburg|freeport|fulton|gagepark|gageslake|galena|galesburg|galva|gardner|geneseo|geneva|genoa|georgetown|germantown|germantownhills|gibsoncity|gifford|gilberts|gillespie|gilman|girard|glasford|glencarbon|glencoe|glendaleheights|glenellyn|glenview|glenwood|godfrey|golconda|goodingsgrove|goreville|grandboulevard|grandview|grandwoodpark|granitecity|grantpark|granville|grayslake|grayville|greatergrandcrossing|greenfield|greenoaks|greenrock|greenup|greenville|gridley|griggsville|gurnee|hainesville|hamilton|hampshire|hampton|hannacity|hanoverpark|hardin|harrisburg|harristown|hartford|harvard|harvey|harwoodheights|havana|hawthornwoods|hazelcrest|hebron|hennepin|henry|heritagelake|herrin|herscher|heyworth|hickoryhills|highland|highlandpark|highwood|hillcrest|hillsboro|hillside|hinckley|hinsdale|hodgkins|hoffmanestates|holidayshores|homer|homerglen|hometown|homewood|hoopeston|hudson|huntley|hydepark|ina|indianheadpark|ingallspark|inverness|irvingpark|islandlake|itasca|jacksonville|jerome|jerseyville|johnsburg|johnstoncity|joliet|jonesboro|justice|kankakee|kenilworth|kenwood|kewanee|kildeer|kincaid|kingston|kirkland|knollwood|knoxville|lacon|ladd|lagrange|lagrangepark|laharpe|lakebarrington|lakebluff|lakecamelot|lakecatherine|lakeforest|lakeholiday|lakeinthehills|lakemoor|lakeofthewoods|lakesummerset|lakevilla|lakewood|lakewoodshores|lakezurich|lanark|lansing|lasalle|lawrenceville|lebanon|lelandgrove|lemont|lena|leroy|lewistown|lexington|libertyville|lilylake|limestone|lincoln|lincolnpark|lincolnshire|lincolnsquare|lincolnwood|lindenhurst|lisle|litchfield|lockport|logansquare|lombard|longcreek|longgrove|longlake|louisville|lovespark|lovington|lowerwestside|lynwood|lyons|machesneypark|mackinaw|macomb|macon|madison|mahomet|malta|manhattan|manito|manteno|maplepark|marengo|marion|marissa|markham|maroa|marquetteheights|marseilles|marshall|martinsville|maryville|mascoutah|masoncity|matteson|mattoon|maywood|mccullomlake|mchenry|mckinleypark|mcleansboro|medinah|melrosepark|mendota|meredosia|merrionettepark|metamora|metropolis|midlothian|milan|milford|millstadt|minier|minonk|minooka|mitchell|mokena|moline|momence|monee|monmouth|montgomery|monticello|morganpark|morris|morrison|morrisonville|morton|mortongrove|moundcity|moweaqua|mtcarmel|mtcarroll|mtgreenwood|mtmorris|mtolive|mtprospect|mtpulaski|mtsterling|mtvernon|mtzion|mundelein|murphysboro|naperville|nashville|nauvoo|nearnorthside|nearsouthside|neoga|newark|newathens|newbaden|newberlin|newcity|newlenox|newton|niles|nokomis|normal|norridge|norriscity|northaurora|northbarrington|northbrook|northcenter|northchicago|northfield|northlake|northlawndale|northpekin|northpeoria|northriverside|oakbrook|oakbrookterrace|oakforest|oaklawn|oakpark|oakwood|oakwoodhills|oblong|odin|ofallon|oglesby|okawville|olney|olympiafields|onarga|oquawka|oregon|orion|orlandhills|orlandpark|oswego|ottawa|palatine|palestine|palosheights|paloshills|palospark|pana|paris|parkcity|parkforest|parkridge|parkwaygardenhomes|pawnee|paxton|payson|pecatonica|pekin|peoria|peoriaheights|peotone|peru|petersburg|philo|phoenix|pinckneyville|pingreegrove|pistakeehighlands|pittsfield|plainfield|plano|polo|pontiac|pontoonbeach|poplargrove|portagepark|portbarrington|portbyron|posen|prairiegrove|prestbury|prestonheights|princeton|princeville|prophetstown|prospectheights|quincy|ramsey|rantoul|redbud|richmond|richtonpark|riverdale|riverforest|rivergrove|riverside|riverton|riverwoods|roanoke|robbins|robinson|rochelle|rochester|rockdale|rockfalls|rockford|rockisland|rockton|rogerspark|rollingmeadows|rome|romeoville|roodhouse|roscoe|roselle|rosemont|rosewoodheights|rosiclare|rossville|roundlake|roundlakebeach|roundlakeheights|roundlakepark|roxana|royalton|rushville|salem|sandoval|sandwich|saukvillage|savanna|savoy|schaumburg|schillerpark|scottairforcebase|seneca|sesser|shawneetown|shelbyville|sheldon|sheridan|sherman|shiloh|shorewood|sidney|silvis|skokie|sleepyhollow|smithton|somonauk|southbarrington|southbeloit|southchicago|southchicagoheights|southelgin|southernview|southholland|southjacksonville|southlawndale|southpekin|southroxana|southshore|sparta|springfield|springgrove|springvalley|stanne|staunton|stcharles|steeleville|steger|stelmo|sterling|stickney|stillmanvalley|stjacob|stjoseph|stockton|stonepark|streamwood|streator|sugargrove|sullivan|summit|sumner|swansea|sycamore|taylorville|teutopolis|thegalenaterritory|thirdlake|thomasboro|thornton|tilton|tinleypark|toledo|tolono|toluca|toulon|towerlake|tremont|trenton|troy|tuscola|twingrove|ukrainianvillage|universitypark|upperalton|uptown|urbana|valmeyer|vandalia|venetianvillage|venice|vernonhills|vienna|villageofcamptonhills|villagrove|villapark|virden|virginia|volo|wadsworth|walnut|wamac|warren|warrensburg|warrenville|warsaw|wasco|washburn|washington|washingtonpark|waterloo|waterman|watseka|wauconda|waukegan|waverly|wayne|waynecity|westchester|westchicago|westdundee|westelsdon|westenglewood|westernsprings|westfrankfort|westgarfieldpark|westlawn|westmont|westpeoria|westridge|westtown|westville|wheaton|wheeling|whitehall|williamsville|willowbrook|willowsprings|wilmette|wilmington|winchester|windsor|winfield|winnebago|winnetka|winthropharbor|wonderlake|wooddale|woodlawn|woodridge|woodriver|woodstock|worden|worth|wyoming|yorkville|zeigler|zion',
|
|
@@ -8,4 +11,6 @@ export const REGION_TOWNS = {
|
|
|
8
11
|
MT: 'absarokee|anaconda|baker|belgrade|bigfork|bigsky|bigtimber|billings|bonnerwestriverside|boulder|bozeman|broadus|browning|butte|chester|chinook|choteau|circle|clancy|clinton|colstrip|columbiafalls|columbus|conrad|crowagency|cutbank|deerlodge|dillon|easthelena|eastmissoula|ekalaka|eureka|evergreen|forsyth|fourcorners|frenchtown|ftbelknapagency|ftbenton|glasgow|glendive|greatfalls|hamilton|hardin|harlowton|havre|helena|helenavalleynortheast|helenavalleynorthwest|helenavalleysoutheast|helenavalleywestcentral|helenawestside|hysham|jordan|kalispell|lakeside|lamedeer|laurel|lewistown|libby|lincoln|livingston|lockwood|lolo|malmstromairforcebase|malta|manhattan|milescity|missoula|montanacity|northbrowning|orchardhomes|pablo|philipsburg|plains|plentywood|polson|redlodge|ronan|roundup|ryegate|scobey|seeleylake|shelby|sidney|somers|southbrowning|stanford|stevensville|sunprairie|superior|terry|thompsonfalls|threeforks|townsend|virginiacity|warmsprings|westglendive|westyellowstone|whitefish|whitehall|whitesulphursprings|wibaux|winnett|wolfpoint',
|
|
9
12
|
NL: 'airportheights|baieverte|ballyhaly|bannerman|baybulls|bayroberts|baystgeorgesouth|bellsturn|bishopsfalls|bonavista|botwood|burgeo|burin|carbonear|centrevillewarehamtrinity|channelportauxbasques|churchillpark|clarenville|clarenvilleshoalharbour|clarkesbeach|clovellytrails|conceptionbaysouth|cornerbrook|cowanheights|deerlake|donovans|downtownstjohns|eastmeadows|evergreenvillage|fairviewacres|flatrock|fogoisland|fortune|ftwilliam|gambo|gander|georgestown|glendale|glenridgecrescent|glovertown|goulds|grandbank|grandfallswindsor|happyvalleygoosebay|harbourbreton|harbourgrace|harbourmainchapelscovelakeview|holyrood|humberarmsouth|irishtownsummerside|karwoodestates|kenmountterrace|kilbride|kingwilliamestates|kippens|labradorcity|lewisporte|littlecanada|logybaymiddlecoveoutercove|marystown|masseydrive|mtcashel|mtpearl|mundypond|nain|newwesvalley|paradise|pasadena|pearlgate|placentia|pleasantville|portugalcovestphillips|pouchcove|quidividi|rabbittown|sesamepark|sheaheights|sheshatshiu|signalhill|southlands|spaniardsbay|springdale|sprucemeadows|stalbans|stanthony|stephenville|stephenvillecrossing|stgeorges|stjohns|stlawrence|torbay|trinitybaynorth|twillingate|upperislandcove|victoria|virginiapark|wabana|wabush|waterfordvalley|whitbourne|wishingwellpark|witlessbay',
|
|
10
13
|
SK: 'adelaidechurchill|albertpark|alritchie|arborcreek|arcolaeast|argylepark|aspenridge|assiniboia|avalon|balgonie|battleford|biggar|blairmore|blairmoreurbancentre|boothill|brevoortpark|briarwood|brighton|buenavista|buffalonarrows|canora|carlyle|caswellhill|cathedral|centralbusinessdistrict|centresquare|citypark|collegepark|collegeparkeast|confederation|confederationpark|confederationurbancentre|coreneighbourhoods|coronationpark|creighton|dalmeny|davidson|delisle|dewdneyeast|dieppewesterra|downtown|dundonald|eastview|erindale|esterhazy|estevan|evergreen|exhibition|fairhaven|foamlake|forestgrove|ftquappelle|gladmerpark|gravelbourg|greystoneheights|grosvenorpark|hamptonvillage|harbourlanding|haultain|heritage|hillsdale|holidaypark|holliston|holmwood|hudsonbay|hudsonbaypark|humboldt|indianhead|kamsack|kensington|kerrobert|kindersley|kinggeorge|lakeridge|lakeview|lakewood|lakewoodurbancentre|langenburg|langham|lanigan|laronge|lawson|lawsonheights|lawsonheightsurbancentre|lumsden|macklin|maplecreek|martensville|masseyplace|mcnab|meadowgreen|meadowlake|melfort|melville|montgomeryplace|moosejaw|moosomin|mtroyal|nipawin|normanview|normanviewwest|northbattleford|northcentral|northeastregina|northpark|nutana|nutanapark|nutanasector|nutanaurbancentre|outlook|oxbow|pacificheights|parkridge|pelicannarrows|pilotbutte|pleasanthill|prairieview|preeceville|princealbert|queenelizabeth|regentpark|regina|reginabeach|riverheights|riversdale|rosemontmtroyal|rosetown|rosewood|rosthern|saskatoon|shaunavon|shellbrook|sherwoodmccarthy|silverspring|silverwoodheights|stonebridge|sutherland|swiftcurrent|tisdale|twinlakes|unity|universityheights|universityheightsurbancentre|uofslandssouthmanagementarea|uplands|varsityview|wadena|waldheim|walshacreslakeridgegardenridge|warman|watrous|westmount|westview|weyburn|whitecity|whitmorepark|wildwood|wilkie|willowgrove|wynyard|yorkton',
|
|
14
|
+
'ES-CN': 'adeje|agaete|aguimes|agulo|alajero|antigua|arafo|arona|arrecife|artenara|arucas|barlovento|brenaalta|brenabaja|buenavistadelnorte|candelaria|carrizal|centro|corralejo|costacalma|costateguise|crucedearinaga|elcotillo|elhierro|elpaso|fasnia|firgas|frontera|fuencalientedelapalma|fuerteventura|galdar|garachico|garafia|granadilladeabona|grancanaria|guia|guiadeisora|guimar|haria|hermigua|icoddelosvinos|ingenio|lagomera|lagraciosa|laguancha|lalaguna|lamatanzadeacentejo|lanzarote|laoliva|laorotava|lapalma|laspalmas|laspalmasdegrancanaria|lasrosas|lavictoriadeacentejo|lomodearico|losgigantes|losllanosdearidane|losrealejos|lossilos|macher|maspalomas|mazo|mogan|nazaret|pajara|playablanca|playadelasamericas|playadelingles|playadesanjuan|puertodelacruz|puertodelcarmen|puertodelrosario|puertorico|puntademujeres|puntagorda|puntallana|realejoalto|sanbartolome|sanbartolomedetirajana|sanjuandelarambla|sanmigueldeabona|sannicolas|sansebastiandelagomera|santabrigida|santacruzdelapalma|santacruzdetenerife|santalucia|santaursula|santiagodelteide|sauzal|tacoronte|tamaraceite|tanque|tazacorte|tegueste|teguise|tejeda|telde|tenerife|teror|tias|tijarafe|tinajo|tuineje|vallehermosa|valleseco|valsequillodegrancanaria|vecindario|vegadesanmateo|vilaflor|yaiza',
|
|
15
|
+
'PT-20': 'aguadepau|angradoheroismo|angustias|arrifes|biscoitos|cabouco|caisdopico|corvo|fajadebaixo|fenaisdaajuda|fenaisdaluz|feteira|flores|fontebastardo|furnas|graciosa|horta|lajes|lajesdasflores|mosteiros|nordeste|pico|pontadelgada|pontagarca|portojudeu|portomartins|povoacao|praiadavitoria|rabodepeixe|relva|ribeiragrande|ribeiraseca|ribeirinha|rostodecao|santabarbara|santacruzdagraciosa|santacruzdasflores|santamaria|saomateus|saomiguel|saoroquedopico|saosebastiao|terceira|velas|vilafrancadocampo',
|
|
11
16
|
};
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import { load } from 'cheerio';
|
|
11
11
|
import { DateTime } from 'luxon';
|
|
12
12
|
import timers from 'node:timers/promises';
|
|
13
|
-
import { facilityZone } from '../../carriers/dhl-express/clock.js';
|
|
14
13
|
import { carrierTimezone } from '../../core/catalog/index.js';
|
|
15
14
|
import { brandTimeZones, carrierIdFromName, carrierNameCountryZone } from '../../core/catalog/hints.js';
|
|
16
15
|
import { CarrierError, carrierErrorKind, ChallengeError, IndeterminateError, InputRequiredError, NoHistoryError, SchemaError, UpstreamHttpError, UpstreamNetworkError } from '../../core/errors/index.js';
|
|
@@ -18,6 +17,7 @@ import { runSteps } from '../../core/runner/index.js';
|
|
|
18
17
|
import { canadaProvinceTimeZone, countryCode, countryTimeZone, mislabeledLocalTime, mislabeledWallTime, regionHasTown, sharedClockZone, timeZoneCountry, usStateTimeZone, } from '../../core/time/index.js';
|
|
19
18
|
import { isRecord } from '../../core/types.js';
|
|
20
19
|
import { capturedBodies, loadCapture } from '../shared/capture.js';
|
|
20
|
+
import { facilityZone, LatestScan, relaysFacilityClock } from '../shared/facilityClock.js';
|
|
21
21
|
import { universalCarrierHints } from '../shared/hints.js';
|
|
22
22
|
import { event, isNotice, localEvent, numberOf, result } from '../shared/result.js';
|
|
23
23
|
import { carrierScan, markReturnLeg } from '../shared/scans.js';
|
|
@@ -130,12 +130,9 @@ function scanZone(payload, state, fallback, number, inNorthAmerica = false) {
|
|
|
130
130
|
if (zone !== 'UTC' && !(typeof name === 'string' && carrierNameCountryZone(name)))
|
|
131
131
|
return zone;
|
|
132
132
|
const location = typeof state.location === 'string' ? state.location.trim() : '';
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const facility = carrier === 'dhl-express' ? facilityZone(location) : null;
|
|
137
|
-
if (facility)
|
|
138
|
-
return facility;
|
|
133
|
+
// A facility's clock, whatever the label: no other step applies to it.
|
|
134
|
+
if (relaysFacilityClock(name))
|
|
135
|
+
return facilityZone(location);
|
|
139
136
|
const place = location.split(',').at(-1);
|
|
140
137
|
// A state or province the town confirms beats the country its code also
|
|
141
138
|
// names ("Chicago, IL"); a merely possible one comes after ("Koeln, DE").
|
|
@@ -220,10 +217,14 @@ function parseHistory(payload, trackingNumber, timezone = null) {
|
|
|
220
217
|
return wall && text ? `${wall}|${text}` : '';
|
|
221
218
|
};
|
|
222
219
|
const localClocks = new Map(entries.filter((entry) => entry.local).map((entry) => [copyKey(entry), entry.zone]).filter(([key]) => key));
|
|
220
|
+
const latest = new LatestScan(SOURCE);
|
|
223
221
|
for (const entry of entries) {
|
|
224
222
|
const { raw, name, scan } = entry;
|
|
225
223
|
const zone = (localClocks.size && !entry.local ? localClocks.get(copyKey(entry)) : undefined) ?? entry.zone;
|
|
226
|
-
const
|
|
224
|
+
const unplaced = !zone && relaysFacilityClock(name);
|
|
225
|
+
const parsed = localEvent((zone ? mislabeledLocalTime(raw.date, zone)?.iso : unplaced ? mislabeledWallTime(raw.date) : undefined) ?? raw.date, entry.wording);
|
|
226
|
+
if (parsed)
|
|
227
|
+
latest.see(mislabeledWallTime(raw.date) ?? '', unplaced);
|
|
227
228
|
// Nor has an offset-less date that no zone resolves: a wall time, not an instant.
|
|
228
229
|
if (parsed && !parsed.time) {
|
|
229
230
|
undated++;
|
|
@@ -241,6 +242,7 @@ function parseHistory(payload, trackingNumber, timezone = null) {
|
|
|
241
242
|
scans.push({ event: Object.assign(parsed, { stage: scan.stage, stage_source: 'carrier_map' }), scan });
|
|
242
243
|
}
|
|
243
244
|
markReturnLeg(scans);
|
|
245
|
+
latest.check();
|
|
244
246
|
if (!events.length) {
|
|
245
247
|
const fields = payload.states.flatMap((raw) => isRecord(raw) && Array.isArray(raw.require_fields) ? raw.require_fields : []);
|
|
246
248
|
if (fields.some((field) => isRecord(field) && field.name === 'zipcode')) {
|
|
@@ -293,6 +295,7 @@ export function parseParcelsAppHtml(html, trackingNumber, timezone = null) {
|
|
|
293
295
|
const carriers = [];
|
|
294
296
|
const scans = [];
|
|
295
297
|
let undated = 0;
|
|
298
|
+
const latest = new LatestScan(SOURCE);
|
|
296
299
|
nodes.each((_, node) => {
|
|
297
300
|
const row = $(node);
|
|
298
301
|
if (row.find('input, select, form').length)
|
|
@@ -323,6 +326,13 @@ export function parseParcelsAppHtml(html, trackingNumber, timezone = null) {
|
|
|
323
326
|
const state = { date: labeled.toISO(), ...(name ? { carrier: carriers.indexOf(name) } : {}) };
|
|
324
327
|
const zone = scanZone({ carriers }, state, timezone, numberOf(trackingNumber));
|
|
325
328
|
const scan = carrierScan(name ? carrierIdFromName(name) : undefined, description);
|
|
329
|
+
// The page prints no location, so no DHL Express scan has a facility to read its clock in.
|
|
330
|
+
const unplaced = !zone && relaysFacilityClock(name);
|
|
331
|
+
latest.see(mislabeledWallTime(state.date) ?? '', unplaced);
|
|
332
|
+
if (unplaced) {
|
|
333
|
+
undated++;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
326
336
|
const parsed = event((zone ? mislabeledLocalTime(state.date, zone)?.iso : undefined) ?? state.date, scan?.wording ?? description);
|
|
327
337
|
if (parsed)
|
|
328
338
|
events.push(parsed);
|
|
@@ -330,6 +340,7 @@ export function parseParcelsAppHtml(html, trackingNumber, timezone = null) {
|
|
|
330
340
|
scans.push({ event: Object.assign(parsed, { stage: scan.stage, stage_source: 'carrier_map' }), scan });
|
|
331
341
|
});
|
|
332
342
|
markReturnLeg(scans);
|
|
343
|
+
latest.check();
|
|
333
344
|
return { ...result(events, SOURCE), ...(undated ? { undated_event_count: undated } : {}),
|
|
334
345
|
...universalCarrierHints(carriers.slice(0, 20), numberOf(trackingNumber)) };
|
|
335
346
|
}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 17TRACK (t.17track.net), a universal aggregator with richer China Post history.
|
|
3
|
-
*
|
|
4
|
-
* Transport: the browser service loads the public tracking page and captures
|
|
5
|
-
* the page's own `restapi` response; unsigned direct probes answer HTTP 200
|
|
6
|
-
* with a rejection code instead of history (verified 2026-09-10). The parser
|
|
7
|
-
* keeps only history whose shipment number equals the requested one, and turns
|
|
8
|
-
* the provider's own rejection codes into typed errors so routing can tell a
|
|
9
|
-
* verification wall from an outage.
|
|
10
|
-
*/
|
|
11
1
|
import type { AdapterFactory } from '../../core/adapter/index.js';
|
|
12
2
|
import { ChallengeError, NoHistoryError, TransportError } from '../../core/errors/index.js';
|
|
13
3
|
import type { CarrierResult } from '../../core/result/index.js';
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 17TRACK (t.17track.net), a universal aggregator with richer China Post history.
|
|
3
|
+
*
|
|
4
|
+
* Transport: the browser service loads the public tracking page and captures
|
|
5
|
+
* the page's own `restapi` response; unsigned direct probes answer HTTP 200
|
|
6
|
+
* with a rejection code instead of history (verified 2026-09-10). The parser
|
|
7
|
+
* keeps only history whose shipment number equals the requested one, and turns
|
|
8
|
+
* the provider's own rejection codes into typed errors so routing can tell a
|
|
9
|
+
* verification wall from an outage.
|
|
10
|
+
*/
|
|
11
|
+
import { DateTime } from 'luxon';
|
|
1
12
|
import { ChallengeError, NoHistoryError, NotFoundError, SchemaError, TransportError } from '../../core/errors/index.js';
|
|
2
13
|
import { runSteps } from '../../core/runner/index.js';
|
|
3
14
|
import { isRecord } from '../../core/types.js';
|
|
4
15
|
import { universalCarrierHints } from '../shared/hints.js';
|
|
5
16
|
import { capturedBodies, captureFailure, loadCapture } from '../shared/capture.js';
|
|
17
|
+
import { facilityZone, LatestScan, relaysFacilityClock } from '../shared/facilityClock.js';
|
|
6
18
|
import { numberOf, result, text } from '../shared/result.js';
|
|
7
19
|
import { seventeenTrackEvent } from './events.js';
|
|
8
20
|
const SOURCE = '17TRACK';
|
|
@@ -57,6 +69,12 @@ function lookupError(code, providerMessage) {
|
|
|
57
69
|
? new SeventeenTrackVerificationError(code, providerMessage)
|
|
58
70
|
: new SeventeenTrackLookupError('lookup_unavailable', code, providerMessage);
|
|
59
71
|
}
|
|
72
|
+
/** The offset-less clock of a scan whose offset 17TRACK added itself. */
|
|
73
|
+
function facilityWall(raw) {
|
|
74
|
+
const clock = isRecord(raw.time_raw) && raw.time_raw.timezone === null ? raw.time_raw : null;
|
|
75
|
+
return clock && typeof clock.date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(clock.date)
|
|
76
|
+
&& typeof clock.time === 'string' && /^\d{2}:\d{2}:\d{2}$/.test(clock.time) ? `${clock.date}T${clock.time}` : null;
|
|
77
|
+
}
|
|
60
78
|
export function parse17TrackResponse(payload, trackingNumber) {
|
|
61
79
|
const number = numberOf(trackingNumber);
|
|
62
80
|
if (!isRecord(payload) || !isRecord(payload.meta) || !Number.isInteger(payload.meta.code)) {
|
|
@@ -91,6 +109,7 @@ export function parse17TrackResponse(payload, trackingNumber) {
|
|
|
91
109
|
const events = [];
|
|
92
110
|
let count = 0;
|
|
93
111
|
let undated = 0;
|
|
112
|
+
const latest = new LatestScan(SOURCE);
|
|
94
113
|
for (const provider of tracking.providers) {
|
|
95
114
|
if (!isRecord(provider) || !Array.isArray(provider.events))
|
|
96
115
|
throw new SchemaError(SOURCE, '17TRACK returned invalid provider history');
|
|
@@ -103,17 +122,29 @@ export function parse17TrackResponse(payload, trackingNumber) {
|
|
|
103
122
|
throw new SchemaError(SOURCE, '17TRACK returned invalid events');
|
|
104
123
|
if (raw.time_utc == null && raw.time_iso == null)
|
|
105
124
|
undated++;
|
|
125
|
+
// A facility's clock, on which 17TRACK puts one offset for the whole parcel.
|
|
126
|
+
const wall = relaysFacilityClock(name) ? facilityWall(raw) : null;
|
|
127
|
+
const zone = wall ? facilityZone(text(raw.location)) : null;
|
|
128
|
+
const placed = wall && zone ? DateTime.fromISO(wall, { zone }) : null;
|
|
106
129
|
let parsed;
|
|
107
130
|
try {
|
|
108
|
-
parsed = seventeenTrackEvent(raw, operator);
|
|
131
|
+
parsed = seventeenTrackEvent(placed?.isValid ? { ...raw, time_utc: placed.toUTC().toISO() } : raw, operator);
|
|
109
132
|
}
|
|
110
133
|
catch (cause) {
|
|
111
134
|
throw new SchemaError(SOURCE, '17TRACK returned an invalid tracking event', { cause });
|
|
112
135
|
}
|
|
113
|
-
if (parsed)
|
|
136
|
+
if (!parsed)
|
|
137
|
+
continue;
|
|
138
|
+
const unplaced = wall !== null && !placed?.isValid;
|
|
139
|
+
latest.see(wall ?? String(raw.time_iso ?? raw.time_utc).slice(0, 19), unplaced);
|
|
140
|
+
// With no zone for the facility it is a wall time, counted like a row without a date.
|
|
141
|
+
if (unplaced)
|
|
142
|
+
undated++;
|
|
143
|
+
else
|
|
114
144
|
events.push(parsed);
|
|
115
145
|
}
|
|
116
146
|
}
|
|
147
|
+
latest.check();
|
|
117
148
|
if (count === 0 && isRecord(shipment.latest_status) && shipment.latest_status.status === 'NotFound') {
|
|
118
149
|
throw new NotFoundError(SOURCE);
|
|
119
150
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { facilityZone } from '../../carriers/dhl-express/clock.js';
|
|
2
|
+
import type { UniversalSource } from './result.js';
|
|
3
|
+
/**
|
|
4
|
+
* DHL Express dates a scan with its facility's clock and no offset, and the
|
|
5
|
+
* providers relay that clock under a label of their own: UTC at ParcelsApp, one
|
|
6
|
+
* offset for the whole parcel at 17TRACK. Ship24 relays it with the facility's
|
|
7
|
+
* offset, and DHL's mobile API gives the same clocks scan for scan (checked
|
|
8
|
+
* 2026-10-06). So such a scan is read in its facility's zone, and with no zone
|
|
9
|
+
* for the facility it is a wall time that no label makes an instant.
|
|
10
|
+
*/
|
|
11
|
+
export declare function relaysFacilityClock(carrierName: unknown): boolean;
|
|
12
|
+
export { facilityZone };
|
|
13
|
+
/**
|
|
14
|
+
* The newest wall clock of a reply, and whether that scan was left without an
|
|
15
|
+
* instant. A reply whose latest scan has none cannot say where the parcel is,
|
|
16
|
+
* and an older scan must not stand in for it.
|
|
17
|
+
*/
|
|
18
|
+
export declare class LatestScan {
|
|
19
|
+
private readonly source;
|
|
20
|
+
private wall;
|
|
21
|
+
private unplaced;
|
|
22
|
+
constructor(source: UniversalSource);
|
|
23
|
+
see(wall: string, unplaced: boolean): void;
|
|
24
|
+
check(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { facilityZone } from '../../carriers/dhl-express/clock.js';
|
|
2
|
+
import { carrierIdFromName } from '../../core/catalog/hints.js';
|
|
3
|
+
import { IndeterminateError } from '../../core/errors/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* DHL Express dates a scan with its facility's clock and no offset, and the
|
|
6
|
+
* providers relay that clock under a label of their own: UTC at ParcelsApp, one
|
|
7
|
+
* offset for the whole parcel at 17TRACK. Ship24 relays it with the facility's
|
|
8
|
+
* offset, and DHL's mobile API gives the same clocks scan for scan (checked
|
|
9
|
+
* 2026-10-06). So such a scan is read in its facility's zone, and with no zone
|
|
10
|
+
* for the facility it is a wall time that no label makes an instant.
|
|
11
|
+
*/
|
|
12
|
+
export function relaysFacilityClock(carrierName) {
|
|
13
|
+
return typeof carrierName === 'string' && carrierIdFromName(carrierName) === 'dhl-express';
|
|
14
|
+
}
|
|
15
|
+
export { facilityZone };
|
|
16
|
+
/**
|
|
17
|
+
* The newest wall clock of a reply, and whether that scan was left without an
|
|
18
|
+
* instant. A reply whose latest scan has none cannot say where the parcel is,
|
|
19
|
+
* and an older scan must not stand in for it.
|
|
20
|
+
*/
|
|
21
|
+
export class LatestScan {
|
|
22
|
+
source;
|
|
23
|
+
wall = '';
|
|
24
|
+
unplaced = false;
|
|
25
|
+
constructor(source) {
|
|
26
|
+
this.source = source;
|
|
27
|
+
}
|
|
28
|
+
see(wall, unplaced) {
|
|
29
|
+
if (wall > this.wall || (wall === this.wall && unplaced))
|
|
30
|
+
[this.wall, this.unplaced] = [wall, unplaced];
|
|
31
|
+
}
|
|
32
|
+
check() {
|
|
33
|
+
if (this.unplaced)
|
|
34
|
+
throw new IndeterminateError(this.source, `${this.source} cannot place the latest DHL Express scan`);
|
|
35
|
+
}
|
|
36
|
+
}
|
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.25.
|
|
3
|
+
"version": "0.25.3-main.366",
|
|
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",
|