universal-parcel-scraper 0.968.2-main.5250 → 0.968.3-main.5252

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.
@@ -9,6 +9,7 @@ import { runSteps, singleFlight, takeTurn } from '../../core/runner/index.js';
9
9
  import { languageStageStatus } from '../../core/status/index.js';
10
10
  import { NOOP_RECORDER } from '../../core/telemetry/index.js';
11
11
  import { calendarDay, countryCode, countryTimeZone, firstOccurrence } from '../../core/time/index.js';
12
+ import { otherRegionZones, regionZone } from '../../places/zones.js';
12
13
  import { clean, cleanScalar, decodeText, fetchBounded, parseJsonBytes, TRAWL_TRANSPORT_ALLOWANCE_MS } from '../../core/transport/index.js';
13
14
  import { isRecord } from '../../core/types.js';
14
15
  import { UPSApiClient, upsApiCredentials, upsApiNotFound, upsApiNotices } from './api.js';
@@ -351,18 +352,22 @@ function statedOffset(stated) {
351
352
  }
352
353
  /**
353
354
  * The zone of a scan's place, from its country's code or English name, when the country keeps one
354
- * civil time. Spain and Portugal have none here: their islands keep a clock an hour from the
355
- * mainland's, the offset the mainland keeps in the other season.
355
+ * civil time, or from its state or province's code in a country with several, when all of it
356
+ * keeps one: California's, not Texas's. Spain and Portugal have none here: their islands keep a
357
+ * clock an hour from the mainland's, the offset the mainland keeps in the other season.
356
358
  */
357
- function placeZone(country) {
359
+ function placeZone(country, region) {
358
360
  const code = countryCode(country);
359
- return code && code !== 'ES' && code !== 'PT' ? countryTimeZone(code) : null;
361
+ if (!code || code === 'ES' || code === 'PT')
362
+ return null;
363
+ const state = region.trim().toUpperCase();
364
+ return countryTimeZone(code) ?? (state && !otherRegionZones(code, state).length ? regionZone(code, state) : null);
360
365
  }
361
366
  /**
362
367
  * A scan UPS states on its place's offset for the other season, one the place's zone does not
363
368
  * keep at that wall clock but keeps at another time of the year, dated on the place's clock.
364
- * After the clocks went back, UPS dated deliveries in France on the summer offset: each reached
365
- * its answer an hour after the instant that offset gives, and the wall clock was the place's.
369
+ * After the clocks went back, UPS dated deliveries in France and California on the summer offset:
370
+ * each reached its answer an hour after the instant that offset gives, and the wall clock was the place's.
366
371
  * The scan keeps that wall clock, and the instant says its zone is the place's.
367
372
  */
368
373
  function placeClock(wall, offset, zone) {
@@ -421,7 +426,7 @@ function scanClock(activity, local, day, raw, zone) {
421
426
  return { local_time: day };
422
427
  return raw ? { provider_time_text: raw } : {};
423
428
  }
424
- /** The clock of a scan in the page's reply, whose local pair reads `08/04/2026` and `9:12 A.M.`, at a place such as `ZUERICH, CH`. */
429
+ /** The clock of a scan in the page's reply, whose local pair reads `08/04/2026` and `9:12 A.M.`, at a place such as `ZUERICH, CH` or `SAN DIEGO, CA, US`. */
425
430
  function activityClock(activity, place) {
426
431
  const localDate = cleanScalar(activity.date);
427
432
  const localTime = cleanScalar(activity.time);
@@ -432,7 +437,8 @@ function activityClock(activity, place) {
432
437
  ? `${dateMatch[3]}-${dateMatch[1]}-${dateMatch[2]}T${String(hour % 12 + (timeMatch[3].toUpperCase() === 'P' ? 12 : 0)).padStart(2, '0')}:${timeMatch[2]}:00`
433
438
  : null;
434
439
  const day = dateMatch && !localTime ? calendarDay(Number(dateMatch[3]), Number(dateMatch[1]), Number(dateMatch[2])) : null;
435
- return scanClock(activity, local, day, clean(`${localDate} ${localTime}`), placeZone(place.split(',').at(-1).trim()));
440
+ const fields = place.split(',').map((field) => field.trim());
441
+ return scanClock(activity, local, day, clean(`${localDate} ${localTime}`), placeZone(fields.at(-1), fields.length > 2 ? fields.at(-2) : ''));
436
442
  }
437
443
  /** The clock of a scan in the official API's reply, whose local pair reads `20260804` and `091204`. */
438
444
  function apiActivityClock(activity) {
@@ -443,7 +449,7 @@ function apiActivityClock(activity) {
443
449
  const day = dateMatch ? calendarDay(Number(dateMatch[1]), Number(dateMatch[2]), Number(dateMatch[3])) : null;
444
450
  const local = day && timeMatch ? `${day}T${timeMatch[1]}:${timeMatch[2]}:${timeMatch[3]}` : null;
445
451
  const address = isRecord(activity.location) && isRecord(activity.location.address) ? activity.location.address : {};
446
- return scanClock(activity, local, localTime ? null : day, clean(`${localDate} ${localTime}`), placeZone(cleanScalar(address.countryCode || address.country, 8)));
452
+ return scanClock(activity, local, localTime ? null : day, clean(`${localDate} ${localTime}`), placeZone(cleanScalar(address.countryCode || address.country, 8), cleanScalar(address.stateProvince, 8)));
447
453
  }
448
454
  const MONTHS = {
449
455
  jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6,
@@ -652,6 +652,9 @@ function providerWordingStage(description) {
652
652
  return 'in_transit';
653
653
  if (/^delivery to local courier[.!]?$/i.test(value))
654
654
  return 'in_transit';
655
+ // YunExpress's handler taking the parcel from the airline's cargo terminal after its flight, as its own map reads it.
656
+ if (/^collected at cargo terminal[.!]?$/i.test(value))
657
+ return 'in_transit';
655
658
  // Maersk's leg as ParcelsApp relays it after the first carrier's: the manifest announcing the parcel, then
656
659
  // Maersk taking it over in the destination country, the step the first carrier calls its local delivery
657
660
  // company's receipt. A parcel relabelled at a facility on the way moves on.
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.968.2-main.5250"
5
+ "version": "0.968.3-main.5252"
6
6
  },
7
7
  "paths": {
8
8
  "/health": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-parcel-scraper",
3
- "version": "0.968.2-main.5250",
3
+ "version": "0.968.3-main.5252",
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",