universal-parcel-scraper 0.993.0-main.5345 → 0.993.1-main.5347
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.
|
@@ -5,3 +5,5 @@ export declare const DHLINK_RECORD_ZONE = "Asia/Shanghai";
|
|
|
5
5
|
* gives that instant.
|
|
6
6
|
*/
|
|
7
7
|
export declare function usClockCopy(wall: string, timestamp: number): boolean;
|
|
8
|
+
/** The US zone, the state's first, on whose clock `wall` strikes at `timestamp`; null when none does. */
|
|
9
|
+
export declare function usClockZone(wall: string, timestamp: number, state?: string | null): string | null;
|
|
@@ -10,5 +10,9 @@ const US_ZONES = ['America/New_York', 'America/Chicago', 'America/Denver', 'Amer
|
|
|
10
10
|
* gives that instant.
|
|
11
11
|
*/
|
|
12
12
|
export function usClockCopy(wall, timestamp) {
|
|
13
|
-
return
|
|
13
|
+
return usClockZone(wall, timestamp) !== null;
|
|
14
|
+
}
|
|
15
|
+
/** The US zone, the state's first, on whose clock `wall` strikes at `timestamp`; null when none does. */
|
|
16
|
+
export function usClockZone(wall, timestamp, state) {
|
|
17
|
+
return [...(state ? [state] : []), ...US_ZONES].find((zone) => isoTime(wall, zone)?.timestamp === timestamp) ?? null;
|
|
14
18
|
}
|
|
@@ -7,7 +7,7 @@ import { countryCode, firstOccurrence, isoTime, usStateTimeZone } from '../../co
|
|
|
7
7
|
import { cleanScalar } from '../../core/transport/text.js';
|
|
8
8
|
import { isRecord } from '../../core/types.js';
|
|
9
9
|
import { countryZones } from '../../places/zones.js';
|
|
10
|
-
import { DHLINK_RECORD_ZONE as RECORD_ZONE, usClockCopy } from './clock.js';
|
|
10
|
+
import { DHLINK_RECORD_ZONE as RECORD_ZONE, usClockCopy, usClockZone } from './clock.js';
|
|
11
11
|
import { dhlinkEnglish, dhlinkStage, FLIGHT_BOOKED } from './status.js';
|
|
12
12
|
export const PROVIDER = 'DHLink';
|
|
13
13
|
// A long history is a few dozen kilobytes.
|
|
@@ -179,6 +179,10 @@ function projectRow(row, readAt) {
|
|
|
179
179
|
return { stamp: local, event, rank, china, state, hub: usStateTimeZone(hub) !== null,
|
|
180
180
|
recorded: Math.min(recordedAt(row) ?? readAt, readAt) };
|
|
181
181
|
}
|
|
182
|
+
/** A relayed wording without the town and state a US partner can write before it ("Sampletown IL, "). */
|
|
183
|
+
function unplaced(description) {
|
|
184
|
+
return (description ?? '').replace(/^[A-Z][A-Za-z .'-]{0,40} [A-Z]{2}, /, '');
|
|
185
|
+
}
|
|
182
186
|
function outranks(left, right) {
|
|
183
187
|
const index = left.findIndex((value, position) => value !== right[position]);
|
|
184
188
|
return index >= 0 && left[index] > right[index];
|
|
@@ -259,6 +263,29 @@ export function parseDhlink(payload, raw, readAt = Date.now()) {
|
|
|
259
263
|
const zone = scanZone(scan, destination, last?.leg.beijingClock === true && !(scan.hub && last.leg.hubClockPickup));
|
|
260
264
|
return { scan, zone, time: zone ? isoTime(scan.stamp, zone) : null };
|
|
261
265
|
});
|
|
266
|
+
// A US partner's scan can come twice, as UniUni's do: on its hub's wall clock, often after the
|
|
267
|
+
// hub's town, and on UTC. The UTC copy's clock is the other's on a US reading, to the second,
|
|
268
|
+
// before DHLink recorded either: one scan, on that US clock.
|
|
269
|
+
const utcCopies = new Set();
|
|
270
|
+
if (destination === 'US') {
|
|
271
|
+
for (const local of dated) {
|
|
272
|
+
if (utcCopies.has(local))
|
|
273
|
+
continue;
|
|
274
|
+
const wording = unplaced(local.scan.event.description);
|
|
275
|
+
const copy = dated.find((other) => {
|
|
276
|
+
if (other === local || utcCopies.has(other) || unplaced(other.scan.event.description) !== wording)
|
|
277
|
+
return false;
|
|
278
|
+
const instant = isoTime(other.scan.stamp, 'UTC')?.timestamp;
|
|
279
|
+
return instant !== undefined && instant <= Math.min(local.scan.recorded, other.scan.recorded) + CLOCK_SKEW_MS
|
|
280
|
+
&& usClockZone(local.scan.stamp, instant, local.scan.state) !== null;
|
|
281
|
+
});
|
|
282
|
+
if (!copy)
|
|
283
|
+
continue;
|
|
284
|
+
const zone = usClockZone(local.scan.stamp, isoTime(copy.scan.stamp, 'UTC').timestamp, local.scan.state);
|
|
285
|
+
Object.assign(local, { zone, time: isoTime(local.scan.stamp, zone) });
|
|
286
|
+
utcCopies.add(copy);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
262
289
|
// Beside DHLink's own record of a partner's scan on Beijing time, the partner's copy can come
|
|
263
290
|
// on its US wall clock: the same wording, at the instant of DHLink's row on a US reading.
|
|
264
291
|
// Without a partner known to be relayed on Beijing time, DHLink's row must be read on it.
|
|
@@ -266,7 +293,7 @@ export function parseDhlink(payload, raw, readAt = Date.now()) {
|
|
|
266
293
|
&& other.scan.event.description === scan.event.description && usClockCopy(scan.stamp, other.time.timestamp))) : [];
|
|
267
294
|
// A place naming only the destination country is the parcel's, not the scan's: a Canada line's
|
|
268
295
|
// airline hand-over in China reads "Canada".
|
|
269
|
-
const kept = dated.filter((entry) => !copies.includes(entry));
|
|
296
|
+
const kept = dated.filter((entry) => !copies.includes(entry) && !utcCopies.has(entry));
|
|
270
297
|
const events = kept
|
|
271
298
|
.map(({ scan, time }) => {
|
|
272
299
|
const { location, ...event } = scan.event;
|
package/dist/server/openapi.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-parcel-scraper",
|
|
3
|
-
"version": "0.993.
|
|
3
|
+
"version": "0.993.1-main.5347",
|
|
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",
|