universal-parcel-scraper 0.49.2-main.551 → 0.49.2-main.552

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/README.md CHANGED
@@ -149,7 +149,7 @@ createTracker({ providers: ['ParcelsApp', 'Ship24', '17TRACK', 'Postal Ninja', '
149
149
  <!-- GENERATED:stages -->
150
150
  <img src="docs/assets/stages.svg" alt="DHL: Die Sendung wurde in das Zustellfahrzeug geladen.; Mondial Relay: En cours de livraison; Correios Brazil: Objeto saiu para entrega ao destinatário; Correos Express: EN REPARTO; Yamato Transport: 配達中; La Poste / Colissimo: DISTOU. All are filed under out_for_delivery." width="760">
151
151
 
152
- The carrier folders record 2,758 such statuses, each filed under one stage.
152
+ The carrier folders record 2,766 such statuses, each filed under one stage.
153
153
  <!-- /GENERATED:stages -->
154
154
 
155
155
  Wording nobody recorded yet goes through a classifier that reads seven European languages.
@@ -15,18 +15,20 @@
15
15
  * does echo the delivery postcode, which must agree with the requested one.
16
16
  *
17
17
  * Privacy: the loader payload carries the retailer, the recipient's name,
18
- * e-mail, phone and address, and per-event `variables` that repeat them.
19
- * `parse()` copies nothing from those objects: each event is rebuilt from its
20
- * timestamp, our own description and the mapped stage, and the only order-level
21
- * fields read are the delivery postcode, compared and never copied, the
22
- * delivery country, which picks the window's clock, and the delivery window.
18
+ * e-mail, phone and address, the PaackGo Point pickup code and QR link, and
19
+ * per-event `variables` that repeat them. `parse()` copies nothing from those:
20
+ * each event is rebuilt from its timestamp, our own description and the mapped
21
+ * stage, and the only order-level fields read are the delivery postcode,
22
+ * compared and never copied, the delivery country, which picks the window's
23
+ * clock, the delivery window, and the PaackGo Point's name and address while
24
+ * the parcel waits there.
23
25
  */
24
26
  import { load } from 'cheerio';
25
27
  import { DateTime } from 'luxon';
26
28
  import { lookupBudget } from '../../core/adapter/index.js';
27
29
  import { IndeterminateError, InputRequiredError, InvalidInputError, NotFoundError, SchemaError } from '../../core/errors/index.js';
28
30
  import { countryCode, countryTimeZone, explicitOffsetTime } from '../../core/time/index.js';
29
- import { decodeText, fetchBounded, UpstreamHttpError, userAgentOf } from '../../core/transport/index.js';
31
+ import { clean, decodeText, fetchBounded, UpstreamHttpError, userAgentOf } from '../../core/transport/index.js';
30
32
  import { isRecord } from '../../core/types.js';
31
33
  import { classifyPaackEvent } from './status.js';
32
34
  const TRACKING_ENDPOINT = 'https://mydeliveries.paack.app/tracking/order';
@@ -196,6 +198,22 @@ function redirectTarget(location, from, trackingNumber, postcode) {
196
198
  return null;
197
199
  return { url: trackingOrderUrl(`${target.origin}${target.pathname}`, trackingNumber, postcode) };
198
200
  }
201
+ /**
202
+ * The PaackGo Point the page names: `pudo_name`, then `pudo_address` on the
203
+ * following lines. The address is one string Paack formats itself; the page
204
+ * prints it as it comes and breaks it only where it holds a line break. The
205
+ * pickup code (`pudo_passcode`), QR link and collection deadline beside them
206
+ * are never read.
207
+ */
208
+ function pickupPoint(order) {
209
+ const name = clean(order.pudo_name, 120);
210
+ if (!name)
211
+ return '';
212
+ const address = typeof order.pudo_address === 'string'
213
+ ? order.pudo_address.split('\n').map((line) => clean(line, 200)).filter(Boolean)
214
+ : [];
215
+ return [name, ...address].join('\n');
216
+ }
199
217
  /** Letters and digits only, so "SW1A 1AA" and "4445-027" compare by content. */
200
218
  function postcodeKey(value) {
201
219
  return value.toLocaleUpperCase('en-US').replace(/[^A-Z0-9]/g, '');
@@ -258,12 +276,14 @@ export function parsePaackTrackingResponse(payload, rawPostcode) {
258
276
  const expected = isRecord(order.expected_delivery_ts) && !['delivered', 'exception'].includes(current.status)
259
277
  ? expectedDelivery(order.expected_delivery_ts, zone, newest)
260
278
  : null;
279
+ const pickup = current.stage === 'ready_for_pickup' ? pickupPoint(order) : '';
261
280
  return {
262
281
  status: current.status,
263
282
  current_stage: current.stage,
264
283
  last_status_text: current.description,
265
284
  last_update: activeTime?.iso ?? events[0]?.time ?? null,
266
285
  expected_delivery: expected,
286
+ ...(pickup ? { pickup_point: pickup } : {}),
267
287
  timezone: zone,
268
288
  events,
269
289
  };
@@ -40,7 +40,8 @@
40
40
  },
41
41
  "capabilities": [
42
42
  "history",
43
- "eta"
43
+ "eta",
44
+ "pickup_point"
44
45
  ],
45
46
  "portal": {
46
47
  "url": "https://mydeliveries.paack.app/tracking?tracking_number={trackingNumber}",
@@ -50,6 +51,8 @@
50
51
  "history",
51
52
  "eta",
52
53
  "eta_window",
54
+ "pickup_point",
55
+ "pickup_code",
53
56
  "sender_name",
54
57
  "recipient_name",
55
58
  "recipient_address"
@@ -58,9 +61,11 @@
58
61
  "status",
59
62
  "history",
60
63
  "eta",
61
- "eta_window"
64
+ "eta_window",
65
+ "pickup_point"
62
66
  ],
63
67
  "discarded": [
68
+ "pickup_code",
64
69
  "sender_name",
65
70
  "recipient_name",
66
71
  "recipient_address"
@@ -10,6 +10,10 @@
10
10
  *
11
11
  * Order matters: return and failure identifiers are tested before the broader
12
12
  * delivery ones, so "notDelivered" can never match the "delivered" substring.
13
+ * PaackGo Point identifiers come from the page's own vocabulary and are tested
14
+ * before the generic words they contain: "rejectedByPudo" is a retried
15
+ * delivery, not a rejection, and "collectedByCustomer" is the recipient
16
+ * collecting the parcel, not a transit collection.
13
17
  * The provider's own wording is never returned; each mapped entry supplies the
14
18
  * English description we display.
15
19
  *
@@ -15,6 +15,7 @@ const OUT_FOR_DELIVERY = {
15
15
  description: 'Out for delivery',
16
16
  };
17
17
  const DELIVERED = { status: 'delivered', stage: 'delivered', description: 'Delivered' };
18
+ const DELIVERY_ISSUE = { status: 'exception', stage: 'failed_attempt', description: 'Delivery issue' };
18
19
  /** Paack's English timeline labels, keyed like `statusKey`, with the identifier each one renders. */
19
20
  const RELAYED = new Map([
20
21
  ['orderdetailsreceived', REGISTERED], // manifested
@@ -36,6 +37,18 @@ export function classifyPaackEvent(value) {
36
37
  ])) {
37
38
  return { status: 'exception', stage: 'returned', description: 'Shipment returned' };
38
39
  }
40
+ // Expired, refused or unpaid at the PaackGo Point: on its way back to the retailer.
41
+ if (includesAny(key, [
42
+ 'inpudotoreturnexpired',
43
+ 'inpudotoreturnrejected',
44
+ 'inpudotoreturncodnotaccepted',
45
+ ])) {
46
+ return { status: 'exception', stage: 'returned', description: 'Returning to sender' };
47
+ }
48
+ // The point was closed, full or refused the parcel; Paack tries again.
49
+ if (includesAny(key, ['pudoclosed', 'pudofull', 'rejectedbypudo'])) {
50
+ return { ...DELIVERY_ISSUE };
51
+ }
39
52
  if (includesAny(key, [
40
53
  'incorrectaddress',
41
54
  'notaccepted',
@@ -59,10 +72,10 @@ export function classifyPaackEvent(value) {
59
72
  'notdelivered',
60
73
  'undelivered',
61
74
  ]))
62
- return { status: 'exception', stage: 'failed_attempt', description: 'Delivery issue' };
63
- if (includesAny(key, ['delivered', 'deliverycompleted']))
75
+ return { ...DELIVERY_ISSUE };
76
+ if (includesAny(key, ['delivered', 'deliverycompleted', 'collectedbycustomer']))
64
77
  return { ...DELIVERED };
65
- if (includesAny(key, ['readyforpickup', 'atpickuppoint'])) {
78
+ if (includesAny(key, ['droppedinpudo'])) {
66
79
  return { status: 'out_for_delivery', stage: 'ready_for_pickup', description: 'Ready for pickup' };
67
80
  }
68
81
  if (includesAny(key, ['outfordelivery', 'indelivery', 'driverassigned', 'inprogress']))
@@ -25,6 +25,54 @@
25
25
  "firstSeen": "2026-09-12",
26
26
  "note": "Identifier substring inherited from the pre-package adapter; source not recorded."
27
27
  },
28
+ {
29
+ "code": "inpudotoreturnexpired",
30
+ "language": "en",
31
+ "stage": "returned",
32
+ "confirmedBy": "bundle",
33
+ "firstSeen": "2026-10-08",
34
+ "note": "Page label inPudoToReturnExpired, \"Expired at PaackGo Point\": not collected in time, returned to the retailer."
35
+ },
36
+ {
37
+ "code": "inpudotoreturnrejected",
38
+ "language": "en",
39
+ "stage": "returned",
40
+ "confirmedBy": "bundle",
41
+ "firstSeen": "2026-10-08",
42
+ "note": "Page label inPudoToReturnRejected: refused at the PaackGo Point, returned to the retailer. Tested before the rejected exception rule."
43
+ },
44
+ {
45
+ "code": "inpudotoreturncodnotaccepted",
46
+ "language": "en",
47
+ "stage": "returned",
48
+ "confirmedBy": "bundle",
49
+ "firstSeen": "2026-10-08",
50
+ "note": "Page label inPudoToReturnCODNotAccepted: cash on delivery not paid at the PaackGo Point, returned to the retailer."
51
+ },
52
+ {
53
+ "code": "pudoclosed",
54
+ "language": "en",
55
+ "stage": "failed_attempt",
56
+ "confirmedBy": "bundle",
57
+ "firstSeen": "2026-10-08",
58
+ "note": "Page label pudoClosed, \"Has been rescheduled\": the PaackGo Point was closed; Paack tries again."
59
+ },
60
+ {
61
+ "code": "pudofull",
62
+ "language": "en",
63
+ "stage": "failed_attempt",
64
+ "confirmedBy": "bundle",
65
+ "firstSeen": "2026-10-08",
66
+ "note": "Page label pudoFull, \"Has been rescheduled\": the PaackGo Point was full; Paack tries again."
67
+ },
68
+ {
69
+ "code": "rejectedbypudo",
70
+ "language": "en",
71
+ "stage": "failed_attempt",
72
+ "confirmedBy": "bundle",
73
+ "firstSeen": "2026-10-08",
74
+ "note": "Page label rejectedByPudo, \"Has been rescheduled\": the PaackGo Point refused the parcel; Paack tries again. Tested before the rejected exception rule."
75
+ },
28
76
  {
29
77
  "code": "returntosenderscheduled",
30
78
  "language": "en",
@@ -145,6 +193,22 @@
145
193
  "firstSeen": "2026-09-12",
146
194
  "note": "Identifier substring inherited from the pre-package adapter; source not recorded. Reclassified to exception 2026-09-12."
147
195
  },
196
+ {
197
+ "code": "inpudotoreturndamaged",
198
+ "language": "en",
199
+ "stage": "exception",
200
+ "confirmedBy": "bundle",
201
+ "firstSeen": "2026-10-08",
202
+ "note": "Page label inPudoToReturnDamaged, \"Logistic issue\": no longer available at the PaackGo Point. Reached through damaged."
203
+ },
204
+ {
205
+ "code": "lostbypudo",
206
+ "language": "en",
207
+ "stage": "exception",
208
+ "confirmedBy": "bundle",
209
+ "firstSeen": "2026-10-08",
210
+ "note": "Page label lostByPudo, \"Logistic issue\". Reached through lost."
211
+ },
148
212
  {
149
213
  "code": "integrationerror",
150
214
  "language": "en",
@@ -186,20 +250,20 @@
186
250
  "note": "Identifier substring inherited from the pre-package adapter; source not recorded."
187
251
  },
188
252
  {
189
- "code": "readyforpickup",
253
+ "code": "collectedbycustomer",
190
254
  "language": "en",
191
- "stage": "ready_for_pickup",
192
- "confirmedBy": "prior-art",
193
- "firstSeen": "2026-09-12",
194
- "note": "Identifier substring inherited from the pre-package adapter; source not recorded."
255
+ "stage": "delivered",
256
+ "confirmedBy": "bundle",
257
+ "firstSeen": "2026-10-08",
258
+ "note": "Page label collectedByCustomer, \"Collected from PaackGo Point\". Tested before the collected transit rule."
195
259
  },
196
260
  {
197
- "code": "atpickuppoint",
261
+ "code": "droppedinpudo",
198
262
  "language": "en",
199
263
  "stage": "ready_for_pickup",
200
- "confirmedBy": "prior-art",
201
- "firstSeen": "2026-09-12",
202
- "note": "Identifier substring inherited from the pre-package adapter; source not recorded."
264
+ "confirmedBy": "bundle",
265
+ "firstSeen": "2026-10-08",
266
+ "note": "Page label droppedInPudo, \"At PaackGo Point\": the parcel waits at the point named by pudo_name and pudo_address."
203
267
  },
204
268
  {
205
269
  "code": "outfordelivery",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-parcel-scraper",
3
- "version": "0.49.2-main.551",
3
+ "version": "0.49.2-main.552",
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",