universal-parcel-scraper 0.0.0-stage → 0.2.0
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/LICENSE +201 -0
- package/NOTICE +15 -0
- package/README.md +123 -2
- package/data/carrier-order.json +107 -0
- package/data/carrier.schema.json +415 -0
- package/data/catalog.json +3969 -0
- package/data/coverage-summary.json +17 -0
- package/data/detection-golden.json +5375 -0
- package/data/stages.json +13 -0
- package/dist/browser/scraper.js +9292 -0
- package/dist/carriers/aliexpress/adapter.d.ts +30 -0
- package/dist/carriers/aliexpress/adapter.js +164 -0
- package/dist/carriers/aliexpress/carrier.json +59 -0
- package/dist/carriers/aliexpress/numbers.json +36 -0
- package/dist/carriers/aliexpress/status.d.ts +31 -0
- package/dist/carriers/aliexpress/status.js +79 -0
- package/dist/carriers/aliexpress/statuses.json +355 -0
- package/dist/carriers/amazon-logistics/carrier.json +94 -0
- package/dist/carriers/amazon-logistics/numbers.json +307 -0
- package/dist/carriers/amazon-shipping/adapter.d.ts +34 -0
- package/dist/carriers/amazon-shipping/adapter.js +292 -0
- package/dist/carriers/amazon-shipping/carrier.json +96 -0
- package/dist/carriers/amazon-shipping/eligibility.d.ts +3 -0
- package/dist/carriers/amazon-shipping/eligibility.js +16 -0
- package/dist/carriers/amazon-shipping/numbers.json +67 -0
- package/dist/carriers/amazon-shipping/status.d.ts +26 -0
- package/dist/carriers/amazon-shipping/status.js +118 -0
- package/dist/carriers/amazon-shipping/statuses.json +19 -0
- package/dist/carriers/an-post/carrier.json +38 -0
- package/dist/carriers/an-post/numbers.json +33 -0
- package/dist/carriers/aramex/adapter.d.ts +11 -0
- package/dist/carriers/aramex/adapter.js +49 -0
- package/dist/carriers/aramex/carrier.json +62 -0
- package/dist/carriers/aramex/numbers.json +71 -0
- package/dist/carriers/aramex/parser.d.ts +5 -0
- package/dist/carriers/aramex/parser.js +83 -0
- package/dist/carriers/aramex/status.d.ts +2 -0
- package/dist/carriers/aramex/status.js +16 -0
- package/dist/carriers/aramex/statuses.json +41 -0
- package/dist/carriers/asendia/adapter.d.ts +82 -0
- package/dist/carriers/asendia/adapter.js +342 -0
- package/dist/carriers/asendia/carrier.json +91 -0
- package/dist/carriers/asendia/numbers.json +177 -0
- package/dist/carriers/asendia/probe.d.ts +31 -0
- package/dist/carriers/asendia/probe.js +355 -0
- package/dist/carriers/asendia/status.d.ts +41 -0
- package/dist/carriers/asendia/status.js +147 -0
- package/dist/carriers/asendia/statuses.json +877 -0
- package/dist/carriers/australia-post/adapter.d.ts +20 -0
- package/dist/carriers/australia-post/adapter.js +175 -0
- package/dist/carriers/australia-post/carrier.json +72 -0
- package/dist/carriers/australia-post/numbers.json +66 -0
- package/dist/carriers/australia-post/status.d.ts +2 -0
- package/dist/carriers/australia-post/status.js +34 -0
- package/dist/carriers/australia-post/statuses.json +172 -0
- package/dist/carriers/austrian-post/adapter.d.ts +13 -0
- package/dist/carriers/austrian-post/adapter.js +101 -0
- package/dist/carriers/austrian-post/carrier.json +58 -0
- package/dist/carriers/austrian-post/numbers.json +73 -0
- package/dist/carriers/austrian-post/status.d.ts +3 -0
- package/dist/carriers/austrian-post/status.js +30 -0
- package/dist/carriers/austrian-post/statuses.json +47 -0
- package/dist/carriers/blue-dart/adapter.d.ts +11 -0
- package/dist/carriers/blue-dart/adapter.js +34 -0
- package/dist/carriers/blue-dart/carrier.json +59 -0
- package/dist/carriers/blue-dart/numbers.json +113 -0
- package/dist/carriers/blue-dart/parser.d.ts +3 -0
- package/dist/carriers/blue-dart/parser.js +75 -0
- package/dist/carriers/blue-dart/status.d.ts +2 -0
- package/dist/carriers/blue-dart/status.js +14 -0
- package/dist/carriers/blue-dart/statuses.json +59 -0
- package/dist/carriers/bpost/adapter.d.ts +11 -0
- package/dist/carriers/bpost/adapter.js +46 -0
- package/dist/carriers/bpost/carrier.json +81 -0
- package/dist/carriers/bpost/numbers.json +103 -0
- package/dist/carriers/bpost/parser.d.ts +3 -0
- package/dist/carriers/bpost/parser.js +84 -0
- package/dist/carriers/bpost/status.d.ts +2 -0
- package/dist/carriers/bpost/status.js +18 -0
- package/dist/carriers/bpost/statuses.json +96 -0
- package/dist/carriers/bring-posten/adapter.d.ts +11 -0
- package/dist/carriers/bring-posten/adapter.js +35 -0
- package/dist/carriers/bring-posten/carrier.json +91 -0
- package/dist/carriers/bring-posten/numbers.json +33 -0
- package/dist/carriers/bring-posten/parser.d.ts +3 -0
- package/dist/carriers/bring-posten/parser.js +95 -0
- package/dist/carriers/bring-posten/status.d.ts +3 -0
- package/dist/carriers/bring-posten/status.js +23 -0
- package/dist/carriers/bring-posten/statuses.json +33 -0
- package/dist/carriers/brt/adapter.d.ts +11 -0
- package/dist/carriers/brt/adapter.js +36 -0
- package/dist/carriers/brt/carrier.json +80 -0
- package/dist/carriers/brt/numbers.json +91 -0
- package/dist/carriers/brt/parser.d.ts +3 -0
- package/dist/carriers/brt/parser.js +84 -0
- package/dist/carriers/brt/status.d.ts +2 -0
- package/dist/carriers/brt/status.js +14 -0
- package/dist/carriers/brt/statuses.json +53 -0
- package/dist/carriers/c-chez-vous/adapter.d.ts +31 -0
- package/dist/carriers/c-chez-vous/adapter.js +181 -0
- package/dist/carriers/c-chez-vous/carrier.json +69 -0
- package/dist/carriers/c-chez-vous/numbers.json +31 -0
- package/dist/carriers/c-chez-vous/status.d.ts +27 -0
- package/dist/carriers/c-chez-vous/status.js +19 -0
- package/dist/carriers/c-chez-vous/statuses.json +10 -0
- package/dist/carriers/canada-post/adapter.d.ts +40 -0
- package/dist/carriers/canada-post/adapter.js +78 -0
- package/dist/carriers/canada-post/carrier.json +77 -0
- package/dist/carriers/canada-post/numbers.json +81 -0
- package/dist/carriers/canada-post/parser.d.ts +5 -0
- package/dist/carriers/canada-post/parser.js +202 -0
- package/dist/carriers/canada-post/status.d.ts +9 -0
- package/dist/carriers/canada-post/status.js +67 -0
- package/dist/carriers/canada-post/statuses.json +171 -0
- package/dist/carriers/canpar/adapter.d.ts +11 -0
- package/dist/carriers/canpar/adapter.js +40 -0
- package/dist/carriers/canpar/carrier.json +74 -0
- package/dist/carriers/canpar/numbers.json +71 -0
- package/dist/carriers/canpar/parser.d.ts +3 -0
- package/dist/carriers/canpar/parser.js +75 -0
- package/dist/carriers/canpar/status.d.ts +2 -0
- package/dist/carriers/canpar/status.js +16 -0
- package/dist/carriers/canpar/statuses.json +82 -0
- package/dist/carriers/china-post/carrier.json +38 -0
- package/dist/carriers/china-post/numbers.json +59 -0
- package/dist/carriers/chronopost/carrier.json +84 -0
- package/dist/carriers/chronopost/numbers.json +110 -0
- package/dist/carriers/chronopost/statuses.json +47 -0
- package/dist/carriers/ciblex/adapter.d.ts +17 -0
- package/dist/carriers/ciblex/adapter.js +46 -0
- package/dist/carriers/ciblex/carrier.json +71 -0
- package/dist/carriers/ciblex/numbers.json +63 -0
- package/dist/carriers/ciblex/parser.d.ts +4 -0
- package/dist/carriers/ciblex/parser.js +100 -0
- package/dist/carriers/ciblex/status.d.ts +24 -0
- package/dist/carriers/ciblex/status.js +55 -0
- package/dist/carriers/ciblex/statuses.json +221 -0
- package/dist/carriers/colis-prive/adapter.d.ts +39 -0
- package/dist/carriers/colis-prive/adapter.js +0 -0
- package/dist/carriers/colis-prive/carrier.json +72 -0
- package/dist/carriers/colis-prive/numbers.json +97 -0
- package/dist/carriers/colis-prive/status.d.ts +30 -0
- package/dist/carriers/colis-prive/status.js +85 -0
- package/dist/carriers/colis-prive/statuses.json +39 -0
- package/dist/carriers/colisweb/adapter.d.ts +29 -0
- package/dist/carriers/colisweb/adapter.js +166 -0
- package/dist/carriers/colisweb/carrier.json +63 -0
- package/dist/carriers/colisweb/numbers.json +39 -0
- package/dist/carriers/colisweb/status.d.ts +32 -0
- package/dist/carriers/colisweb/status.js +61 -0
- package/dist/carriers/colisweb/statuses.json +25 -0
- package/dist/carriers/correios-br/adapter.d.ts +16 -0
- package/dist/carriers/correios-br/adapter.js +78 -0
- package/dist/carriers/correios-br/carrier.json +64 -0
- package/dist/carriers/correios-br/model/LICENSE +21 -0
- package/dist/carriers/correios-br/model/captcha.onnx +0 -0
- package/dist/carriers/correios-br/numbers.json +72 -0
- package/dist/carriers/correios-br/ocr-worker.mjs +53 -0
- package/dist/carriers/correios-br/ocr.d.ts +10 -0
- package/dist/carriers/correios-br/ocr.js +83 -0
- package/dist/carriers/correios-br/parser.d.ts +4 -0
- package/dist/carriers/correios-br/parser.js +79 -0
- package/dist/carriers/correios-br/status.d.ts +2 -0
- package/dist/carriers/correios-br/status.js +17 -0
- package/dist/carriers/correios-br/statuses.json +16 -0
- package/dist/carriers/correos-chile/adapter.d.ts +12 -0
- package/dist/carriers/correos-chile/adapter.js +85 -0
- package/dist/carriers/correos-chile/carrier.json +62 -0
- package/dist/carriers/correos-chile/numbers.json +38 -0
- package/dist/carriers/correos-chile/parser.d.ts +9 -0
- package/dist/carriers/correos-chile/parser.js +120 -0
- package/dist/carriers/correos-chile/status.d.ts +3 -0
- package/dist/carriers/correos-chile/status.js +17 -0
- package/dist/carriers/correos-chile/statuses.json +12 -0
- package/dist/carriers/correos-express/adapter.d.ts +11 -0
- package/dist/carriers/correos-express/adapter.js +32 -0
- package/dist/carriers/correos-express/carrier.json +65 -0
- package/dist/carriers/correos-express/numbers.json +80 -0
- package/dist/carriers/correos-express/parser.d.ts +3 -0
- package/dist/carriers/correos-express/parser.js +97 -0
- package/dist/carriers/correos-express/status.d.ts +2 -0
- package/dist/carriers/correos-express/status.js +25 -0
- package/dist/carriers/correos-express/statuses.json +19 -0
- package/dist/carriers/correos-spain/adapter.d.ts +15 -0
- package/dist/carriers/correos-spain/adapter.js +191 -0
- package/dist/carriers/correos-spain/carrier.json +92 -0
- package/dist/carriers/correos-spain/numbers.json +149 -0
- package/dist/carriers/correos-spain/status.d.ts +17 -0
- package/dist/carriers/correos-spain/status.js +51 -0
- package/dist/carriers/correos-spain/statuses.json +46 -0
- package/dist/carriers/ctt/adapter.d.ts +34 -0
- package/dist/carriers/ctt/adapter.js +320 -0
- package/dist/carriers/ctt/carrier.json +72 -0
- package/dist/carriers/ctt/numbers.json +33 -0
- package/dist/carriers/ctt/status.d.ts +16 -0
- package/dist/carriers/ctt/status.js +16 -0
- package/dist/carriers/ctt/statuses.json +15 -0
- package/dist/carriers/ctt-express/adapter.d.ts +11 -0
- package/dist/carriers/ctt-express/adapter.js +41 -0
- package/dist/carriers/ctt-express/carrier.json +64 -0
- package/dist/carriers/ctt-express/numbers.json +59 -0
- package/dist/carriers/ctt-express/parser.d.ts +3 -0
- package/dist/carriers/ctt-express/parser.js +107 -0
- package/dist/carriers/ctt-express/status.d.ts +2 -0
- package/dist/carriers/ctt-express/status.js +17 -0
- package/dist/carriers/ctt-express/statuses.json +15 -0
- package/dist/carriers/dachser/adapter.d.ts +25 -0
- package/dist/carriers/dachser/adapter.js +168 -0
- package/dist/carriers/dachser/carrier.json +80 -0
- package/dist/carriers/dachser/numbers.json +66 -0
- package/dist/carriers/dachser/status.d.ts +40 -0
- package/dist/carriers/dachser/status.js +57 -0
- package/dist/carriers/dachser/statuses.json +18 -0
- package/dist/carriers/delhivery/adapter.d.ts +11 -0
- package/dist/carriers/delhivery/adapter.js +36 -0
- package/dist/carriers/delhivery/carrier.json +50 -0
- package/dist/carriers/delhivery/numbers.json +108 -0
- package/dist/carriers/delhivery/parser.d.ts +3 -0
- package/dist/carriers/delhivery/parser.js +81 -0
- package/dist/carriers/delhivery/status.d.ts +2 -0
- package/dist/carriers/delhivery/status.js +11 -0
- package/dist/carriers/delhivery/statuses.json +11 -0
- package/dist/carriers/delivengo/carrier.json +56 -0
- package/dist/carriers/delivengo/numbers.json +31 -0
- package/dist/carriers/delivengo/statuses.json +5 -0
- package/dist/carriers/dhl/adapter.d.ts +70 -0
- package/dist/carriers/dhl/adapter.js +308 -0
- package/dist/carriers/dhl/carrier.json +98 -0
- package/dist/carriers/dhl/numbers.json +153 -0
- package/dist/carriers/dhl/status.d.ts +14 -0
- package/dist/carriers/dhl/status.js +45 -0
- package/dist/carriers/dhl/statuses.json +27 -0
- package/dist/carriers/dhl-ecommerce/adapter.d.ts +50 -0
- package/dist/carriers/dhl-ecommerce/adapter.js +179 -0
- package/dist/carriers/dhl-ecommerce/carrier.json +97 -0
- package/dist/carriers/dhl-ecommerce/numbers.json +130 -0
- package/dist/carriers/dhl-ecommerce/status.d.ts +13 -0
- package/dist/carriers/dhl-ecommerce/status.js +57 -0
- package/dist/carriers/dhl-ecommerce/statuses.json +23 -0
- package/dist/carriers/dpd/adapter.d.ts +70 -0
- package/dist/carriers/dpd/adapter.js +809 -0
- package/dist/carriers/dpd/carrier.json +111 -0
- package/dist/carriers/dpd/numbers.json +43 -0
- package/dist/carriers/dpd/status.d.ts +39 -0
- package/dist/carriers/dpd/status.js +93 -0
- package/dist/carriers/dpd/statuses.json +144 -0
- package/dist/carriers/dpd-fr/adapter.d.ts +42 -0
- package/dist/carriers/dpd-fr/adapter.js +288 -0
- package/dist/carriers/dpd-fr/carrier.json +83 -0
- package/dist/carriers/dpd-fr/numbers.json +80 -0
- package/dist/carriers/dpd-fr/status.d.ts +21 -0
- package/dist/carriers/dpd-fr/status.js +93 -0
- package/dist/carriers/dpd-fr/statuses.json +325 -0
- package/dist/carriers/dtdc/adapter.d.ts +11 -0
- package/dist/carriers/dtdc/adapter.js +39 -0
- package/dist/carriers/dtdc/carrier.json +77 -0
- package/dist/carriers/dtdc/numbers.json +50 -0
- package/dist/carriers/dtdc/parser.d.ts +3 -0
- package/dist/carriers/dtdc/parser.js +90 -0
- package/dist/carriers/dtdc/status.d.ts +2 -0
- package/dist/carriers/dtdc/status.js +15 -0
- package/dist/carriers/dtdc/statuses.json +65 -0
- package/dist/carriers/ecoscooting/adapter.d.ts +11 -0
- package/dist/carriers/ecoscooting/adapter.js +42 -0
- package/dist/carriers/ecoscooting/carrier.json +76 -0
- package/dist/carriers/ecoscooting/numbers.json +291 -0
- package/dist/carriers/ecoscooting/parser.d.ts +3 -0
- package/dist/carriers/ecoscooting/parser.js +88 -0
- package/dist/carriers/ecoscooting/status.d.ts +2 -0
- package/dist/carriers/ecoscooting/status.js +50 -0
- package/dist/carriers/ecoscooting/statuses.json +295 -0
- package/dist/carriers/ems/adapter.d.ts +14 -0
- package/dist/carriers/ems/adapter.js +124 -0
- package/dist/carriers/ems/carrier.json +61 -0
- package/dist/carriers/ems/numbers.json +43 -0
- package/dist/carriers/ems/status.d.ts +2 -0
- package/dist/carriers/ems/status.js +24 -0
- package/dist/carriers/ems/statuses.json +128 -0
- package/dist/carriers/estafeta/adapter.d.ts +11 -0
- package/dist/carriers/estafeta/adapter.js +45 -0
- package/dist/carriers/estafeta/carrier.json +79 -0
- package/dist/carriers/estafeta/numbers.json +85 -0
- package/dist/carriers/estafeta/parser.d.ts +11 -0
- package/dist/carriers/estafeta/parser.js +123 -0
- package/dist/carriers/estafeta/status.d.ts +3 -0
- package/dist/carriers/estafeta/status.js +17 -0
- package/dist/carriers/estafeta/statuses.json +47 -0
- package/dist/carriers/evri/adapter.d.ts +14 -0
- package/dist/carriers/evri/adapter.js +146 -0
- package/dist/carriers/evri/carrier.json +69 -0
- package/dist/carriers/evri/numbers.json +32 -0
- package/dist/carriers/evri/status.d.ts +2 -0
- package/dist/carriers/evri/status.js +24 -0
- package/dist/carriers/evri/statuses.json +133 -0
- package/dist/carriers/fedex/adapter.d.ts +32 -0
- package/dist/carriers/fedex/adapter.js +307 -0
- package/dist/carriers/fedex/carrier.json +74 -0
- package/dist/carriers/fedex/numbers.json +198 -0
- package/dist/carriers/fedex/status.d.ts +33 -0
- package/dist/carriers/fedex/status.js +114 -0
- package/dist/carriers/fedex/statuses.json +34 -0
- package/dist/carriers/four-px/adapter.d.ts +12 -0
- package/dist/carriers/four-px/adapter.js +113 -0
- package/dist/carriers/four-px/carrier.json +60 -0
- package/dist/carriers/four-px/numbers.json +32 -0
- package/dist/carriers/four-px/status.d.ts +2 -0
- package/dist/carriers/four-px/status.js +22 -0
- package/dist/carriers/four-px/statuses.json +141 -0
- package/dist/carriers/geodis/adapter.d.ts +37 -0
- package/dist/carriers/geodis/adapter.js +275 -0
- package/dist/carriers/geodis/carrier.json +75 -0
- package/dist/carriers/geodis/numbers.json +45 -0
- package/dist/carriers/geodis/status.d.ts +37 -0
- package/dist/carriers/geodis/status.js +105 -0
- package/dist/carriers/geodis/statuses.json +51 -0
- package/dist/carriers/gls-ch/adapter.d.ts +39 -0
- package/dist/carriers/gls-ch/adapter.js +405 -0
- package/dist/carriers/gls-ch/carrier.json +122 -0
- package/dist/carriers/gls-ch/numbers.json +31 -0
- package/dist/carriers/gls-ch/status.d.ts +19 -0
- package/dist/carriers/gls-ch/status.js +79 -0
- package/dist/carriers/gls-ch/statuses.json +31 -0
- package/dist/carriers/gls-de/adapter.d.ts +28 -0
- package/dist/carriers/gls-de/adapter.js +113 -0
- package/dist/carriers/gls-de/carrier.json +126 -0
- package/dist/carriers/gls-de/numbers.json +111 -0
- package/dist/carriers/gls-de/status.d.ts +10 -0
- package/dist/carriers/gls-de/status.js +10 -0
- package/dist/carriers/gls-de/statuses.json +31 -0
- package/dist/carriers/gls-fr/adapter.d.ts +29 -0
- package/dist/carriers/gls-fr/adapter.js +220 -0
- package/dist/carriers/gls-fr/carrier.json +107 -0
- package/dist/carriers/gls-fr/numbers.json +113 -0
- package/dist/carriers/gls-fr/status.d.ts +28 -0
- package/dist/carriers/gls-fr/status.js +58 -0
- package/dist/carriers/gls-fr/statuses.json +34 -0
- package/dist/carriers/gofo/adapter.d.ts +11 -0
- package/dist/carriers/gofo/adapter.js +41 -0
- package/dist/carriers/gofo/carrier.json +80 -0
- package/dist/carriers/gofo/numbers.json +59 -0
- package/dist/carriers/gofo/parser.d.ts +5 -0
- package/dist/carriers/gofo/parser.js +131 -0
- package/dist/carriers/gofo/status.d.ts +2 -0
- package/dist/carriers/gofo/status.js +7 -0
- package/dist/carriers/gofo/statuses.json +54 -0
- package/dist/carriers/heppner/adapter.d.ts +27 -0
- package/dist/carriers/heppner/adapter.js +247 -0
- package/dist/carriers/heppner/carrier.json +74 -0
- package/dist/carriers/heppner/numbers.json +39 -0
- package/dist/carriers/heppner/status.d.ts +28 -0
- package/dist/carriers/heppner/status.js +43 -0
- package/dist/carriers/heppner/statuses.json +69 -0
- package/dist/carriers/hermes/adapter.d.ts +17 -0
- package/dist/carriers/hermes/adapter.js +103 -0
- package/dist/carriers/hermes/carrier.json +58 -0
- package/dist/carriers/hermes/numbers.json +20 -0
- package/dist/carriers/hermes/status.d.ts +15 -0
- package/dist/carriers/hermes/status.js +61 -0
- package/dist/carriers/hermes/statuses.json +31 -0
- package/dist/carriers/hermes-de/adapter.d.ts +22 -0
- package/dist/carriers/hermes-de/adapter.js +168 -0
- package/dist/carriers/hermes-de/carrier.json +90 -0
- package/dist/carriers/hermes-de/numbers.json +125 -0
- package/dist/carriers/hermes-de/status.d.ts +24 -0
- package/dist/carriers/hermes-de/status.js +52 -0
- package/dist/carriers/hermes-de/statuses.json +277 -0
- package/dist/carriers/hongkong-post/carrier.json +38 -0
- package/dist/carriers/hongkong-post/numbers.json +32 -0
- package/dist/carriers/india-post/adapter.d.ts +33 -0
- package/dist/carriers/india-post/adapter.js +415 -0
- package/dist/carriers/india-post/carrier.json +74 -0
- package/dist/carriers/india-post/numbers.json +58 -0
- package/dist/carriers/india-post/status.d.ts +23 -0
- package/dist/carriers/india-post/status.js +99 -0
- package/dist/carriers/india-post/statuses.json +18 -0
- package/dist/carriers/inpost/adapter.d.ts +14 -0
- package/dist/carriers/inpost/adapter.js +143 -0
- package/dist/carriers/inpost/carrier.json +75 -0
- package/dist/carriers/inpost/numbers.json +130 -0
- package/dist/carriers/inpost/status.d.ts +16 -0
- package/dist/carriers/inpost/status.js +31 -0
- package/dist/carriers/inpost/statuses.json +26 -0
- package/dist/carriers/intl-post/carrier.json +46 -0
- package/dist/carriers/intl-post/numbers.json +29 -0
- package/dist/carriers/j-and-t/carrier.json +66 -0
- package/dist/carriers/j-and-t/numbers.json +90 -0
- package/dist/carriers/japan-post/adapter.d.ts +13 -0
- package/dist/carriers/japan-post/adapter.js +159 -0
- package/dist/carriers/japan-post/carrier.json +71 -0
- package/dist/carriers/japan-post/numbers.json +35 -0
- package/dist/carriers/japan-post/status.d.ts +2 -0
- package/dist/carriers/japan-post/status.js +25 -0
- package/dist/carriers/japan-post/statuses.json +136 -0
- package/dist/carriers/jd-logistics/carrier.json +37 -0
- package/dist/carriers/jd-logistics/numbers.json +19 -0
- package/dist/carriers/korea-post/adapter.d.ts +12 -0
- package/dist/carriers/korea-post/adapter.js +101 -0
- package/dist/carriers/korea-post/carrier.json +74 -0
- package/dist/carriers/korea-post/numbers.json +54 -0
- package/dist/carriers/korea-post/status.d.ts +2 -0
- package/dist/carriers/korea-post/status.js +19 -0
- package/dist/carriers/korea-post/statuses.json +103 -0
- package/dist/carriers/la-poste/adapter.d.ts +29 -0
- package/dist/carriers/la-poste/adapter.js +241 -0
- package/dist/carriers/la-poste/carrier.json +87 -0
- package/dist/carriers/la-poste/numbers.json +140 -0
- package/dist/carriers/la-poste/status.d.ts +30 -0
- package/dist/carriers/la-poste/status.js +110 -0
- package/dist/carriers/la-poste/statuses.json +163 -0
- package/dist/carriers/landmark-global/adapter.d.ts +11 -0
- package/dist/carriers/landmark-global/adapter.js +30 -0
- package/dist/carriers/landmark-global/carrier.json +64 -0
- package/dist/carriers/landmark-global/numbers.json +58 -0
- package/dist/carriers/landmark-global/parser.d.ts +3 -0
- package/dist/carriers/landmark-global/parser.js +78 -0
- package/dist/carriers/landmark-global/status.d.ts +2 -0
- package/dist/carriers/landmark-global/status.js +13 -0
- package/dist/carriers/landmark-global/statuses.json +125 -0
- package/dist/carriers/mondial-relay/adapter.d.ts +32 -0
- package/dist/carriers/mondial-relay/adapter.js +352 -0
- package/dist/carriers/mondial-relay/carrier.json +94 -0
- package/dist/carriers/mondial-relay/numbers.json +130 -0
- package/dist/carriers/mondial-relay/status.d.ts +31 -0
- package/dist/carriers/mondial-relay/status.js +98 -0
- package/dist/carriers/mondial-relay/statuses.json +50 -0
- package/dist/carriers/mrw/adapter.d.ts +13 -0
- package/dist/carriers/mrw/adapter.js +131 -0
- package/dist/carriers/mrw/carrier.json +60 -0
- package/dist/carriers/mrw/numbers.json +116 -0
- package/dist/carriers/mrw/parser.d.ts +12 -0
- package/dist/carriers/mrw/parser.js +149 -0
- package/dist/carriers/mrw/status.d.ts +2 -0
- package/dist/carriers/mrw/status.js +14 -0
- package/dist/carriers/mrw/statuses.json +13 -0
- package/dist/carriers/nacex/adapter.d.ts +11 -0
- package/dist/carriers/nacex/adapter.js +99 -0
- package/dist/carriers/nacex/carrier.json +78 -0
- package/dist/carriers/nacex/numbers.json +47 -0
- package/dist/carriers/nacex/parser.d.ts +4 -0
- package/dist/carriers/nacex/parser.js +119 -0
- package/dist/carriers/nacex/status.d.ts +2 -0
- package/dist/carriers/nacex/status.js +12 -0
- package/dist/carriers/nacex/statuses.json +11 -0
- package/dist/carriers/ninja-van/adapter.d.ts +11 -0
- package/dist/carriers/ninja-van/adapter.js +45 -0
- package/dist/carriers/ninja-van/carrier.json +79 -0
- package/dist/carriers/ninja-van/numbers.json +67 -0
- package/dist/carriers/ninja-van/parser.d.ts +5 -0
- package/dist/carriers/ninja-van/parser.js +95 -0
- package/dist/carriers/ninja-van/status.d.ts +3 -0
- package/dist/carriers/ninja-van/status.js +25 -0
- package/dist/carriers/ninja-van/statuses.json +11 -0
- package/dist/carriers/nz-post/adapter.d.ts +11 -0
- package/dist/carriers/nz-post/adapter.js +45 -0
- package/dist/carriers/nz-post/carrier.json +82 -0
- package/dist/carriers/nz-post/numbers.json +49 -0
- package/dist/carriers/nz-post/parser.d.ts +3 -0
- package/dist/carriers/nz-post/parser.js +97 -0
- package/dist/carriers/nz-post/status.d.ts +2 -0
- package/dist/carriers/nz-post/status.js +29 -0
- package/dist/carriers/nz-post/statuses.json +68 -0
- package/dist/carriers/old-dominion/carrier.json +51 -0
- package/dist/carriers/old-dominion/numbers.json +83 -0
- package/dist/carriers/ontrac/adapter.d.ts +11 -0
- package/dist/carriers/ontrac/adapter.js +34 -0
- package/dist/carriers/ontrac/carrier.json +81 -0
- package/dist/carriers/ontrac/numbers.json +214 -0
- package/dist/carriers/ontrac/parser.d.ts +3 -0
- package/dist/carriers/ontrac/parser.js +72 -0
- package/dist/carriers/ontrac/status.d.ts +2 -0
- package/dist/carriers/ontrac/status.js +19 -0
- package/dist/carriers/ontrac/statuses.json +61 -0
- package/dist/carriers/paack/adapter.d.ts +19 -0
- package/dist/carriers/paack/adapter.js +255 -0
- package/dist/carriers/paack/carrier.json +79 -0
- package/dist/carriers/paack/numbers.json +106 -0
- package/dist/carriers/paack/status.d.ts +26 -0
- package/dist/carriers/paack/status.js +82 -0
- package/dist/carriers/paack/statuses.json +357 -0
- package/dist/carriers/packeta/adapter.d.ts +15 -0
- package/dist/carriers/packeta/adapter.js +166 -0
- package/dist/carriers/packeta/carrier.json +90 -0
- package/dist/carriers/packeta/numbers.json +71 -0
- package/dist/carriers/packeta/status.d.ts +23 -0
- package/dist/carriers/packeta/status.js +40 -0
- package/dist/carriers/packeta/statuses.json +21 -0
- package/dist/carriers/parcelforce/carrier.json +47 -0
- package/dist/carriers/parcelforce/numbers.json +32 -0
- package/dist/carriers/planzer/adapter.d.ts +40 -0
- package/dist/carriers/planzer/adapter.js +148 -0
- package/dist/carriers/planzer/carrier.json +85 -0
- package/dist/carriers/planzer/numbers.json +118 -0
- package/dist/carriers/planzer/shared.d.ts +19 -0
- package/dist/carriers/planzer/shared.js +135 -0
- package/dist/carriers/planzer/status.d.ts +33 -0
- package/dist/carriers/planzer/status.js +89 -0
- package/dist/carriers/planzer/statuses.json +229 -0
- package/dist/carriers/poczta-polska/adapter.d.ts +15 -0
- package/dist/carriers/poczta-polska/adapter.js +65 -0
- package/dist/carriers/poczta-polska/carrier.json +79 -0
- package/dist/carriers/poczta-polska/numbers.json +130 -0
- package/dist/carriers/poczta-polska/parser.d.ts +3 -0
- package/dist/carriers/poczta-polska/parser.js +96 -0
- package/dist/carriers/poczta-polska/status.d.ts +2 -0
- package/dist/carriers/poczta-polska/status.js +21 -0
- package/dist/carriers/poczta-polska/statuses.json +47 -0
- package/dist/carriers/pos-malaysia/adapter.d.ts +18 -0
- package/dist/carriers/pos-malaysia/adapter.js +199 -0
- package/dist/carriers/pos-malaysia/carrier.json +78 -0
- package/dist/carriers/pos-malaysia/numbers.json +73 -0
- package/dist/carriers/pos-malaysia/status.d.ts +17 -0
- package/dist/carriers/pos-malaysia/status.js +21 -0
- package/dist/carriers/pos-malaysia/statuses.json +17 -0
- package/dist/carriers/poste-italiane/adapter.d.ts +15 -0
- package/dist/carriers/poste-italiane/adapter.js +211 -0
- package/dist/carriers/poste-italiane/carrier.json +86 -0
- package/dist/carriers/poste-italiane/numbers.json +117 -0
- package/dist/carriers/poste-italiane/status.d.ts +19 -0
- package/dist/carriers/poste-italiane/status.js +44 -0
- package/dist/carriers/poste-italiane/statuses.json +22 -0
- package/dist/carriers/posti/adapter.d.ts +17 -0
- package/dist/carriers/posti/adapter.js +195 -0
- package/dist/carriers/posti/carrier.json +69 -0
- package/dist/carriers/posti/numbers.json +52 -0
- package/dist/carriers/posti/status.d.ts +4 -0
- package/dist/carriers/posti/status.js +41 -0
- package/dist/carriers/posti/statuses.json +18 -0
- package/dist/carriers/postlogistics/adapter.d.ts +39 -0
- package/dist/carriers/postlogistics/adapter.js +162 -0
- package/dist/carriers/postlogistics/carrier.json +67 -0
- package/dist/carriers/postlogistics/number.d.ts +2 -0
- package/dist/carriers/postlogistics/number.js +7 -0
- package/dist/carriers/postlogistics/numbers.json +54 -0
- package/dist/carriers/postlogistics/status.d.ts +20 -0
- package/dist/carriers/postlogistics/status.js +12 -0
- package/dist/carriers/postlogistics/statuses.json +40 -0
- package/dist/carriers/postnord/adapter.d.ts +11 -0
- package/dist/carriers/postnord/adapter.js +76 -0
- package/dist/carriers/postnord/carrier.json +82 -0
- package/dist/carriers/postnord/numbers.json +66 -0
- package/dist/carriers/postnord/parser.d.ts +3 -0
- package/dist/carriers/postnord/parser.js +75 -0
- package/dist/carriers/postnord/status.d.ts +3 -0
- package/dist/carriers/postnord/status.js +38 -0
- package/dist/carriers/postnord/statuses.json +16 -0
- package/dist/carriers/purolator/adapter.d.ts +11 -0
- package/dist/carriers/purolator/adapter.js +45 -0
- package/dist/carriers/purolator/carrier.json +75 -0
- package/dist/carriers/purolator/numbers.json +146 -0
- package/dist/carriers/purolator/parser.d.ts +3 -0
- package/dist/carriers/purolator/parser.js +79 -0
- package/dist/carriers/purolator/status.d.ts +2 -0
- package/dist/carriers/purolator/status.js +17 -0
- package/dist/carriers/purolator/statuses.json +15 -0
- package/dist/carriers/quickpac/carrier.json +68 -0
- package/dist/carriers/quickpac/numbers.json +31 -0
- package/dist/carriers/quickpac/statuses.json +213 -0
- package/dist/carriers/relais-colis/adapter.d.ts +28 -0
- package/dist/carriers/relais-colis/adapter.js +242 -0
- package/dist/carriers/relais-colis/carrier.json +77 -0
- package/dist/carriers/relais-colis/numbers.json +64 -0
- package/dist/carriers/relais-colis/status.d.ts +26 -0
- package/dist/carriers/relais-colis/status.js +91 -0
- package/dist/carriers/relais-colis/statuses.json +341 -0
- package/dist/carriers/royal-mail/adapter.d.ts +32 -0
- package/dist/carriers/royal-mail/adapter.js +260 -0
- package/dist/carriers/royal-mail/carrier.json +60 -0
- package/dist/carriers/royal-mail/numbers.json +57 -0
- package/dist/carriers/royal-mail/status.d.ts +19 -0
- package/dist/carriers/royal-mail/status.js +80 -0
- package/dist/carriers/royal-mail/statuses.json +16 -0
- package/dist/carriers/seur/adapter.d.ts +11 -0
- package/dist/carriers/seur/adapter.js +35 -0
- package/dist/carriers/seur/carrier.json +89 -0
- package/dist/carriers/seur/numbers.json +80 -0
- package/dist/carriers/seur/parser.d.ts +3 -0
- package/dist/carriers/seur/parser.js +78 -0
- package/dist/carriers/seur/status.d.ts +2 -0
- package/dist/carriers/seur/status.js +14 -0
- package/dist/carriers/seur/statuses.json +47 -0
- package/dist/carriers/sf-express/adapter.d.ts +17 -0
- package/dist/carriers/sf-express/adapter.js +87 -0
- package/dist/carriers/sf-express/carrier.json +78 -0
- package/dist/carriers/sf-express/numbers.json +66 -0
- package/dist/carriers/sf-express/parser.d.ts +4 -0
- package/dist/carriers/sf-express/parser.js +102 -0
- package/dist/carriers/sf-express/status.d.ts +4 -0
- package/dist/carriers/sf-express/status.js +28 -0
- package/dist/carriers/sf-express/statuses.json +334 -0
- package/dist/carriers/shipup/carrier.json +28 -0
- package/dist/carriers/shipup/numbers.json +20 -0
- package/dist/carriers/singapore-post/adapter.d.ts +12 -0
- package/dist/carriers/singapore-post/adapter.js +106 -0
- package/dist/carriers/singapore-post/carrier.json +62 -0
- package/dist/carriers/singapore-post/numbers.json +77 -0
- package/dist/carriers/singapore-post/status.d.ts +2 -0
- package/dist/carriers/singapore-post/status.js +17 -0
- package/dist/carriers/singapore-post/statuses.json +101 -0
- package/dist/carriers/spee-dee/carrier.json +51 -0
- package/dist/carriers/spee-dee/numbers.json +55 -0
- package/dist/carriers/speedx/carrier.json +49 -0
- package/dist/carriers/speedx/numbers.json +112 -0
- package/dist/carriers/spring-gds/adapter.d.ts +35 -0
- package/dist/carriers/spring-gds/adapter.js +212 -0
- package/dist/carriers/spring-gds/carrier.json +90 -0
- package/dist/carriers/spring-gds/numbers.json +224 -0
- package/dist/carriers/spring-gds/status.d.ts +19 -0
- package/dist/carriers/spring-gds/status.js +28 -0
- package/dist/carriers/spring-gds/statuses.json +140 -0
- package/dist/carriers/sto/carrier.json +37 -0
- package/dist/carriers/sto/numbers.json +33 -0
- package/dist/carriers/sunyou/adapter.d.ts +18 -0
- package/dist/carriers/sunyou/adapter.js +156 -0
- package/dist/carriers/sunyou/carrier.json +64 -0
- package/dist/carriers/sunyou/numbers.json +44 -0
- package/dist/carriers/sunyou/status.d.ts +17 -0
- package/dist/carriers/sunyou/status.js +13 -0
- package/dist/carriers/sunyou/statuses.json +53 -0
- package/dist/carriers/swiss-post/adapter.d.ts +18 -0
- package/dist/carriers/swiss-post/adapter.js +282 -0
- package/dist/carriers/swiss-post/carrier.json +84 -0
- package/dist/carriers/swiss-post/numbers.json +55 -0
- package/dist/carriers/swiss-post/status.d.ts +27 -0
- package/dist/carriers/swiss-post/status.js +64 -0
- package/dist/carriers/swiss-post/statuses.json +33 -0
- package/dist/carriers/swiss-post-cargo/adapter.d.ts +19 -0
- package/dist/carriers/swiss-post-cargo/adapter.js +173 -0
- package/dist/carriers/swiss-post-cargo/carrier.json +60 -0
- package/dist/carriers/swiss-post-cargo/numbers.json +23 -0
- package/dist/carriers/swiss-post-cargo/reference.d.ts +13 -0
- package/dist/carriers/swiss-post-cargo/reference.js +48 -0
- package/dist/carriers/swiss-post-cargo/status.d.ts +13 -0
- package/dist/carriers/swiss-post-cargo/status.js +53 -0
- package/dist/carriers/swiss-post-cargo/statuses.json +19 -0
- package/dist/carriers/thailand-post/carrier.json +38 -0
- package/dist/carriers/thailand-post/numbers.json +33 -0
- package/dist/carriers/the-courier-guy/adapter.d.ts +11 -0
- package/dist/carriers/the-courier-guy/adapter.js +49 -0
- package/dist/carriers/the-courier-guy/carrier.json +73 -0
- package/dist/carriers/the-courier-guy/number.d.ts +3 -0
- package/dist/carriers/the-courier-guy/number.js +18 -0
- package/dist/carriers/the-courier-guy/numbers.json +74 -0
- package/dist/carriers/the-courier-guy/parser.d.ts +3 -0
- package/dist/carriers/the-courier-guy/parser.js +104 -0
- package/dist/carriers/the-courier-guy/status.d.ts +5 -0
- package/dist/carriers/the-courier-guy/status.js +21 -0
- package/dist/carriers/the-courier-guy/statuses.json +61 -0
- package/dist/carriers/tipsa/adapter.d.ts +12 -0
- package/dist/carriers/tipsa/adapter.js +52 -0
- package/dist/carriers/tipsa/carrier.json +92 -0
- package/dist/carriers/tipsa/numbers.json +100 -0
- package/dist/carriers/tipsa/parser.d.ts +17 -0
- package/dist/carriers/tipsa/parser.js +114 -0
- package/dist/carriers/tipsa/status.d.ts +3 -0
- package/dist/carriers/tipsa/status.js +25 -0
- package/dist/carriers/tipsa/statuses.json +12 -0
- package/dist/carriers/tnt/adapter.d.ts +23 -0
- package/dist/carriers/tnt/adapter.js +210 -0
- package/dist/carriers/tnt/carrier.json +66 -0
- package/dist/carriers/tnt/numbers.json +116 -0
- package/dist/carriers/tnt/status.d.ts +3 -0
- package/dist/carriers/tnt/status.js +35 -0
- package/dist/carriers/tnt/statuses.json +126 -0
- package/dist/carriers/ukrposhta/adapter.d.ts +13 -0
- package/dist/carriers/ukrposhta/adapter.js +152 -0
- package/dist/carriers/ukrposhta/carrier.json +62 -0
- package/dist/carriers/ukrposhta/numbers.json +53 -0
- package/dist/carriers/ukrposhta/parser.d.ts +15 -0
- package/dist/carriers/ukrposhta/parser.js +130 -0
- package/dist/carriers/ukrposhta/status.d.ts +3 -0
- package/dist/carriers/ukrposhta/status.js +34 -0
- package/dist/carriers/ukrposhta/statuses.json +208 -0
- package/dist/carriers/uniuni/adapter.d.ts +11 -0
- package/dist/carriers/uniuni/adapter.js +48 -0
- package/dist/carriers/uniuni/carrier.json +90 -0
- package/dist/carriers/uniuni/numbers.json +72 -0
- package/dist/carriers/uniuni/parser.d.ts +5 -0
- package/dist/carriers/uniuni/parser.js +114 -0
- package/dist/carriers/uniuni/status.d.ts +2 -0
- package/dist/carriers/uniuni/status.js +16 -0
- package/dist/carriers/uniuni/statuses.json +54 -0
- package/dist/carriers/unknown/carrier.json +57 -0
- package/dist/carriers/unknown/numbers.json +257 -0
- package/dist/carriers/ups/adapter.d.ts +40 -0
- package/dist/carriers/ups/adapter.js +473 -0
- package/dist/carriers/ups/carrier.json +69 -0
- package/dist/carriers/ups/numbers.json +134 -0
- package/dist/carriers/ups/status.d.ts +25 -0
- package/dist/carriers/ups/status.js +41 -0
- package/dist/carriers/ups/statuses.json +35 -0
- package/dist/carriers/usps/adapter.d.ts +25 -0
- package/dist/carriers/usps/adapter.js +261 -0
- package/dist/carriers/usps/carrier.json +74 -0
- package/dist/carriers/usps/numbers.json +286 -0
- package/dist/carriers/usps/status.d.ts +25 -0
- package/dist/carriers/usps/status.js +72 -0
- package/dist/carriers/usps/statuses.json +24 -0
- package/dist/carriers/yamato/adapter.d.ts +13 -0
- package/dist/carriers/yamato/adapter.js +130 -0
- package/dist/carriers/yamato/carrier.json +72 -0
- package/dist/carriers/yamato/numbers.json +57 -0
- package/dist/carriers/yamato/status.d.ts +2 -0
- package/dist/carriers/yamato/status.js +27 -0
- package/dist/carriers/yamato/statuses.json +159 -0
- package/dist/carriers/yanwen/adapter.d.ts +13 -0
- package/dist/carriers/yanwen/adapter.js +130 -0
- package/dist/carriers/yanwen/carrier.json +66 -0
- package/dist/carriers/yanwen/numbers.json +45 -0
- package/dist/carriers/yanwen/status.d.ts +2 -0
- package/dist/carriers/yanwen/status.js +25 -0
- package/dist/carriers/yanwen/statuses.json +131 -0
- package/dist/carriers/yto/adapter.d.ts +11 -0
- package/dist/carriers/yto/adapter.js +36 -0
- package/dist/carriers/yto/carrier.json +75 -0
- package/dist/carriers/yto/numbers.json +33 -0
- package/dist/carriers/yto/parser.d.ts +3 -0
- package/dist/carriers/yto/parser.js +92 -0
- package/dist/carriers/yto/status.d.ts +10 -0
- package/dist/carriers/yto/status.js +46 -0
- package/dist/carriers/yto/statuses.json +206 -0
- package/dist/carriers/yunda/adapter.d.ts +11 -0
- package/dist/carriers/yunda/adapter.js +68 -0
- package/dist/carriers/yunda/carrier.json +69 -0
- package/dist/carriers/yunda/challenge.d.ts +5 -0
- package/dist/carriers/yunda/challenge.js +68 -0
- package/dist/carriers/yunda/numbers.json +43 -0
- package/dist/carriers/yunda/parser.d.ts +4 -0
- package/dist/carriers/yunda/parser.js +105 -0
- package/dist/carriers/yunda/status.d.ts +4 -0
- package/dist/carriers/yunda/status.js +9 -0
- package/dist/carriers/yunda/statuses.json +40 -0
- package/dist/carriers/yunexpress/adapter.d.ts +20 -0
- package/dist/carriers/yunexpress/adapter.js +240 -0
- package/dist/carriers/yunexpress/carrier.json +69 -0
- package/dist/carriers/yunexpress/numbers.json +32 -0
- package/dist/carriers/yunexpress/status.d.ts +2 -0
- package/dist/carriers/yunexpress/status.js +23 -0
- package/dist/carriers/yunexpress/statuses.json +112 -0
- package/dist/carriers/zto/carrier.json +37 -0
- package/dist/carriers/zto/numbers.json +30 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +82 -0
- package/dist/core/adapter/index.d.ts +90 -0
- package/dist/core/adapter/index.js +103 -0
- package/dist/core/adapter/track.d.ts +10 -0
- package/dist/core/adapter/track.js +20 -0
- package/dist/core/catalog/amazon.d.ts +5 -0
- package/dist/core/catalog/amazon.js +33 -0
- package/dist/core/catalog/definitions.d.ts +24 -0
- package/dist/core/catalog/definitions.js +44 -0
- package/dist/core/catalog/handoff.d.ts +9 -0
- package/dist/core/catalog/handoff.js +56 -0
- package/dist/core/catalog/hints.d.ts +45 -0
- package/dist/core/catalog/hints.js +162 -0
- package/dist/core/catalog/index.d.ts +38 -0
- package/dist/core/catalog/index.js +223 -0
- package/dist/core/catalog/inputs.d.ts +6 -0
- package/dist/core/catalog/inputs.js +89 -0
- package/dist/core/catalog/linkRules.d.ts +24 -0
- package/dist/core/catalog/linkRules.js +20 -0
- package/dist/core/catalog/networks.d.ts +9 -0
- package/dist/core/catalog/networks.js +13 -0
- package/dist/core/catalog/recognition.d.ts +22 -0
- package/dist/core/catalog/recognition.js +52 -0
- package/dist/core/catalog/types.d.ts +135 -0
- package/dist/core/catalog/types.js +1 -0
- package/dist/core/catalog/urls.d.ts +2 -0
- package/dist/core/catalog/urls.js +125 -0
- package/dist/core/detection/amazon.d.ts +2 -0
- package/dist/core/detection/amazon.js +14 -0
- package/dist/core/detection/candidates.d.ts +5 -0
- package/dist/core/detection/candidates.js +41 -0
- package/dist/core/detection/detect.d.ts +17 -0
- package/dist/core/detection/detect.js +52 -0
- package/dist/core/detection/gls.d.ts +16 -0
- package/dist/core/detection/gls.js +21 -0
- package/dist/core/detection/hermes.d.ts +14 -0
- package/dist/core/detection/hermes.js +19 -0
- package/dist/core/detection/index.d.ts +19 -0
- package/dist/core/detection/index.js +9 -0
- package/dist/core/detection/mondialRelay.d.ts +13 -0
- package/dist/core/detection/mondialRelay.js +25 -0
- package/dist/core/detection/normalize.d.ts +15 -0
- package/dist/core/detection/normalize.js +39 -0
- package/dist/core/detection/parse.d.ts +6 -0
- package/dist/core/detection/parse.js +112 -0
- package/dist/core/detection/s10.d.ts +4 -0
- package/dist/core/detection/s10.js +25 -0
- package/dist/core/detection/types.d.ts +22 -0
- package/dist/core/detection/types.js +1 -0
- package/dist/core/errors/hint.d.ts +7 -0
- package/dist/core/errors/hint.js +7 -0
- package/dist/core/errors/index.d.ts +98 -0
- package/dist/core/errors/index.js +178 -0
- package/dist/core/recognition/index.d.ts +24 -0
- package/dist/core/recognition/index.js +65 -0
- package/dist/core/result/index.d.ts +57 -0
- package/dist/core/result/index.js +114 -0
- package/dist/core/result/resolve.d.ts +18 -0
- package/dist/core/result/resolve.js +49 -0
- package/dist/core/runner/index.d.ts +33 -0
- package/dist/core/runner/index.js +109 -0
- package/dist/core/status/index.d.ts +22 -0
- package/dist/core/status/index.js +3 -0
- package/dist/core/status/language.d.ts +11 -0
- package/dist/core/status/language.js +149 -0
- package/dist/core/status/wording.d.ts +25 -0
- package/dist/core/status/wording.js +91 -0
- package/dist/core/telemetry/index.d.ts +62 -0
- package/dist/core/telemetry/index.js +55 -0
- package/dist/core/time/index.d.ts +84 -0
- package/dist/core/time/index.js +243 -0
- package/dist/core/time/result.d.ts +5 -0
- package/dist/core/time/result.js +55 -0
- package/dist/core/transport/boundedFetch.d.ts +26 -0
- package/dist/core/transport/boundedFetch.js +152 -0
- package/dist/core/transport/browser.d.ts +14 -0
- package/dist/core/transport/browser.js +113 -0
- package/dist/core/transport/index.d.ts +8 -0
- package/dist/core/transport/index.js +5 -0
- package/dist/core/transport/optional.d.ts +2 -0
- package/dist/core/transport/optional.js +17 -0
- package/dist/core/transport/text.d.ts +8 -0
- package/dist/core/transport/text.js +18 -0
- package/dist/core/transport/trawl.d.ts +80 -0
- package/dist/core/transport/trawl.js +212 -0
- package/dist/core/transport/upstreamHttpDiagnostics.d.ts +16 -0
- package/dist/core/transport/upstreamHttpDiagnostics.js +97 -0
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.js +11 -0
- package/dist/data/catalog.json +3969 -0
- package/dist/facade/index.d.ts +57 -0
- package/dist/facade/index.js +195 -0
- package/dist/generated/catalog.d.ts +2671 -0
- package/dist/generated/catalog.js +4090 -0
- package/dist/generated/recognition.d.ts +2 -0
- package/dist/generated/recognition.js +31 -0
- package/dist/generated/regionTowns.d.ts +2 -0
- package/dist/generated/regionTowns.js +11 -0
- package/dist/generated/registry.d.ts +4 -0
- package/dist/generated/registry.js +286 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +18 -0
- package/dist/node.d.ts +11 -0
- package/dist/node.js +11 -0
- package/dist/places/countries.json +1670 -0
- package/dist/places/facilities.json +11 -0
- package/dist/places/index.d.ts +42 -0
- package/dist/places/index.js +307 -0
- package/dist/places/names.d.mts +15 -0
- package/dist/places/names.mjs +36 -0
- package/dist/places/places.tsv.br +0 -0
- package/dist/places/trackingLocation.d.ts +18 -0
- package/dist/places/trackingLocation.js +89 -0
- package/dist/providers/coverage.d.ts +31 -0
- package/dist/providers/coverage.js +70 -0
- package/dist/providers/coverage.json +1744 -0
- package/dist/providers/parcelsapp/adapter.d.ts +23 -0
- package/dist/providers/parcelsapp/adapter.js +391 -0
- package/dist/providers/parcelsapp/http.d.ts +9 -0
- package/dist/providers/parcelsapp/http.js +67 -0
- package/dist/providers/plan.d.ts +38 -0
- package/dist/providers/plan.js +54 -0
- package/dist/providers/postal-ninja/adapter.d.ts +17 -0
- package/dist/providers/postal-ninja/adapter.js +200 -0
- package/dist/providers/seventeentrack/adapter.d.ts +50 -0
- package/dist/providers/seventeentrack/adapter.js +165 -0
- package/dist/providers/seventeentrack/events.d.ts +4 -0
- package/dist/providers/seventeentrack/events.js +56 -0
- package/dist/providers/shared/capture.d.ts +46 -0
- package/dist/providers/shared/capture.js +92 -0
- package/dist/providers/shared/hints.d.ts +16 -0
- package/dist/providers/shared/hints.js +37 -0
- package/dist/providers/shared/result.d.ts +15 -0
- package/dist/providers/shared/result.js +161 -0
- package/dist/providers/shared/scans.d.ts +22 -0
- package/dist/providers/shared/scans.js +29 -0
- package/dist/providers/ship24/adapter.d.ts +17 -0
- package/dist/providers/ship24/adapter.js +140 -0
- package/dist/providers/ship24/http.d.ts +7 -0
- package/dist/providers/ship24/http.js +75 -0
- package/dist/providers/types.d.ts +1 -0
- package/dist/providers/types.js +1 -0
- package/dist/providers/universal.d.ts +66 -0
- package/dist/providers/universal.js +95 -0
- package/dist/providers/upu/adapter.d.ts +5 -0
- package/dist/providers/upu/adapter.js +107 -0
- package/dist/scripts/canary-report.d.mts +22 -0
- package/dist/scripts/canary-report.mjs +69 -0
- package/dist/scripts/carrier-canary.d.ts +50 -0
- package/dist/scripts/carrier-canary.js +220 -0
- package/dist/server/index.d.ts +20 -0
- package/dist/server/index.js +226 -0
- package/dist/server/openapi.json +487 -0
- package/dist/server/page.d.ts +1 -0
- package/dist/server/page.js +33 -0
- package/package.json +102 -4
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { load } from 'cheerio';
|
|
4
|
+
import { ChallengeError, IndeterminateError, NotFoundError, SchemaError, carrierErrorKind, } from '../../core/errors/index.js';
|
|
5
|
+
import { runSteps } from '../../core/runner/index.js';
|
|
6
|
+
import { isoTime, explicitOffsetTime, zonedTime } from '../../core/time/index.js';
|
|
7
|
+
import { TrawlClient, decodeText, fetchBounded, parseJsonBytes } from '../../core/transport/index.js';
|
|
8
|
+
import { isRecord } from '../../core/types.js';
|
|
9
|
+
import { API_LABELS, PROOF_OF_DELIVERY_SCAN, apiStage, apiStatus, scanStage, wordingStatus, } from './status.js';
|
|
10
|
+
// Protocol provenance:
|
|
11
|
+
// - The myDPD Android application talks to a guest JSON API: a Firebase
|
|
12
|
+
// installation identifies the app, Remote Config hands out the guest Basic
|
|
13
|
+
// credential, that credential buys a client-credentials access token, and the
|
|
14
|
+
// token reads `/v10/parcels/details/<number>`. Every value below is shipped
|
|
15
|
+
// publicly in the application; none of it is an account secret.
|
|
16
|
+
// - The delivery postcode is optional. With it, DPD unlocks verified scans and
|
|
17
|
+
// the delivery window; without it the lookup continues with
|
|
18
|
+
// `continueWithoutVerification=true`. A rejected postcode (HTTP 400) is
|
|
19
|
+
// retried once without verification and reported as unverified rather than
|
|
20
|
+
// failing the lookup.
|
|
21
|
+
// - The consignee web page is the fallback when the guest API is inconclusive.
|
|
22
|
+
// It sits behind Cloudflare, so it is fetched through the browser service's
|
|
23
|
+
// legacy command API when one is configured and directly otherwise.
|
|
24
|
+
const TRACKING_BASE = 'https://www.dpdgroup.com/ch/mydpd/my-parcels/incoming';
|
|
25
|
+
const FETCH_BASE = 'https://www.dpdgroup.com/ch/mydpd/my-parcels/track';
|
|
26
|
+
const API_BASE = 'https://www.dpdgroup.com/concept/webservice';
|
|
27
|
+
const OAUTH_URL = `${API_BASE}/oauth/token?grant_type=client_credentials`;
|
|
28
|
+
const DETAILS_BASE = `${API_BASE}/v10/parcels/details`;
|
|
29
|
+
const FIREBASE_PROJECT = 'consignee-portal';
|
|
30
|
+
const FIREBASE_PROJECT_NUMBER = '959401347543';
|
|
31
|
+
const FIREBASE_APP_ID = '1:959401347543:android:8d1a84133332291109e392';
|
|
32
|
+
// Public, app-restricted identifier shipped in the myDPD Android application.
|
|
33
|
+
const FIREBASE_API_KEY = 'AIzaSyDHMkUNUyUwFrQzKJhdC_J-L7QEwNUzwrc'; // gitleaks:allow
|
|
34
|
+
const ANDROID_PACKAGE = 'com.dpdgroup.chatbot.lemny.prod';
|
|
35
|
+
const ANDROID_CERT = '3872ACD98DE975F69C68CAF5119A5A1B2024B873';
|
|
36
|
+
const CLIENT_VERSION = '3.79.14';
|
|
37
|
+
const INSTALLATIONS_URL = `https://firebaseinstallations.googleapis.com/v1/projects/${FIREBASE_PROJECT}/installations`;
|
|
38
|
+
const REMOTE_CONFIG_URL = `https://firebaseremoteconfig.googleapis.com/v1/projects/${FIREBASE_PROJECT_NUMBER}/namespaces/firebase:fetch`;
|
|
39
|
+
const TIMEZONE = 'Europe/Zurich';
|
|
40
|
+
const DEFAULT_TIMEOUT_MS = 90_000;
|
|
41
|
+
/** How long a failed guest login answers new lookups before the next attempt. */
|
|
42
|
+
const TOKEN_FAILURE_MEMORY_MS = 30_000;
|
|
43
|
+
/** The browser service may spend the whole request timeout plus its own transport allowance. */
|
|
44
|
+
const SOLVER_ALLOWANCE_MS = 15_000;
|
|
45
|
+
const MAX_BYTES = 10_000_000;
|
|
46
|
+
/** Cloudflare interrupted the consignee page with an interactive challenge. */
|
|
47
|
+
export class DPDChallengeError extends ChallengeError {
|
|
48
|
+
constructor(message = 'DPD returned a Cloudflare browser challenge', options) {
|
|
49
|
+
super('DPD', message, options);
|
|
50
|
+
this.name = 'DPDChallengeError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The guest API answered, but the answer proves nothing about the shipment
|
|
55
|
+
* (unreachable, malformed, unauthenticated, or an HTTP status the guest flow
|
|
56
|
+
* handles itself). The rendered page is allowed to recover from it.
|
|
57
|
+
*/
|
|
58
|
+
export class DPDAPIError extends IndeterminateError {
|
|
59
|
+
constructor(message, options) {
|
|
60
|
+
super('DPD guest API', message, options);
|
|
61
|
+
this.name = 'DPDAPIError';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** DPD positively reports that it does not know the parcel number. */
|
|
65
|
+
export class DPDTrackingError extends NotFoundError {
|
|
66
|
+
constructor(options) {
|
|
67
|
+
super('DPD', 'DPD could not locate the shipment', options);
|
|
68
|
+
this.name = 'DPDTrackingError';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
class DPDAPIHttpError extends DPDAPIError {
|
|
72
|
+
constructor(status) {
|
|
73
|
+
super(`DPD guest API returned HTTP ${status}`, { status });
|
|
74
|
+
this.name = 'DPDAPIHttpError';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** The parcel-details call itself refused the lookup (HTTP 400), not a token step. */
|
|
78
|
+
class DPDDetailsRefusedError extends DPDAPIHttpError {
|
|
79
|
+
constructor() {
|
|
80
|
+
super(400);
|
|
81
|
+
this.name = 'DPDDetailsRefusedError';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The rendered page recovers exactly what the guest API path used to hand it:
|
|
86
|
+
* an inconclusive answer, a transport failure, or a payload that did not match
|
|
87
|
+
* the request. A positive "unknown parcel" ends the lookup instead.
|
|
88
|
+
*/
|
|
89
|
+
const PAGE_RECOVERS = new Set(['indeterminate', 'transport', 'schema']);
|
|
90
|
+
/**
|
|
91
|
+
* Local to this adapter: the guest API mixes strings and numbers in the fields
|
|
92
|
+
* we project, and its labels are not length-capped here (they are capped at the
|
|
93
|
+
* projection site instead), so `core/transport`'s string-only `clean` would
|
|
94
|
+
* change what a numeric city or code normalizes to. Anything else (objects,
|
|
95
|
+
* arrays, booleans) is empty: `String()` of an object is "[object Object]".
|
|
96
|
+
*/
|
|
97
|
+
function clean(value) {
|
|
98
|
+
const text = textual(value);
|
|
99
|
+
return text === undefined ? '' : String(text).trim().split(/\s+/).filter(Boolean).join(' ');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The value when `clean` can read it, otherwise undefined, so a `??` chain
|
|
103
|
+
* passes over an object as it does over null. An empty string still stops the
|
|
104
|
+
* chain: stored events are keyed on what that chain produced.
|
|
105
|
+
*/
|
|
106
|
+
function textual(value) {
|
|
107
|
+
return typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value))
|
|
108
|
+
? value
|
|
109
|
+
: undefined;
|
|
110
|
+
}
|
|
111
|
+
/** What DPD writes where it has no value, e.g. `country: "UNDEFINED"` on customs scans. */
|
|
112
|
+
const PLACEHOLDERS = new Set(['UNDEFINED', 'UNKNOWN', 'NULL', 'NONE', 'N/A', '-']);
|
|
113
|
+
/** Provider text with DPD's placeholders read as empty. */
|
|
114
|
+
function known(value) {
|
|
115
|
+
const text = clean(value);
|
|
116
|
+
return PLACEHOLDERS.has(text.toUpperCase()) ? '' : text;
|
|
117
|
+
}
|
|
118
|
+
/** The first candidate that carries real text. */
|
|
119
|
+
function firstText(...candidates) {
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
const text = known(candidate);
|
|
122
|
+
if (text)
|
|
123
|
+
return text;
|
|
124
|
+
}
|
|
125
|
+
return '';
|
|
126
|
+
}
|
|
127
|
+
function optionalText(value) {
|
|
128
|
+
return clean(value) || null;
|
|
129
|
+
}
|
|
130
|
+
/** A guest API code (`DEY`, `PARCEL_HANDED`) fit to travel as `provider_code`, or ''. */
|
|
131
|
+
function scanCode(value) {
|
|
132
|
+
const code = known(value).toUpperCase().replaceAll(' ', '_');
|
|
133
|
+
return /^[A-Z0-9_]{1,40}$/.test(code) ? code : '';
|
|
134
|
+
}
|
|
135
|
+
export function dpdTrackingUrl(trackingNumber, language) {
|
|
136
|
+
const url = new URL(TRACKING_BASE);
|
|
137
|
+
url.searchParams.set('parcelNumber', trackingNumber);
|
|
138
|
+
if (language)
|
|
139
|
+
url.searchParams.set('lang', language);
|
|
140
|
+
return url.toString();
|
|
141
|
+
}
|
|
142
|
+
/** Page timestamps are Swiss wall-clock without an offset; unparsable values stay raw. */
|
|
143
|
+
function eventTime(date, clock) {
|
|
144
|
+
const value = `${date} ${clock}`.trim();
|
|
145
|
+
const formats = clock
|
|
146
|
+
? ['dd.MM.yyyy HH:mm:ss', 'dd.MM.yyyy HH:mm', 'dd.MM.yyyy']
|
|
147
|
+
: ['dd.MM.yyyy'];
|
|
148
|
+
for (const format of formats) {
|
|
149
|
+
const parsed = zonedTime(value, format, TIMEZONE);
|
|
150
|
+
if (parsed)
|
|
151
|
+
return parsed.iso;
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
function apiDescription(value) {
|
|
156
|
+
const key = clean(value).toUpperCase().replaceAll(' ', '_');
|
|
157
|
+
return API_LABELS[key] ?? key.toLocaleLowerCase('en-US')
|
|
158
|
+
.replaceAll('_', ' ')
|
|
159
|
+
.replace(/\b\w/g, (character) => character.toUpperCase());
|
|
160
|
+
}
|
|
161
|
+
function apiSender(payload, current) {
|
|
162
|
+
// Webshop sender only; recipient names stay out. A verified lookup sends
|
|
163
|
+
// `sender` as an object with a company, a name, an id and a postal address:
|
|
164
|
+
// only the company, then the name, is read. The `receiver` block never is.
|
|
165
|
+
const sender = isRecord(payload.sender) ? payload.sender : {};
|
|
166
|
+
const value = firstText(sender.companyName, sender.name, payload.senderName, payload.sender, current.senderName);
|
|
167
|
+
return value ? value.slice(0, 200) : null;
|
|
168
|
+
}
|
|
169
|
+
function apiPickupPoint(payload, current, stage) {
|
|
170
|
+
if (stage !== 'ready_for_pickup')
|
|
171
|
+
return null;
|
|
172
|
+
// ParcelShop collection points are operational locations, not private
|
|
173
|
+
// addresses. `receiverName` is the last fallback and is only read as text.
|
|
174
|
+
const value = firstText(...[current.pickupPoint, current.parcelShop, payload.pickupPoint, payload.parcelShop]
|
|
175
|
+
.map((candidate) => (isRecord(candidate) ? firstText(candidate.name, candidate.shopName) : candidate)), current.receiverName, payload.receiverName);
|
|
176
|
+
return value ? value.slice(0, 200) : null;
|
|
177
|
+
}
|
|
178
|
+
function apiLocation(event) {
|
|
179
|
+
const city = known(event.city);
|
|
180
|
+
// A scan's own `country` wins even when it is a placeholder: `depotCountry`
|
|
181
|
+
// names the business unit's depot on every scan, customs and paperwork scans
|
|
182
|
+
// included. It stays the last fallback for a scan that names no country at
|
|
183
|
+
// all, as it always was, so places stored from such scans keep their identity.
|
|
184
|
+
const country = known(textual(event.country) ?? textual(event.countryCode) ?? textual(event.depotCountry));
|
|
185
|
+
return city && country && city.toLocaleLowerCase('en-US') !== country.toLocaleLowerCase('en-US')
|
|
186
|
+
? `${city}, ${country}`
|
|
187
|
+
: city || country;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* A scan's `eventDateAndTimeZoneId` as a zone luxon reads. DPD sends "+02:00"
|
|
191
|
+
* and "Europe/Zurich"; "+0200", "UTC+2", "Z", "UTC" and "GMT" are accepted
|
|
192
|
+
* too. Anything else passes through, and an unreadable zone means Swiss time.
|
|
193
|
+
*/
|
|
194
|
+
function scanZone(value) {
|
|
195
|
+
const text = clean(value);
|
|
196
|
+
if (/^(?:Z|UTC|GMT)$/i.test(text))
|
|
197
|
+
return 'UTC';
|
|
198
|
+
const offset = /^(?:UTC|GMT)?([+-])(\d{1,2}):?(\d{2})?$/i.exec(text);
|
|
199
|
+
if (!offset)
|
|
200
|
+
return text;
|
|
201
|
+
const [, sign, hours, minutes = '00'] = offset;
|
|
202
|
+
return Number(hours) <= 14 && Number(minutes) < 60 ? `UTC${sign}${hours.padStart(2, '0')}:${minutes}` : '';
|
|
203
|
+
}
|
|
204
|
+
/** How this adapter read a zone id before `scanZone`; stored events carry its spelling. */
|
|
205
|
+
function storedTime(value, zoneId) {
|
|
206
|
+
const zoneText = clean(zoneId);
|
|
207
|
+
const zone = /^[+-]\d{2}:\d{2}$/.test(zoneText) ? `UTC${zoneText}` : zoneText || TIMEZONE;
|
|
208
|
+
return isoTime(value, zone) ?? (zoneText ? isoTime(value, TIMEZONE) : null);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Guest API timestamps: an explicit offset wins, then the zone DPD names for
|
|
212
|
+
* that scan, then Swiss time. Unparsable values keep their raw text.
|
|
213
|
+
*
|
|
214
|
+
* The sync keys stored events on this exact string. When the instant is the
|
|
215
|
+
* one the adapter read before (with `storedZoneId`, Swiss time for
|
|
216
|
+
* `parcelEvents`), the earlier spelling is returned so no stored scan is
|
|
217
|
+
* duplicated.
|
|
218
|
+
*/
|
|
219
|
+
function apiEventTime(date, clock = '', zoneId = null, storedZoneId = zoneId) {
|
|
220
|
+
const dateText = clean(date);
|
|
221
|
+
const clockText = clean(clock);
|
|
222
|
+
const value = clockText ? `${dateText}T${clockText}` : dateText;
|
|
223
|
+
if (!value)
|
|
224
|
+
return '';
|
|
225
|
+
const offsetParsed = explicitOffsetTime(value);
|
|
226
|
+
if (offsetParsed)
|
|
227
|
+
return offsetParsed.iso;
|
|
228
|
+
const zone = scanZone(zoneId);
|
|
229
|
+
const parsed = (zone ? isoTime(value, zone) : null) ?? isoTime(value, TIMEZONE);
|
|
230
|
+
const stored = storedTime(value, storedZoneId);
|
|
231
|
+
return (stored && stored.timestamp === parsed?.timestamp ? stored : parsed)?.iso
|
|
232
|
+
?? clean(`${dateText} ${clockText}`);
|
|
233
|
+
}
|
|
234
|
+
/** The parcel weight in kilograms; absent, zero and implausible values are dropped. */
|
|
235
|
+
function apiWeight(value) {
|
|
236
|
+
const text = clean(value).replace(',', '.');
|
|
237
|
+
const weight = /^\d+(?:\.\d+)?$/.test(text) ? Number(text) : Number.NaN;
|
|
238
|
+
return Number.isFinite(weight) && weight > 0 && weight < 10_000 ? weight : null;
|
|
239
|
+
}
|
|
240
|
+
function expectedDelivery(payload) {
|
|
241
|
+
const rawDate = clean(payload.deliveryDate);
|
|
242
|
+
if (!rawDate)
|
|
243
|
+
return null;
|
|
244
|
+
const date = /^\d{4}-\d{2}-\d{2}/.exec(rawDate)?.[0] ?? rawDate;
|
|
245
|
+
const shortTime = (value) => /^(\d{2}:\d{2})/.exec(clean(value))?.[1] ?? '';
|
|
246
|
+
const from = shortTime(payload.deliveryTimeFrom);
|
|
247
|
+
const to = shortTime(payload.deliveryTimeTo);
|
|
248
|
+
if (from && to)
|
|
249
|
+
return `${date} ${from}–${to}`;
|
|
250
|
+
return from || to ? `${date} ${from || to}` : date;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Newest scan first. The guest API has listed a parcel's scans oldest first,
|
|
254
|
+
* and the summary and freshness watermark read the first event. Equal or
|
|
255
|
+
* unreadable times keep the payload's order.
|
|
256
|
+
*/
|
|
257
|
+
function newestFirst(events) {
|
|
258
|
+
return events
|
|
259
|
+
.map((event, index) => ({ event, index, timestamp: Date.parse(event.time ?? '') || 0 }))
|
|
260
|
+
.sort((left, right) => right.timestamp - left.timestamp || left.index - right.index)
|
|
261
|
+
.map(({ event }) => event);
|
|
262
|
+
}
|
|
263
|
+
export function parseDPDTrackingApi(payload, trackingNumber, postcodeVerified) {
|
|
264
|
+
if (!isRecord(payload))
|
|
265
|
+
throw new DPDAPIError('DPD guest API returned an invalid response');
|
|
266
|
+
if (clean(payload.parcelNumber ?? payload.shipmentId) !== trackingNumber) {
|
|
267
|
+
throw new SchemaError('DPD', 'DPD did not return the requested parcel');
|
|
268
|
+
}
|
|
269
|
+
const history = Array.isArray(payload.parcelHistory) ? payload.parcelHistory.filter(isRecord) : [];
|
|
270
|
+
// A verified lookup lists each movement twice: in `parcelEvents` with its
|
|
271
|
+
// wording, place and scan code but a bare Swiss wall clock, and in
|
|
272
|
+
// `parcelHistory` with the enumeration and DPD's offset for that scan. A
|
|
273
|
+
// scan takes the offset of the history entry at the same wall clock, so the
|
|
274
|
+
// repeated autumn hour keeps its instant; without one it is read in Swiss time.
|
|
275
|
+
// Only a scan alone at its wall clock with a single entry there is paired
|
|
276
|
+
// and lent that entry's stage. Otherwise nothing says which entry is whose:
|
|
277
|
+
// the scans share an offset only when every entry there names the same zone.
|
|
278
|
+
const parcelEvents = Array.isArray(payload.parcelEvents) ? payload.parcelEvents.filter(isRecord) : [];
|
|
279
|
+
const scanWallClock = (raw) => `${clean(raw.date)}T${clean(raw.time)}`;
|
|
280
|
+
const entriesAt = new Map();
|
|
281
|
+
for (const raw of history) {
|
|
282
|
+
const wallClock = clean(raw.eventDateAndTime).slice(0, 19);
|
|
283
|
+
if (wallClock)
|
|
284
|
+
entriesAt.set(wallClock, [...(entriesAt.get(wallClock) ?? []), raw]);
|
|
285
|
+
}
|
|
286
|
+
const scansAt = new Map();
|
|
287
|
+
for (const raw of parcelEvents) {
|
|
288
|
+
const wallClock = scanWallClock(raw);
|
|
289
|
+
scansAt.set(wallClock, (scansAt.get(wallClock) ?? 0) + 1);
|
|
290
|
+
}
|
|
291
|
+
const scans = [];
|
|
292
|
+
const seen = new Set();
|
|
293
|
+
const append = (event) => {
|
|
294
|
+
const key = JSON.stringify([event.time ?? '', event.location ?? '', event.description ?? '']);
|
|
295
|
+
if (!seen.has(key)) {
|
|
296
|
+
seen.add(key);
|
|
297
|
+
scans.push(event);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
// Only the fields below leave a scan. `podUrl` embeds the parcel number, and
|
|
301
|
+
// `pudoId`, `buShortName` and `depotCountry` are DPD's own routing data.
|
|
302
|
+
for (const raw of parcelEvents) {
|
|
303
|
+
const code = scanCode(raw.eventType);
|
|
304
|
+
const wallClock = scanWallClock(raw);
|
|
305
|
+
const entries = entriesAt.get(wallClock) ?? [];
|
|
306
|
+
const twin = entries.length === 1 && scansAt.get(wallClock) === 1 ? entries[0] : undefined;
|
|
307
|
+
const zoneId = new Set(entries.map((entry) => clean(entry.eventDateAndTimeZoneId))).size === 1
|
|
308
|
+
? entries[0]?.eventDateAndTimeZoneId
|
|
309
|
+
: null;
|
|
310
|
+
const stage = scanStage(code) ?? apiStage(code) ?? (twin ? apiStage(twin.description) : null);
|
|
311
|
+
append({
|
|
312
|
+
time: apiEventTime(raw.date, raw.time, zoneId, null),
|
|
313
|
+
location: apiLocation(raw),
|
|
314
|
+
description: clean(textual(raw.translation) ?? textual(raw.eventTypeText) ?? apiDescription(raw.eventType))
|
|
315
|
+
|| 'Tracking update',
|
|
316
|
+
...(stage ? { stage } : {}),
|
|
317
|
+
...(code ? { provider_code: code } : {}),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (scans.length === 0) {
|
|
321
|
+
for (const raw of history) {
|
|
322
|
+
const code = scanCode(raw.description);
|
|
323
|
+
const stage = apiStage(code);
|
|
324
|
+
append({
|
|
325
|
+
time: apiEventTime(raw.eventDateAndTime, '', raw.eventDateAndTimeZoneId),
|
|
326
|
+
location: apiLocation(raw),
|
|
327
|
+
description: apiDescription(raw.description),
|
|
328
|
+
...(stage ? { stage } : {}),
|
|
329
|
+
...(code ? { provider_code: code } : {}),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const current = isRecord(payload.status) ? payload.status : {};
|
|
334
|
+
const currentDescription = current.description;
|
|
335
|
+
const stage = apiStage(currentDescription);
|
|
336
|
+
// The proof-of-delivery scan follows the delivery scan by minutes, with no
|
|
337
|
+
// place. Next to it the paperwork adds nothing, and as the newest event its
|
|
338
|
+
// wording, which no rule maps, would become the summary and the apps'
|
|
339
|
+
// current stage. Alone it proves the delivery only when the enumeration
|
|
340
|
+
// says delivered; otherwise (a return, say) it would hide the real state.
|
|
341
|
+
const deliveryScanned = scans.some((event) => (event.stage === 'delivered' && event.provider_code !== PROOF_OF_DELIVERY_SCAN));
|
|
342
|
+
const events = newestFirst(scans.flatMap((event) => {
|
|
343
|
+
if (event.provider_code !== PROOF_OF_DELIVERY_SCAN)
|
|
344
|
+
return [event];
|
|
345
|
+
return deliveryScanned || stage !== 'delivered' ? [] : [{ ...event, stage: 'delivered' }];
|
|
346
|
+
}));
|
|
347
|
+
const statusText = events[0]?.description || apiDescription(currentDescription)
|
|
348
|
+
|| 'Tracking information received';
|
|
349
|
+
const status = apiStatus(currentDescription, statusText, events.length > 0);
|
|
350
|
+
const sender = apiSender(payload, current);
|
|
351
|
+
const pickupPoint = apiPickupPoint(payload, current, stage);
|
|
352
|
+
const weight = apiWeight(payload.weight);
|
|
353
|
+
const deliveredAt = status === 'delivered'
|
|
354
|
+
? events.find((event) => event.stage === 'delivered')?.time
|
|
355
|
+
: undefined;
|
|
356
|
+
// Never projected: `receiver` (name, contact, address, geoPosition), the
|
|
357
|
+
// sender's id and address, `customerReference1/2`, `gttsZipCode`, `podUrl`
|
|
358
|
+
// and `product`, which holds the recipient's delivery preference.
|
|
359
|
+
const result = {
|
|
360
|
+
status,
|
|
361
|
+
...(stage ? { current_stage: stage } : {}),
|
|
362
|
+
last_status_text: statusText,
|
|
363
|
+
last_update: events[0]?.time || apiEventTime(current.eventDateAndTime, '', current.eventDateAndTimeZoneId) || null,
|
|
364
|
+
expected_delivery: status === 'delivered' || status === 'exception' ? null : expectedDelivery(payload),
|
|
365
|
+
events,
|
|
366
|
+
source: 'mydpd_guest_api',
|
|
367
|
+
delivery_date: optionalText(payload.deliveryDate),
|
|
368
|
+
delivery_time_from: optionalText(payload.deliveryTimeFrom),
|
|
369
|
+
delivery_time_to: optionalText(payload.deliveryTimeTo),
|
|
370
|
+
is_predictive_date: Boolean(payload.isPredictiveDate),
|
|
371
|
+
...(sender ? { sender_name: sender } : {}),
|
|
372
|
+
...(pickupPoint ? { pickup_point: pickupPoint } : {}),
|
|
373
|
+
...(weight !== null ? { weight_kg: weight } : {}),
|
|
374
|
+
...(deliveredAt ? { delivered_at: deliveredAt } : {}),
|
|
375
|
+
};
|
|
376
|
+
if (postcodeVerified !== undefined)
|
|
377
|
+
result.dpd_postcode_verified = postcodeVerified;
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
export function parseDPDTrackingHtml(html, trackingNumber) {
|
|
381
|
+
if (/Just a moment|cf-mitigated|Enable JavaScript and cookies/i.test(html)) {
|
|
382
|
+
throw new DPDChallengeError();
|
|
383
|
+
}
|
|
384
|
+
const $ = load(html);
|
|
385
|
+
$('script, style').remove();
|
|
386
|
+
const visible = clean($('body').text());
|
|
387
|
+
if (!visible.includes(trackingNumber)) {
|
|
388
|
+
throw new SchemaError('DPD', 'DPD did not return the requested parcel');
|
|
389
|
+
}
|
|
390
|
+
if (/no parcel|not found|nicht gefunden|aucun colis/i.test(visible)) {
|
|
391
|
+
return {
|
|
392
|
+
status: 'unknown',
|
|
393
|
+
last_status_text: 'No parcel found',
|
|
394
|
+
last_update: null,
|
|
395
|
+
expected_delivery: null,
|
|
396
|
+
events: [],
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const events = [];
|
|
400
|
+
$('li.content-item-track').each((_, element) => {
|
|
401
|
+
const row = $(element);
|
|
402
|
+
const description = clean(row.find('.entry-body').text());
|
|
403
|
+
if (!description)
|
|
404
|
+
return;
|
|
405
|
+
events.push({
|
|
406
|
+
time: eventTime(clean(row.find('.entry-date').text()), clean(row.find('.entry-time').text())),
|
|
407
|
+
location: clean(row.find('.place-track').text()),
|
|
408
|
+
description,
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
if (events.length === 0) {
|
|
412
|
+
const summary = [];
|
|
413
|
+
$('.parcelStatus .row').each((_, element) => {
|
|
414
|
+
const row = $(element);
|
|
415
|
+
const date = /\d{2}\.\d{2}\.\d{4}/.exec(clean(row.text()))?.[0] ?? '';
|
|
416
|
+
const description = clean(row.find('.col-xs-7').text())
|
|
417
|
+
|| clean(row.text()).replace(date, '').trim();
|
|
418
|
+
if (date && description)
|
|
419
|
+
summary.push({ date, description });
|
|
420
|
+
});
|
|
421
|
+
const offsets = new Map();
|
|
422
|
+
for (const item of summary) {
|
|
423
|
+
const offset = offsets.get(item.date) ?? 0;
|
|
424
|
+
offsets.set(item.date, offset + 1);
|
|
425
|
+
events.push({
|
|
426
|
+
time: eventTime(item.date, `00:${String(Math.floor(offset / 60)).padStart(2, '0')}:${String(offset % 60).padStart(2, '0')}`),
|
|
427
|
+
location: '',
|
|
428
|
+
description: item.description,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
events.reverse();
|
|
432
|
+
}
|
|
433
|
+
const labels = $('.gray-out').map((_, element) => clean($(element).text())).get().filter(Boolean);
|
|
434
|
+
const statusText = events[0]?.description ?? labels.at(-1) ?? 'Tracking information received';
|
|
435
|
+
return {
|
|
436
|
+
status: wordingStatus(statusText, events.length > 0),
|
|
437
|
+
last_status_text: statusText,
|
|
438
|
+
last_update: events[0]?.time || null,
|
|
439
|
+
expected_delivery: null,
|
|
440
|
+
events,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function durationSeconds(value, fallback) {
|
|
444
|
+
const match = /^(\d+)s?$/.exec(String(value ?? ''));
|
|
445
|
+
return match ? Number(match[1]) : fallback;
|
|
446
|
+
}
|
|
447
|
+
export class DPDTracker {
|
|
448
|
+
timeoutMs;
|
|
449
|
+
budgetMs;
|
|
450
|
+
flaresolverrUrl;
|
|
451
|
+
firebaseApiKey;
|
|
452
|
+
fetcher;
|
|
453
|
+
trawl;
|
|
454
|
+
recorder;
|
|
455
|
+
#accessToken = '';
|
|
456
|
+
#accessTokenExpiresAt = 0;
|
|
457
|
+
#basicToken = '';
|
|
458
|
+
#installationFid = '';
|
|
459
|
+
#installationToken = '';
|
|
460
|
+
#installationExpiresAt = 0;
|
|
461
|
+
#tokenRefresh = null;
|
|
462
|
+
#tokenFailure = null;
|
|
463
|
+
constructor(options = {}) {
|
|
464
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
465
|
+
this.budgetMs = options.budgetMs ?? this.timeoutMs * 2 + SOLVER_ALLOWANCE_MS;
|
|
466
|
+
this.flaresolverrUrl = (options.flaresolverrUrl ?? '').trim();
|
|
467
|
+
this.firebaseApiKey = (options.firebaseApiKey ?? FIREBASE_API_KEY).trim();
|
|
468
|
+
this.fetcher = options.fetcher;
|
|
469
|
+
this.trawl = options.trawl;
|
|
470
|
+
this.recorder = options.recorder;
|
|
471
|
+
}
|
|
472
|
+
/** The browser service, resolved late so a malformed URL fails the page step, not construction. */
|
|
473
|
+
browserService() {
|
|
474
|
+
if (this.trawl !== undefined)
|
|
475
|
+
return this.trawl;
|
|
476
|
+
return this.flaresolverrUrl ? new TrawlClient(this.flaresolverrUrl, this.fetcher) : null;
|
|
477
|
+
}
|
|
478
|
+
async fetch(trackingNumber, postcode = '') {
|
|
479
|
+
if (!/^\d{14}$/.test(trackingNumber)) {
|
|
480
|
+
throw new TypeError('DPD tracking numbers must contain 14 digits');
|
|
481
|
+
}
|
|
482
|
+
const resolvedPostcode = postcode.trim();
|
|
483
|
+
if (resolvedPostcode && !/^\d{4}$/.test(resolvedPostcode)) {
|
|
484
|
+
throw new TypeError('DPD postcode must contain exactly 4 digits');
|
|
485
|
+
}
|
|
486
|
+
const result = await runSteps({
|
|
487
|
+
carrier: 'dpd', budgetMs: this.budgetMs, recorder: this.recorder,
|
|
488
|
+
}, [
|
|
489
|
+
{ id: 'direct', run: () => this.apiFetch(trackingNumber, resolvedPostcode) },
|
|
490
|
+
{
|
|
491
|
+
id: 'page',
|
|
492
|
+
recovers: (error) => {
|
|
493
|
+
const kind = carrierErrorKind(error);
|
|
494
|
+
return kind !== null && PAGE_RECOVERS.has(kind);
|
|
495
|
+
},
|
|
496
|
+
run: ({ previousError }) => this.pageFetch(trackingNumber, previousError !== undefined),
|
|
497
|
+
},
|
|
498
|
+
]);
|
|
499
|
+
result.tracking_url = dpdTrackingUrl(trackingNumber);
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Whether DPD knows a 14-digit number, for the host's carrier-detection
|
|
504
|
+
* route, which promotes the number to `dpd` only when this returns true.
|
|
505
|
+
* Guest API only, never the page tier. A positive not-found (404) and a
|
|
506
|
+
* details lookup DPD refuses without the postcode (400, seen for old
|
|
507
|
+
* parcels) are false; any other failure, a 400 from a token step included,
|
|
508
|
+
* stays a failure.
|
|
509
|
+
*/
|
|
510
|
+
async recognizes(trackingNumber) {
|
|
511
|
+
if (!/^\d{14}$/.test(trackingNumber))
|
|
512
|
+
return false;
|
|
513
|
+
try {
|
|
514
|
+
const payload = await this.detailsWithFreshToken(trackingNumber);
|
|
515
|
+
return clean(payload.parcelNumber ?? payload.shipmentId) === trackingNumber;
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
if (error instanceof DPDTrackingError)
|
|
519
|
+
return false;
|
|
520
|
+
if (error instanceof DPDDetailsRefusedError)
|
|
521
|
+
return false;
|
|
522
|
+
throw error;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async apiFetch(trackingNumber, postcode) {
|
|
526
|
+
let postcodeVerified;
|
|
527
|
+
let payload;
|
|
528
|
+
try {
|
|
529
|
+
payload = await this.detailsWithFreshToken(trackingNumber, postcode || undefined);
|
|
530
|
+
if (postcode)
|
|
531
|
+
postcodeVerified = true;
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
if (!(error instanceof DPDAPIHttpError) || !postcode || error.status !== 400)
|
|
535
|
+
throw error;
|
|
536
|
+
payload = await this.detailsWithFreshToken(trackingNumber);
|
|
537
|
+
postcodeVerified = false;
|
|
538
|
+
}
|
|
539
|
+
return parseDPDTrackingApi(payload, trackingNumber, postcodeVerified);
|
|
540
|
+
}
|
|
541
|
+
async detailsWithFreshToken(trackingNumber, postcode) {
|
|
542
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
543
|
+
const token = await this.accessToken();
|
|
544
|
+
try {
|
|
545
|
+
return await this.parcelDetails(trackingNumber, postcode, token);
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
if (!(error instanceof DPDAPIHttpError) || error.status !== 401 || attempt > 0)
|
|
549
|
+
throw error;
|
|
550
|
+
this.#accessToken = '';
|
|
551
|
+
this.#accessTokenExpiresAt = 0;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
throw new DPDAPIError('DPD guest API authentication failed');
|
|
555
|
+
}
|
|
556
|
+
async parcelDetails(trackingNumber, postcode, token) {
|
|
557
|
+
const url = new URL(`${DETAILS_BASE}/${encodeURIComponent(trackingNumber)}`);
|
|
558
|
+
url.searchParams.set('parcelType', 'INCOMING');
|
|
559
|
+
url.searchParams.set('businessUnit', 'DPD-CH');
|
|
560
|
+
url.searchParams.set('lang', 'en');
|
|
561
|
+
url.searchParams.set('continueWithoutVerification', postcode ? 'false' : 'true');
|
|
562
|
+
if (postcode)
|
|
563
|
+
url.searchParams.set('dataForVerification', postcode);
|
|
564
|
+
try {
|
|
565
|
+
return await this.requestJson(url, '', {
|
|
566
|
+
Authorization: `Bearer ${token}`,
|
|
567
|
+
Accept: 'application/json',
|
|
568
|
+
'Content-Type': 'application/json',
|
|
569
|
+
'User-Agent': `myDPD/${CLIENT_VERSION} (Android)`,
|
|
570
|
+
}, true);
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
if (error instanceof DPDAPIHttpError && error.status === 404) {
|
|
574
|
+
throw new DPDTrackingError();
|
|
575
|
+
}
|
|
576
|
+
if (error instanceof DPDAPIHttpError && error.status === 400)
|
|
577
|
+
throw new DPDDetailsRefusedError();
|
|
578
|
+
throw error;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async accessToken() {
|
|
582
|
+
if (this.#accessToken && Date.now() < this.#accessTokenExpiresAt)
|
|
583
|
+
return this.#accessToken;
|
|
584
|
+
// Concurrent lookups share one refresh and its outcome. A failed guest
|
|
585
|
+
// login is answered from memory for a short while, so a queue of lookups
|
|
586
|
+
// during an outage does not replay the whole token chain one by one.
|
|
587
|
+
if (this.#tokenRefresh)
|
|
588
|
+
return await this.#tokenRefresh;
|
|
589
|
+
if (this.#tokenFailure && Date.now() < this.#tokenFailure.until)
|
|
590
|
+
throw this.#tokenFailure.error;
|
|
591
|
+
this.#tokenRefresh = this.refreshAccessToken().then((token) => {
|
|
592
|
+
this.#tokenFailure = null;
|
|
593
|
+
return token;
|
|
594
|
+
}, (error) => {
|
|
595
|
+
this.#tokenFailure = { error, until: Date.now() + TOKEN_FAILURE_MEMORY_MS };
|
|
596
|
+
throw error;
|
|
597
|
+
}).finally(() => { this.#tokenRefresh = null; });
|
|
598
|
+
return await this.#tokenRefresh;
|
|
599
|
+
}
|
|
600
|
+
async refreshAccessToken() {
|
|
601
|
+
let payload = null;
|
|
602
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
603
|
+
const basicToken = this.#basicToken || await this.fetchBasicToken();
|
|
604
|
+
try {
|
|
605
|
+
payload = await this.requestJson(OAUTH_URL, '', {
|
|
606
|
+
Authorization: `Basic ${basicToken}`,
|
|
607
|
+
Accept: 'application/json',
|
|
608
|
+
'Content-Type': 'application/json',
|
|
609
|
+
'User-Agent': `myDPD/${CLIENT_VERSION} (Android)`,
|
|
610
|
+
});
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
if (!(error instanceof DPDAPIHttpError)
|
|
615
|
+
|| ![400, 401].includes(error.status)
|
|
616
|
+
|| attempt > 0)
|
|
617
|
+
throw error;
|
|
618
|
+
this.#basicToken = '';
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
const token = clean(payload?.access_token);
|
|
622
|
+
if (!token)
|
|
623
|
+
throw new DPDAPIError('DPD guest API did not issue an access token');
|
|
624
|
+
this.#accessToken = token;
|
|
625
|
+
this.#accessTokenExpiresAt = Date.now()
|
|
626
|
+
+ Math.max(1, durationSeconds(payload?.expires_in, 3_600) - 60) * 1_000;
|
|
627
|
+
return token;
|
|
628
|
+
}
|
|
629
|
+
async fetchBasicToken() {
|
|
630
|
+
if (!this.firebaseApiKey)
|
|
631
|
+
throw new DPDAPIError('DPD Firebase client configuration is missing');
|
|
632
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
633
|
+
const [fid, installationToken] = await this.firebaseInstallation();
|
|
634
|
+
try {
|
|
635
|
+
const payload = await this.requestJson(REMOTE_CONFIG_URL, {
|
|
636
|
+
appId: FIREBASE_APP_ID,
|
|
637
|
+
appInstanceId: fid,
|
|
638
|
+
appInstanceIdToken: installationToken,
|
|
639
|
+
languageCode: 'en-US',
|
|
640
|
+
countryCode: 'CH',
|
|
641
|
+
platformVersion: '36',
|
|
642
|
+
appVersion: CLIENT_VERSION,
|
|
643
|
+
packageName: ANDROID_PACKAGE,
|
|
644
|
+
sdkVersion: '22.1.2',
|
|
645
|
+
analyticsUserProperties: {},
|
|
646
|
+
}, this.firebaseHeaders({ 'X-Goog-Firebase-Installations-Auth': installationToken }));
|
|
647
|
+
const entries = isRecord(payload.entries) ? payload.entries : {};
|
|
648
|
+
const token = clean(entries.basic_dpd_token);
|
|
649
|
+
if (!token)
|
|
650
|
+
throw new DPDAPIError('myDPD Remote Config omitted its guest credential');
|
|
651
|
+
this.#basicToken = token;
|
|
652
|
+
return token;
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
if (!(error instanceof DPDAPIHttpError)
|
|
656
|
+
|| ![401, 403].includes(error.status)
|
|
657
|
+
|| attempt > 0)
|
|
658
|
+
throw error;
|
|
659
|
+
this.#installationFid = '';
|
|
660
|
+
this.#installationToken = '';
|
|
661
|
+
this.#installationExpiresAt = 0;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
throw new DPDAPIError('myDPD Remote Config authentication failed');
|
|
665
|
+
}
|
|
666
|
+
async firebaseInstallation() {
|
|
667
|
+
if (this.#installationFid
|
|
668
|
+
&& this.#installationToken
|
|
669
|
+
&& Date.now() < this.#installationExpiresAt) {
|
|
670
|
+
return [this.#installationFid, this.#installationToken];
|
|
671
|
+
}
|
|
672
|
+
const bytes = randomBytes(17);
|
|
673
|
+
bytes[0] = 0x70 | (bytes[0] & 0x0f);
|
|
674
|
+
const fid = bytes.toString('base64url').slice(0, 22);
|
|
675
|
+
const payload = await this.requestJson(INSTALLATIONS_URL, {
|
|
676
|
+
fid,
|
|
677
|
+
appId: FIREBASE_APP_ID,
|
|
678
|
+
authVersion: 'FIS_v2',
|
|
679
|
+
sdkVersion: 'a:18.0.0',
|
|
680
|
+
}, this.firebaseHeaders());
|
|
681
|
+
const auth = isRecord(payload.authToken) ? payload.authToken : {};
|
|
682
|
+
const token = clean(auth.token);
|
|
683
|
+
if (!token)
|
|
684
|
+
throw new DPDAPIError('Firebase did not issue a myDPD installation token');
|
|
685
|
+
this.#installationFid = clean(payload.fid) || fid;
|
|
686
|
+
this.#installationToken = token;
|
|
687
|
+
this.#installationExpiresAt = Date.now()
|
|
688
|
+
+ Math.max(1, durationSeconds(auth.expiresIn, 604_800) - 300) * 1_000;
|
|
689
|
+
return [this.#installationFid, token];
|
|
690
|
+
}
|
|
691
|
+
firebaseHeaders(extra = {}) {
|
|
692
|
+
return {
|
|
693
|
+
Accept: 'application/json',
|
|
694
|
+
'Content-Type': 'application/json',
|
|
695
|
+
'X-Goog-Api-Key': this.firebaseApiKey,
|
|
696
|
+
'X-Android-Package': ANDROID_PACKAGE,
|
|
697
|
+
'X-Android-Cert': ANDROID_CERT,
|
|
698
|
+
...extra,
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
async requestJson(url, data, headers, retryRead = false) {
|
|
702
|
+
const deadline = performance.now() + this.timeoutMs;
|
|
703
|
+
let result;
|
|
704
|
+
try {
|
|
705
|
+
for (let attempt = 0;; attempt++) {
|
|
706
|
+
result = await fetchBounded(url, {
|
|
707
|
+
method: 'POST',
|
|
708
|
+
headers,
|
|
709
|
+
body: typeof data === 'string' ? data : JSON.stringify(data),
|
|
710
|
+
}, {
|
|
711
|
+
provider: 'DPD guest API',
|
|
712
|
+
timeoutMs: Math.max(1, Math.floor(deadline - performance.now())),
|
|
713
|
+
maxBytes: MAX_BYTES,
|
|
714
|
+
allowHttpError: true,
|
|
715
|
+
fetcher: this.fetcher,
|
|
716
|
+
});
|
|
717
|
+
// Only the read-only parcel-details POST is replayed; auth requests are not.
|
|
718
|
+
const pause = 1_000 + Math.floor(Math.random() * 2_000);
|
|
719
|
+
const retryAfter = result.response.headers.get('retry-after');
|
|
720
|
+
if (!retryRead || attempt > 0 || ![502, 503, 504].includes(result.response.status)
|
|
721
|
+
|| retryAfter !== null || deadline - performance.now() < pause + 1_000)
|
|
722
|
+
break;
|
|
723
|
+
await delay(pause);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
throw new DPDAPIError('DPD guest API is unreachable', { cause: error });
|
|
728
|
+
}
|
|
729
|
+
if (!result.response.ok)
|
|
730
|
+
throw new DPDAPIHttpError(result.response.status);
|
|
731
|
+
let payload;
|
|
732
|
+
try {
|
|
733
|
+
payload = parseJsonBytes(result.bytes, 'DPD guest API');
|
|
734
|
+
}
|
|
735
|
+
catch (error) {
|
|
736
|
+
throw new DPDAPIError('DPD guest API returned invalid JSON', { cause: error });
|
|
737
|
+
}
|
|
738
|
+
if (!isRecord(payload))
|
|
739
|
+
throw new DPDAPIError('DPD guest API returned an invalid response');
|
|
740
|
+
return payload;
|
|
741
|
+
}
|
|
742
|
+
async pageFetch(trackingNumber, apiFailed) {
|
|
743
|
+
const url = new URL(FETCH_BASE);
|
|
744
|
+
url.searchParams.set('lang', 'en');
|
|
745
|
+
url.searchParams.set('parcelNumber', trackingNumber);
|
|
746
|
+
const trawl = this.browserService();
|
|
747
|
+
let html;
|
|
748
|
+
if (trawl) {
|
|
749
|
+
html = await trawl.solve(url.toString(), {
|
|
750
|
+
provider: 'The browser challenge solver',
|
|
751
|
+
timeoutMs: this.timeoutMs,
|
|
752
|
+
maxBytes: MAX_BYTES,
|
|
753
|
+
fetcher: this.fetcher,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
try {
|
|
758
|
+
html = await this.directGet(url);
|
|
759
|
+
}
|
|
760
|
+
catch (error) {
|
|
761
|
+
if (!(error instanceof DPDChallengeError))
|
|
762
|
+
throw error;
|
|
763
|
+
const prefix = apiFailed ? 'DPD guest API is unavailable and ' : 'DPD ';
|
|
764
|
+
throw new DPDChallengeError(`${prefix}the web fallback requires a browser challenge solver; configure FLARESOLVERR_URL`, { cause: error });
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return parseDPDTrackingHtml(html, trackingNumber);
|
|
768
|
+
}
|
|
769
|
+
async directGet(url) {
|
|
770
|
+
const result = await fetchBounded(url, {
|
|
771
|
+
headers: {
|
|
772
|
+
Accept: 'text/html,application/xhtml+xml',
|
|
773
|
+
'Accept-Language': 'en-CH,en;q=0.9',
|
|
774
|
+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/140 Safari/537.36',
|
|
775
|
+
},
|
|
776
|
+
}, {
|
|
777
|
+
provider: 'DPD',
|
|
778
|
+
timeoutMs: this.timeoutMs,
|
|
779
|
+
maxBytes: MAX_BYTES,
|
|
780
|
+
redirect: 'follow',
|
|
781
|
+
allowHttpError: true,
|
|
782
|
+
fetcher: this.fetcher,
|
|
783
|
+
});
|
|
784
|
+
const html = decodeText(result.bytes);
|
|
785
|
+
if (result.response.status === 403
|
|
786
|
+
&& (result.response.headers.get('cf-mitigated') === 'challenge'
|
|
787
|
+
|| /Just a moment|Enable JavaScript and cookies/i.test(html))) {
|
|
788
|
+
throw new DPDChallengeError();
|
|
789
|
+
}
|
|
790
|
+
if (!result.response.ok)
|
|
791
|
+
throw new IndeterminateError('DPD', `DPD returned HTTP ${result.response.status}`);
|
|
792
|
+
return html;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
export const adapter = (environment) => {
|
|
796
|
+
const tracker = new DPDTracker({
|
|
797
|
+
fetcher: environment.fetcher,
|
|
798
|
+
trawl: environment.trawl,
|
|
799
|
+
recorder: environment.recorder,
|
|
800
|
+
});
|
|
801
|
+
return {
|
|
802
|
+
id: 'dpd',
|
|
803
|
+
// The guest JSON protocol first; the Cloudflare-protected consignee page,
|
|
804
|
+
// solved by the browser service when one is configured, second.
|
|
805
|
+
steps: ['direct', 'page'],
|
|
806
|
+
track: (input) => tracker.fetch(input.number, input.postcode ?? ''),
|
|
807
|
+
recognize: async (number) => ({ known: await tracker.recognizes(number) }),
|
|
808
|
+
};
|
|
809
|
+
};
|