universal-parcel-scraper 0.1007.0-main.5443 → 0.1007.2-main.5446

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/data/catalog.json CHANGED
@@ -4585,7 +4585,9 @@
4585
4585
  "displayName": "Korea Post",
4586
4586
  "color": "#da291c",
4587
4587
  "aliases": [
4588
- "South Korea Post"
4588
+ "South Korea Post",
4589
+ "ePOST (인터넷우체국)",
4590
+ "ePOST (인터넷우체국)(Domestic)"
4589
4591
  ],
4590
4592
  "countries": [
4591
4593
  "KR"
@@ -43242,7 +43244,8 @@
43242
43244
  "Shopee Express Brasil",
43243
43245
  "Shopee Xpress Brasil",
43244
43246
  "Shopee Express Brazil",
43245
- "SPX Brasil"
43247
+ "SPX Brasil",
43248
+ "SPX Express (BR)"
43246
43249
  ],
43247
43250
  "countries": [
43248
43251
  "BR"
@@ -43336,7 +43339,8 @@
43336
43339
  "aliases": [
43337
43340
  "Shopee Express Malaysia",
43338
43341
  "Shopee Xpress Malaysia",
43339
- "SPX Malaysia"
43342
+ "SPX Malaysia",
43343
+ "SPX Express (MY)"
43340
43344
  ],
43341
43345
  "countries": [
43342
43346
  "MY"
@@ -4017,7 +4017,11 @@ var lt = {
4017
4017
  "korea-post": {
4018
4018
  displayName: "Korea Post",
4019
4019
  color: "#da291c",
4020
- aliases: ["South Korea Post"],
4020
+ aliases: [
4021
+ "South Korea Post",
4022
+ "ePOST (인터넷우체국)",
4023
+ "ePOST (인터넷우체국)(Domestic)"
4024
+ ],
4021
4025
  countries: ["KR"],
4022
4026
  selectable: !0,
4023
4027
  timezone: "UTC",
@@ -33122,7 +33126,8 @@ var lt = {
33122
33126
  "Shopee Express Brasil",
33123
33127
  "Shopee Xpress Brasil",
33124
33128
  "Shopee Express Brazil",
33125
- "SPX Brasil"
33129
+ "SPX Brasil",
33130
+ "SPX Express (BR)"
33126
33131
  ],
33127
33132
  countries: ["BR"],
33128
33133
  selectable: !0,
@@ -33192,7 +33197,8 @@ var lt = {
33192
33197
  aliases: [
33193
33198
  "Shopee Express Malaysia",
33194
33199
  "Shopee Xpress Malaysia",
33195
- "SPX Malaysia"
33200
+ "SPX Malaysia",
33201
+ "SPX Express (MY)"
33196
33202
  ],
33197
33203
  countries: ["MY"],
33198
33204
  selectable: !0,
@@ -4,7 +4,7 @@ import { isValidS10TrackingNumber } from '../../core/detection/index.js';
4
4
  import { countryTimeZone, explicitOffsetTime, isoTime } from '../../core/time/index.js';
5
5
  import { clean, decodeText, escapeRegExp, fetchBounded, parseJsonBytes, } from '../../core/transport/index.js';
6
6
  import { isRecord } from '../../core/types.js';
7
- import { agencyIntake, cttSiteZone, isCttExpressPlace, portugalZone } from './clock.js';
7
+ import { agencyIntake, cttSiteZone, isCttExpressPlace, officeCountry, portugalZone } from './clock.js';
8
8
  import { classifyCttStatus } from './status.js';
9
9
  // Protocol provenance:
10
10
  // - Prior art (full session mechanics + vocabulary, verified step by step
@@ -86,6 +86,9 @@ function sessionFromSetCookie(response) {
86
86
  const DESTINATION_EVENT = /^(?:EM[D-I]|ED[A-H])$/;
87
87
  /** UPU events an origin post records: from posting to dispatch from its office of exchange. */
88
88
  const ORIGIN_EVENT = /^(?:EM[A-C]|EX[A-DX])$/;
89
+ /** Stages a scan from the return's arrival back at the origin takes as part of the trip back. */
90
+ const RETURN_TRIP = new Set(['accepted', 'in_transit', 'out_for_delivery', 'ready_for_pickup', 'delivered']);
91
+ const RETURNED = { status: 'exception', stage: 'returned' };
89
92
  const WALL_CLOCK = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.\d{1,3})?(?:Z|\+00:?00)?$/i;
90
93
  /** A scan's wall clock read as UTC, only to compare it with another's. */
91
94
  function wallMillis(value) {
@@ -96,20 +99,22 @@ function wallMillis(value) {
96
99
  * A scan's clock. CTT's Z is no offset: the digits are the wall clock where
97
100
  * the scan happened (see clock.ts). That is the partner post's country
98
101
  * (`partner`) for its scans: the destination's on a parcel bound abroad, the
99
- * origin's on mail from abroad. A location naming an office or a site of
100
- * CTT's overrides it; Portugal's clock applies otherwise. In a country with
102
+ * origin's on mail from abroad. A location naming an office of exchange in
103
+ * Portugal or one of the parcel's `countries`, or a site of CTT's, overrides
104
+ * it: a parcel returned to its origin arrives at the origin's office. Portugal's
105
+ * clock applies otherwise. In a country with
101
106
  * several zones the scan keeps CTT's Z, the last guess: hours off, but the
102
107
  * reply keeps its instant. Only a wall clock sent with no Z stays a wall time
103
108
  * there. Any other offset is taken as sent.
104
109
  */
105
- function scanClock(value, location, partner, spanish) {
110
+ function scanClock(value, location, partner, countries, spanish) {
106
111
  const raw = typeof value === 'string' ? value.trim() : '';
107
112
  const wall = WALL_CLOCK.exec(raw)?.[1];
108
113
  if (!wall) {
109
114
  const time = explicitOffsetTime(raw);
110
115
  return time ? { time: time.iso, timestamp: time.timestamp } : null;
111
116
  }
112
- const zone = cttSiteZone(location, partner ? [partner] : []) ?? (spanish ? 'Europe/Madrid' : null)
117
+ const zone = cttSiteZone(location, countries) ?? (spanish ? 'Europe/Madrid' : null)
113
118
  ?? (partner ? countryTimeZone(partner) : portugalZone(location));
114
119
  const placed = zone ? isoTime(wall, zone) : null;
115
120
  if (placed)
@@ -161,7 +166,7 @@ export function parseCttTrackingResponse(payload, trackingNumber) {
161
166
  const partner = abroad && DESTINATION_EVENT.test(code) ? abroad : origin && ORIGIN_EVENT.test(code) ? origin : null;
162
167
  const at = wallMillis(rawEvent.DateTime);
163
168
  const spanish = !location && agencyIntake(at, agencyScans);
164
- const clock = scanClock(rawEvent.DateTime, location, partner, spanish);
169
+ const clock = scanClock(rawEvent.DateTime, location, partner, [abroad, origin], spanish);
165
170
  if (!clock || !description)
166
171
  return;
167
172
  const identity = `${clock.time ?? clock.local_time}\u0000${description}\u0000${Number.isSafeInteger(stateId) ? stateId : ''}`;
@@ -187,9 +192,21 @@ export function parseCttTrackingResponse(payload, trackingNumber) {
187
192
  classified: classified ?? undefined,
188
193
  timestamp: clock.timestamp,
189
194
  index,
195
+ // Mail from abroad arriving at an office of exchange of the country it
196
+ // came from is a return back at its origin.
197
+ back: Boolean(origin && !abroad && DESTINATION_EVENT.test(code) && officeCountry(location) === origin),
190
198
  });
191
199
  });
192
200
  parsed.sort((left, right) => right.timestamp - left.timestamp || left.index - right.index);
201
+ // From that arrival on, the scans are the trip back to the sender: a movement
202
+ // or a delivery among them is the return, not a delivery to the recipient.
203
+ const arrivedBack = parsed.map((item) => item.back).lastIndexOf(true);
204
+ for (const item of parsed.slice(0, arrivedBack + 1)) {
205
+ const trip = RETURN_TRIP.has(item.classified?.stage ?? '');
206
+ if (trip)
207
+ item.classified = RETURNED;
208
+ item.event = { ...item.event, ...(trip ? { stage: 'returned' } : {}), provider_leg: 'return' };
209
+ }
193
210
  const events = parsed.slice(0, MAX_EVENTS_TO_RETURN).map(({ event }) => event);
194
211
  const latest = parsed.find((item) => item.classified);
195
212
  if (!latest) {
@@ -17,6 +17,8 @@ export declare function isCttExpressPlace(location: string): boolean;
17
17
  * read as UTC, only to compare them. It is on Madrid's clock, as the agency's scan is.
18
18
  */
19
19
  export declare function agencyIntake(at: number, agencyScans: readonly number[]): boolean;
20
+ /** The country of the office of exchange leading a location, if one does. */
21
+ export declare function officeCountry(location: string): string | null;
20
22
  /**
21
23
  * The clock a CTT scan's location itself names: the country of the office of
22
24
  * exchange leading it, when that is Portugal or one of `countries` (the
@@ -88,6 +88,10 @@ const AGENCY_INTAKE_MS = 60_000;
88
88
  export function agencyIntake(at, agencyScans) {
89
89
  return agencyScans.some((agency) => agency - at >= 0 && agency - at <= AGENCY_INTAKE_MS);
90
90
  }
91
+ /** The country of the office of exchange leading a location, if one does. */
92
+ export function officeCountry(location) {
93
+ return OFFICE.exec(location)?.[1] ?? null;
94
+ }
91
95
  /**
92
96
  * The clock a CTT scan's location itself names: the country of the office of
93
97
  * exchange leading it, when that is Portugal or one of `countries` (the
@@ -97,7 +101,7 @@ export function agencyIntake(at, agencyScans) {
97
101
  * the same clocks.
98
102
  */
99
103
  export function cttSiteZone(location, countries) {
100
- const office = OFFICE.exec(location)?.[1];
104
+ const office = officeCountry(location);
101
105
  if (office === 'PT' || (office && countries.includes(office))) {
102
106
  return office === 'PT' ? portugalZone(location) : countryTimeZone(office);
103
107
  }
@@ -2,7 +2,9 @@
2
2
  "id": "korea-post",
3
3
  "displayName": "Korea Post",
4
4
  "aliases": [
5
- "South Korea Post"
5
+ "South Korea Post",
6
+ "ePOST (인터넷우체국)",
7
+ "ePOST (인터넷우체국)(Domestic)"
6
8
  ],
7
9
  "region": {
8
10
  "countries": [
@@ -5,7 +5,8 @@
5
5
  "Shopee Express Brasil",
6
6
  "Shopee Xpress Brasil",
7
7
  "Shopee Express Brazil",
8
- "SPX Brasil"
8
+ "SPX Brasil",
9
+ "SPX Express (BR)"
9
10
  ],
10
11
  "region": {
11
12
  "countries": [
@@ -4,7 +4,8 @@
4
4
  "aliases": [
5
5
  "Shopee Express Malaysia",
6
6
  "Shopee Xpress Malaysia",
7
- "SPX Malaysia"
7
+ "SPX Malaysia",
8
+ "SPX Express (MY)"
8
9
  ],
9
10
  "region": {
10
11
  "countries": [
@@ -4585,7 +4585,9 @@
4585
4585
  "displayName": "Korea Post",
4586
4586
  "color": "#da291c",
4587
4587
  "aliases": [
4588
- "South Korea Post"
4588
+ "South Korea Post",
4589
+ "ePOST (인터넷우체국)",
4590
+ "ePOST (인터넷우체국)(Domestic)"
4589
4591
  ],
4590
4592
  "countries": [
4591
4593
  "KR"
@@ -43242,7 +43244,8 @@
43242
43244
  "Shopee Express Brasil",
43243
43245
  "Shopee Xpress Brasil",
43244
43246
  "Shopee Express Brazil",
43245
- "SPX Brasil"
43247
+ "SPX Brasil",
43248
+ "SPX Express (BR)"
43246
43249
  ],
43247
43250
  "countries": [
43248
43251
  "BR"
@@ -43336,7 +43339,8 @@
43336
43339
  "aliases": [
43337
43340
  "Shopee Express Malaysia",
43338
43341
  "Shopee Xpress Malaysia",
43339
- "SPX Malaysia"
43342
+ "SPX Malaysia",
43343
+ "SPX Express (MY)"
43340
43344
  ],
43341
43345
  "countries": [
43342
43346
  "MY"
@@ -4586,7 +4586,9 @@ export const CARRIER_CATALOG = {
4586
4586
  "displayName": "Korea Post",
4587
4587
  "color": "#da291c",
4588
4588
  "aliases": [
4589
- "South Korea Post"
4589
+ "South Korea Post",
4590
+ "ePOST (인터넷우체국)",
4591
+ "ePOST (인터넷우체국)(Domestic)"
4590
4592
  ],
4591
4593
  "countries": [
4592
4594
  "KR"
@@ -43243,7 +43245,8 @@ export const CARRIER_CATALOG = {
43243
43245
  "Shopee Express Brasil",
43244
43246
  "Shopee Xpress Brasil",
43245
43247
  "Shopee Express Brazil",
43246
- "SPX Brasil"
43248
+ "SPX Brasil",
43249
+ "SPX Express (BR)"
43247
43250
  ],
43248
43251
  "countries": [
43249
43252
  "BR"
@@ -43337,7 +43340,8 @@ export const CARRIER_CATALOG = {
43337
43340
  "aliases": [
43338
43341
  "Shopee Express Malaysia",
43339
43342
  "Shopee Xpress Malaysia",
43340
- "SPX Malaysia"
43343
+ "SPX Malaysia",
43344
+ "SPX Express (MY)"
43341
43345
  ],
43342
43346
  "countries": [
43343
43347
  "MY"
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.1007.0-main.5443"
5
+ "version": "0.1007.2-main.5446"
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.1007.0-main.5443",
3
+ "version": "0.1007.2-main.5446",
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",