universal-parcel-scraper 0.25.2-main.365 → 0.25.3-main.367

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
@@ -461,7 +461,7 @@
461
461
  "confidence": "high"
462
462
  },
463
463
  {
464
- "pattern": "^00340434\\d{12}$",
464
+ "pattern": "^0034043[45]\\d{12}$",
465
465
  "confidence": "high"
466
466
  },
467
467
  {
@@ -155,6 +155,14 @@
155
155
  "dhl"
156
156
  ]
157
157
  },
158
+ {
159
+ "input": "00340435061352161527",
160
+ "carrier": "dhl",
161
+ "confidence": "high",
162
+ "candidates": [
163
+ "dhl"
164
+ ]
165
+ },
158
166
  {
159
167
  "input": "00573132901903503247",
160
168
  "carrier": "unknown",
@@ -2612,6 +2620,27 @@
2612
2620
  "dpd-fr"
2613
2621
  ]
2614
2622
  },
2623
+ {
2624
+ "input": "2522622410",
2625
+ "carrier": "unknown",
2626
+ "confidence": "low",
2627
+ "candidates": [
2628
+ "dhl-express",
2629
+ "dhl",
2630
+ "mondial-relay",
2631
+ "relais-colis",
2632
+ "tipsa",
2633
+ "estafeta"
2634
+ ],
2635
+ "preferred": [
2636
+ "dhl-express"
2637
+ ],
2638
+ "asked": [
2639
+ "dhl-express",
2640
+ "relais-colis",
2641
+ "tipsa"
2642
+ ]
2643
+ },
2615
2644
  {
2616
2645
  "input": "25631818",
2617
2646
  "carrier": "unknown",
@@ -464,7 +464,7 @@ var le = {
464
464
  confidence: "high"
465
465
  },
466
466
  {
467
- pattern: "^00340434\\d{12}$",
467
+ pattern: "^0034043[45]\\d{12}$",
468
468
  confidence: "high"
469
469
  },
470
470
  {
@@ -101,7 +101,7 @@
101
101
  },
102
102
  {
103
103
  "id": "dhl-5",
104
- "pattern": "^00340434\\d{12}$",
104
+ "pattern": "^0034043[45]\\d{12}$",
105
105
  "confidence": "high"
106
106
  },
107
107
  {
@@ -15,6 +15,23 @@
15
15
  },
16
16
  "note": "Publicly reported shipment number; the attribution was not independently verified."
17
17
  },
18
+ {
19
+ "number": "00340435061352161527",
20
+ "role": "shipment",
21
+ "evidence": "public_shipment_report",
22
+ "source": {
23
+ "url": "https://de.trustpilot.com/review/www.dhl.de",
24
+ "date": "2026-10-07"
25
+ },
26
+ "context": {
27
+ "assessment": "confirmed"
28
+ },
29
+ "expect": {
30
+ "carrier": "dhl",
31
+ "confidence": "high"
32
+ },
33
+ "note": "Publicly reported shipment number in the second 0034043 range; DHL's recipient endpoint returned its history."
34
+ },
18
35
  {
19
36
  "number": "8449089972",
20
37
  "role": "shipment",
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The zone of a DHL Express facility, from its location: "CITY - COUNTRY", or
3
- * "CITY - REGION - COUNTRY" in the USA and Canada. Null when the location does
4
- * not settle one: a country with several clocks and no region DHL names, or a
5
- * country it writes in a form this does not know.
2
+ * The zone of a DHL Express facility, from its location: "CITY - COUNTRY", and
3
+ * in the USA and Canada "CITY - REGION - COUNTRY" or "CITY, ST - COUNTRY". Null
4
+ * when the location does not settle one: a country with several clocks and no
5
+ * region DHL names, or a country it writes in a form this does not know.
6
6
  *
7
7
  * A US state or Canadian province with several clocks takes its majority zone.
8
8
  * Spain and Portugal take their mainland's, except for a town or island of the
@@ -16,6 +16,12 @@ const CANADA_PROVINCES = {
16
16
  'NOVA SCOTIA': 'NS', 'NORTHWEST TERRITORIES': 'NT', NUNAVUT: 'NU', ONTARIO: 'ON', 'PRINCE EDWARD ISLAND': 'PE',
17
17
  QUEBEC: 'QC', SASKATCHEWAN: 'SK', YUKON: 'YT',
18
18
  };
19
+ // DHL's own names for countries the English ones do not cover: the forms on its
20
+ // scans, and those of its country list for the countries whose zone is known.
21
+ const COUNTRIES = {
22
+ UK: 'GB', "PEOPLE'S REPUBLIC OF CHINA": 'CN', 'CHINA, PEOPLES REPUBLIC': 'CN', 'CZECH REPUBLIC': 'CZ',
23
+ 'IRELAND, REPUBLIC OF': 'IE', 'KOREA, REPUBLIC OF (SOUTH K.)': 'KR', TURKEY: 'TR',
24
+ };
19
25
  // Island groups an hour behind their mainland, which DHL files under the country.
20
26
  const ISLANDS = {
21
27
  'Europe/Madrid': { group: 'ES-CN', zone: 'Atlantic/Canary' },
@@ -26,10 +32,10 @@ function regionCode(region, names) {
26
32
  return parts.find((part) => /^[A-Z]{2}$/.test(part)) ?? parts.map((part) => names[part]).find(Boolean) ?? '';
27
33
  }
28
34
  /**
29
- * The zone of a DHL Express facility, from its location: "CITY - COUNTRY", or
30
- * "CITY - REGION - COUNTRY" in the USA and Canada. Null when the location does
31
- * not settle one: a country with several clocks and no region DHL names, or a
32
- * country it writes in a form this does not know.
35
+ * The zone of a DHL Express facility, from its location: "CITY - COUNTRY", and
36
+ * in the USA and Canada "CITY - REGION - COUNTRY" or "CITY, ST - COUNTRY". Null
37
+ * when the location does not settle one: a country with several clocks and no
38
+ * region DHL names, or a country it writes in a form this does not know.
33
39
  *
34
40
  * A US state or Canadian province with several clocks takes its majority zone.
35
41
  * Spain and Portugal take their mainland's, except for a town or island of the
@@ -38,15 +44,15 @@ function regionCode(region, names) {
38
44
  export function facilityZone(location) {
39
45
  const parts = location.split(' - ').map((part) => part.trim());
40
46
  const country = parts.length > 1 ? parts.at(-1).toUpperCase() : '';
41
- const region = parts.length > 2 ? parts.at(-2) : '';
47
+ // The region is a part of its own, or the code that ends the city's.
48
+ const region = parts.length > 2 ? parts.at(-2) : /, ([A-Z]{2})$/i.exec(parts[0])?.[1] ?? '';
42
49
  if (country === 'USA')
43
50
  return usStateTimeZone(regionCode(region, US_STATES));
44
51
  if (country === 'CANADA')
45
52
  return canadaProvinceTimeZone(regionCode(region, CANADA_PROVINCES));
46
- if (country === 'UK')
47
- return 'Europe/London';
48
- // DHL writes some countries article last, and some with a comma: "NETHERLANDS, THE".
49
- const zone = countryTimeZone(country.replace(/, THE$/, '').replaceAll(',', ''));
53
+ // DHL writes the article first or last: "THE PEOPLE'S REPUBLIC OF CHINA", "NETHERLANDS, THE".
54
+ const name = country.replace(/^THE /, '').replace(/, THE$/, '');
55
+ const zone = countryTimeZone(COUNTRIES[name] ?? name.replaceAll(',', ''));
50
56
  const islands = zone ? ISLANDS[zone] : undefined;
51
57
  return islands && regionHasTown(islands.group, parts[0]) ? islands.zone : zone;
52
58
  }
@@ -1,6 +1,28 @@
1
1
  {
2
2
  "carrier": "dhl-express",
3
3
  "records": [
4
+ {
5
+ "number": "2522622410",
6
+ "role": "shipment",
7
+ "evidence": "open_source_example",
8
+ "source": {
9
+ "url": "https://github.com/iFoxz17/sc-delay-prediction/blob/b9e2f99fcdc157b4fb5ff3b0903e79f75f6b32cb/code/m4estro/LambdaTS/Carrier/sample.json",
10
+ "date": "2025-08-30"
11
+ },
12
+ "expect": {
13
+ "carrier": "unknown",
14
+ "confidence": "low",
15
+ "candidates": [
16
+ "dhl-express",
17
+ "dhl",
18
+ "mondial-relay",
19
+ "relais-colis",
20
+ "tipsa",
21
+ "estafeta"
22
+ ]
23
+ },
24
+ "note": "A provider's reply for a DHL Express waybill, published as sample data with its scans. DHL has since issued the waybill to another shipment."
25
+ },
4
26
  {
5
27
  "number": "3408372365",
6
28
  "role": "shipment",
@@ -461,7 +461,7 @@
461
461
  "confidence": "high"
462
462
  },
463
463
  {
464
- "pattern": "^00340434\\d{12}$",
464
+ "pattern": "^0034043[45]\\d{12}$",
465
465
  "confidence": "high"
466
466
  },
467
467
  {
@@ -301,7 +301,7 @@ export declare const CARRIER_CATALOG: {
301
301
  readonly pattern: "^JD\\d{18}$";
302
302
  readonly confidence: "high";
303
303
  }, {
304
- readonly pattern: "^00340434\\d{12}$";
304
+ readonly pattern: "^0034043[45]\\d{12}$";
305
305
  readonly confidence: "high";
306
306
  }, {
307
307
  readonly pattern: "^JVGL[0-9]{20}$";
@@ -462,7 +462,7 @@ export const CARRIER_CATALOG = {
462
462
  "confidence": "high"
463
463
  },
464
464
  {
465
- "pattern": "^00340434\\d{12}$",
465
+ "pattern": "^0034043[45]\\d{12}$",
466
466
  "confidence": "high"
467
467
  },
468
468
  {
@@ -10,7 +10,6 @@
10
10
  import { load } from 'cheerio';
11
11
  import { DateTime } from 'luxon';
12
12
  import timers from 'node:timers/promises';
13
- import { facilityZone } from '../../carriers/dhl-express/clock.js';
14
13
  import { carrierTimezone } from '../../core/catalog/index.js';
15
14
  import { brandTimeZones, carrierIdFromName, carrierNameCountryZone } from '../../core/catalog/hints.js';
16
15
  import { CarrierError, carrierErrorKind, ChallengeError, IndeterminateError, InputRequiredError, NoHistoryError, SchemaError, UpstreamHttpError, UpstreamNetworkError } from '../../core/errors/index.js';
@@ -18,6 +17,7 @@ import { runSteps } from '../../core/runner/index.js';
18
17
  import { canadaProvinceTimeZone, countryCode, countryTimeZone, mislabeledLocalTime, mislabeledWallTime, regionHasTown, sharedClockZone, timeZoneCountry, usStateTimeZone, } from '../../core/time/index.js';
19
18
  import { isRecord } from '../../core/types.js';
20
19
  import { capturedBodies, loadCapture } from '../shared/capture.js';
20
+ import { facilityZone, LatestScan, relaysFacilityClock } from '../shared/facilityClock.js';
21
21
  import { universalCarrierHints } from '../shared/hints.js';
22
22
  import { event, isNotice, localEvent, numberOf, result } from '../shared/result.js';
23
23
  import { carrierScan, markReturnLeg } from '../shared/scans.js';
@@ -117,29 +117,6 @@ function regionScanZone(name, location, inNorthAmerica) {
117
117
  const region = typeof name === 'string' && LOCAL_CLOCK_CARRIERS.test(name.trim()) ? regionClock(location) : null;
118
118
  return region && (region.certain || inNorthAmerica) ? region : null;
119
119
  }
120
- /** A DHL Express date with no zone for its facility: a wall time that no label makes an instant. */
121
- function isFacilityClock(payload, state) {
122
- const name = stateCarrierName(payload, state);
123
- return typeof name === 'string' && carrierIdFromName(name) === 'dhl-express';
124
- }
125
- /**
126
- * The newest wall clock of a reply, and whether that scan was left without an
127
- * instant. A reply whose latest scan has none cannot say where the parcel is,
128
- * and an older scan must not stand in for it.
129
- */
130
- class LatestScan {
131
- wall = '';
132
- unplaced = false;
133
- see(date, unplaced) {
134
- const wall = mislabeledWallTime(date) ?? '';
135
- if (wall > this.wall || (wall === this.wall && unplaced))
136
- [this.wall, this.unplaced] = [wall, unplaced];
137
- }
138
- check() {
139
- if (this.unplaced)
140
- throw new IndeterminateError(SOURCE, 'ParcelsApp cannot place the latest DHL Express scan');
141
- }
142
- }
143
120
  function scanZone(payload, state, fallback, number, inNorthAmerica = false) {
144
121
  const name = stateCarrierName(payload, state);
145
122
  const carrier = typeof name === 'string' ? carrierIdFromName(name) : undefined;
@@ -153,10 +130,8 @@ function scanZone(payload, state, fallback, number, inNorthAmerica = false) {
153
130
  if (zone !== 'UTC' && !(typeof name === 'string' && carrierNameCountryZone(name)))
154
131
  return zone;
155
132
  const location = typeof state.location === 'string' ? state.location.trim() : '';
156
- // DHL Express dates are its facility's clock, whatever the label: its mobile
157
- // API gives the same clocks scan for scan, and Ship24 the same clocks with
158
- // the facility's offset (checked 2026-10-06). No other step applies to them.
159
- if (carrier === 'dhl-express')
133
+ // A facility's clock, whatever the label: no other step applies to it.
134
+ if (relaysFacilityClock(name))
160
135
  return facilityZone(location);
161
136
  const place = location.split(',').at(-1);
162
137
  // A state or province the town confirms beats the country its code also
@@ -242,14 +217,14 @@ function parseHistory(payload, trackingNumber, timezone = null) {
242
217
  return wall && text ? `${wall}|${text}` : '';
243
218
  };
244
219
  const localClocks = new Map(entries.filter((entry) => entry.local).map((entry) => [copyKey(entry), entry.zone]).filter(([key]) => key));
245
- const latest = new LatestScan();
220
+ const latest = new LatestScan(SOURCE);
246
221
  for (const entry of entries) {
247
222
  const { raw, name, scan } = entry;
248
223
  const zone = (localClocks.size && !entry.local ? localClocks.get(copyKey(entry)) : undefined) ?? entry.zone;
249
- const facilityClock = !zone && isFacilityClock(payload, raw);
250
- const parsed = localEvent((zone ? mislabeledLocalTime(raw.date, zone)?.iso : facilityClock ? mislabeledWallTime(raw.date) : undefined) ?? raw.date, entry.wording);
224
+ const unplaced = !zone && relaysFacilityClock(name);
225
+ const parsed = localEvent((zone ? mislabeledLocalTime(raw.date, zone)?.iso : unplaced ? mislabeledWallTime(raw.date) : undefined) ?? raw.date, entry.wording);
251
226
  if (parsed)
252
- latest.see(raw.date, facilityClock);
227
+ latest.see(mislabeledWallTime(raw.date) ?? '', unplaced);
253
228
  // Nor has an offset-less date that no zone resolves: a wall time, not an instant.
254
229
  if (parsed && !parsed.time) {
255
230
  undated++;
@@ -320,7 +295,7 @@ export function parseParcelsAppHtml(html, trackingNumber, timezone = null) {
320
295
  const carriers = [];
321
296
  const scans = [];
322
297
  let undated = 0;
323
- const latest = new LatestScan();
298
+ const latest = new LatestScan(SOURCE);
324
299
  nodes.each((_, node) => {
325
300
  const row = $(node);
326
301
  if (row.find('input, select, form').length)
@@ -352,9 +327,9 @@ export function parseParcelsAppHtml(html, trackingNumber, timezone = null) {
352
327
  const zone = scanZone({ carriers }, state, timezone, numberOf(trackingNumber));
353
328
  const scan = carrierScan(name ? carrierIdFromName(name) : undefined, description);
354
329
  // The page prints no location, so no DHL Express scan has a facility to read its clock in.
355
- const facilityClock = !zone && isFacilityClock({ carriers }, state);
356
- latest.see(state.date, facilityClock);
357
- if (facilityClock) {
330
+ const unplaced = !zone && relaysFacilityClock(name);
331
+ latest.see(mislabeledWallTime(state.date) ?? '', unplaced);
332
+ if (unplaced) {
358
333
  undated++;
359
334
  return;
360
335
  }
@@ -1,13 +1,3 @@
1
- /**
2
- * 17TRACK (t.17track.net), a universal aggregator with richer China Post history.
3
- *
4
- * Transport: the browser service loads the public tracking page and captures
5
- * the page's own `restapi` response; unsigned direct probes answer HTTP 200
6
- * with a rejection code instead of history (verified 2026-09-10). The parser
7
- * keeps only history whose shipment number equals the requested one, and turns
8
- * the provider's own rejection codes into typed errors so routing can tell a
9
- * verification wall from an outage.
10
- */
11
1
  import type { AdapterFactory } from '../../core/adapter/index.js';
12
2
  import { ChallengeError, NoHistoryError, TransportError } from '../../core/errors/index.js';
13
3
  import type { CarrierResult } from '../../core/result/index.js';
@@ -1,8 +1,20 @@
1
+ /**
2
+ * 17TRACK (t.17track.net), a universal aggregator with richer China Post history.
3
+ *
4
+ * Transport: the browser service loads the public tracking page and captures
5
+ * the page's own `restapi` response; unsigned direct probes answer HTTP 200
6
+ * with a rejection code instead of history (verified 2026-09-10). The parser
7
+ * keeps only history whose shipment number equals the requested one, and turns
8
+ * the provider's own rejection codes into typed errors so routing can tell a
9
+ * verification wall from an outage.
10
+ */
11
+ import { DateTime } from 'luxon';
1
12
  import { ChallengeError, NoHistoryError, NotFoundError, SchemaError, TransportError } from '../../core/errors/index.js';
2
13
  import { runSteps } from '../../core/runner/index.js';
3
14
  import { isRecord } from '../../core/types.js';
4
15
  import { universalCarrierHints } from '../shared/hints.js';
5
16
  import { capturedBodies, captureFailure, loadCapture } from '../shared/capture.js';
17
+ import { facilityZone, LatestScan, relaysFacilityClock } from '../shared/facilityClock.js';
6
18
  import { numberOf, result, text } from '../shared/result.js';
7
19
  import { seventeenTrackEvent } from './events.js';
8
20
  const SOURCE = '17TRACK';
@@ -57,6 +69,12 @@ function lookupError(code, providerMessage) {
57
69
  ? new SeventeenTrackVerificationError(code, providerMessage)
58
70
  : new SeventeenTrackLookupError('lookup_unavailable', code, providerMessage);
59
71
  }
72
+ /** The offset-less clock of a scan whose offset 17TRACK added itself. */
73
+ function facilityWall(raw) {
74
+ const clock = isRecord(raw.time_raw) && raw.time_raw.timezone === null ? raw.time_raw : null;
75
+ return clock && typeof clock.date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(clock.date)
76
+ && typeof clock.time === 'string' && /^\d{2}:\d{2}:\d{2}$/.test(clock.time) ? `${clock.date}T${clock.time}` : null;
77
+ }
60
78
  export function parse17TrackResponse(payload, trackingNumber) {
61
79
  const number = numberOf(trackingNumber);
62
80
  if (!isRecord(payload) || !isRecord(payload.meta) || !Number.isInteger(payload.meta.code)) {
@@ -91,6 +109,7 @@ export function parse17TrackResponse(payload, trackingNumber) {
91
109
  const events = [];
92
110
  let count = 0;
93
111
  let undated = 0;
112
+ const latest = new LatestScan(SOURCE);
94
113
  for (const provider of tracking.providers) {
95
114
  if (!isRecord(provider) || !Array.isArray(provider.events))
96
115
  throw new SchemaError(SOURCE, '17TRACK returned invalid provider history');
@@ -103,17 +122,29 @@ export function parse17TrackResponse(payload, trackingNumber) {
103
122
  throw new SchemaError(SOURCE, '17TRACK returned invalid events');
104
123
  if (raw.time_utc == null && raw.time_iso == null)
105
124
  undated++;
125
+ // A facility's clock, on which 17TRACK puts one offset for the whole parcel.
126
+ const wall = relaysFacilityClock(name) ? facilityWall(raw) : null;
127
+ const zone = wall ? facilityZone(text(raw.location)) : null;
128
+ const placed = wall && zone ? DateTime.fromISO(wall, { zone }) : null;
106
129
  let parsed;
107
130
  try {
108
- parsed = seventeenTrackEvent(raw, operator);
131
+ parsed = seventeenTrackEvent(placed?.isValid ? { ...raw, time_utc: placed.toUTC().toISO() } : raw, operator);
109
132
  }
110
133
  catch (cause) {
111
134
  throw new SchemaError(SOURCE, '17TRACK returned an invalid tracking event', { cause });
112
135
  }
113
- if (parsed)
136
+ if (!parsed)
137
+ continue;
138
+ const unplaced = wall !== null && !placed?.isValid;
139
+ latest.see(wall ?? String(raw.time_iso ?? raw.time_utc).slice(0, 19), unplaced);
140
+ // With no zone for the facility it is a wall time, counted like a row without a date.
141
+ if (unplaced)
142
+ undated++;
143
+ else
114
144
  events.push(parsed);
115
145
  }
116
146
  }
147
+ latest.check();
117
148
  if (count === 0 && isRecord(shipment.latest_status) && shipment.latest_status.status === 'NotFound') {
118
149
  throw new NotFoundError(SOURCE);
119
150
  }
@@ -0,0 +1,25 @@
1
+ import { facilityZone } from '../../carriers/dhl-express/clock.js';
2
+ import type { UniversalSource } from './result.js';
3
+ /**
4
+ * DHL Express dates a scan with its facility's clock and no offset, and the
5
+ * providers relay that clock under a label of their own: UTC at ParcelsApp, one
6
+ * offset for the whole parcel at 17TRACK. Ship24 relays it with the facility's
7
+ * offset, and DHL's mobile API gives the same clocks scan for scan (checked
8
+ * 2026-10-06). So such a scan is read in its facility's zone, and with no zone
9
+ * for the facility it is a wall time that no label makes an instant.
10
+ */
11
+ export declare function relaysFacilityClock(carrierName: unknown): boolean;
12
+ export { facilityZone };
13
+ /**
14
+ * The newest wall clock of a reply, and whether that scan was left without an
15
+ * instant. A reply whose latest scan has none cannot say where the parcel is,
16
+ * and an older scan must not stand in for it.
17
+ */
18
+ export declare class LatestScan {
19
+ private readonly source;
20
+ private wall;
21
+ private unplaced;
22
+ constructor(source: UniversalSource);
23
+ see(wall: string, unplaced: boolean): void;
24
+ check(): void;
25
+ }
@@ -0,0 +1,36 @@
1
+ import { facilityZone } from '../../carriers/dhl-express/clock.js';
2
+ import { carrierIdFromName } from '../../core/catalog/hints.js';
3
+ import { IndeterminateError } from '../../core/errors/index.js';
4
+ /**
5
+ * DHL Express dates a scan with its facility's clock and no offset, and the
6
+ * providers relay that clock under a label of their own: UTC at ParcelsApp, one
7
+ * offset for the whole parcel at 17TRACK. Ship24 relays it with the facility's
8
+ * offset, and DHL's mobile API gives the same clocks scan for scan (checked
9
+ * 2026-10-06). So such a scan is read in its facility's zone, and with no zone
10
+ * for the facility it is a wall time that no label makes an instant.
11
+ */
12
+ export function relaysFacilityClock(carrierName) {
13
+ return typeof carrierName === 'string' && carrierIdFromName(carrierName) === 'dhl-express';
14
+ }
15
+ export { facilityZone };
16
+ /**
17
+ * The newest wall clock of a reply, and whether that scan was left without an
18
+ * instant. A reply whose latest scan has none cannot say where the parcel is,
19
+ * and an older scan must not stand in for it.
20
+ */
21
+ export class LatestScan {
22
+ source;
23
+ wall = '';
24
+ unplaced = false;
25
+ constructor(source) {
26
+ this.source = source;
27
+ }
28
+ see(wall, unplaced) {
29
+ if (wall > this.wall || (wall === this.wall && unplaced))
30
+ [this.wall, this.unplaced] = [wall, unplaced];
31
+ }
32
+ check() {
33
+ if (this.unplaced)
34
+ throw new IndeterminateError(this.source, `${this.source} cannot place the latest DHL Express scan`);
35
+ }
36
+ }
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.25.2"
5
+ "version": "0.25.3"
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.25.2-main.365",
3
+ "version": "0.25.3-main.367",
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",