universal-parcel-scraper 0.76.0-main.932 → 0.76.0-main.934

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.
@@ -2278,6 +2278,14 @@
2278
2278
  "brt"
2279
2279
  ]
2280
2280
  },
2281
+ {
2282
+ "input": "01613j353188",
2283
+ "carrier": "mrw",
2284
+ "confidence": "high",
2285
+ "candidates": [
2286
+ "mrw"
2287
+ ]
2288
+ },
2281
2289
  {
2282
2290
  "input": "0162534770",
2283
2291
  "carrier": "unknown",
@@ -2620,6 +2628,30 @@
2620
2628
  "mrw"
2621
2629
  ]
2622
2630
  },
2631
+ {
2632
+ "input": "02680J332288",
2633
+ "carrier": "mrw",
2634
+ "confidence": "high",
2635
+ "candidates": [
2636
+ "mrw"
2637
+ ]
2638
+ },
2639
+ {
2640
+ "input": "02680j313623",
2641
+ "carrier": "mrw",
2642
+ "confidence": "high",
2643
+ "candidates": [
2644
+ "mrw"
2645
+ ]
2646
+ },
2647
+ {
2648
+ "input": "02692I374753",
2649
+ "carrier": "mrw",
2650
+ "confidence": "high",
2651
+ "candidates": [
2652
+ "mrw"
2653
+ ]
2654
+ },
2623
2655
  {
2624
2656
  "input": "02692M027981",
2625
2657
  "carrier": "mrw",
@@ -2644,6 +2676,30 @@
2644
2676
  "mrw"
2645
2677
  ]
2646
2678
  },
2679
+ {
2680
+ "input": "02692S954563",
2681
+ "carrier": "mrw",
2682
+ "confidence": "high",
2683
+ "candidates": [
2684
+ "mrw"
2685
+ ]
2686
+ },
2687
+ {
2688
+ "input": "02692T001203",
2689
+ "carrier": "mrw",
2690
+ "confidence": "high",
2691
+ "candidates": [
2692
+ "mrw"
2693
+ ]
2694
+ },
2695
+ {
2696
+ "input": "02695F393326",
2697
+ "carrier": "mrw",
2698
+ "confidence": "high",
2699
+ "candidates": [
2700
+ "mrw"
2701
+ ]
2702
+ },
2647
2703
  {
2648
2704
  "input": "02720F088602",
2649
2705
  "carrier": "mrw",
@@ -3025,6 +3081,14 @@
3025
3081
  "mrw"
3026
3082
  ]
3027
3083
  },
3084
+ {
3085
+ "input": "04316F631075",
3086
+ "carrier": "mrw",
3087
+ "confidence": "high",
3088
+ "candidates": [
3089
+ "mrw"
3090
+ ]
3091
+ },
3028
3092
  {
3029
3093
  "input": "04319F171575",
3030
3094
  "carrier": "mrw",
@@ -3707,6 +3771,14 @@
3707
3771
  "mrw"
3708
3772
  ]
3709
3773
  },
3774
+ {
3775
+ "input": "08080f060880",
3776
+ "carrier": "mrw",
3777
+ "confidence": "high",
3778
+ "candidates": [
3779
+ "mrw"
3780
+ ]
3781
+ },
3710
3782
  {
3711
3783
  "input": "081730000038942441",
3712
3784
  "carrier": "unknown",
@@ -3809,6 +3881,14 @@
3809
3881
  "nacex"
3810
3882
  ]
3811
3883
  },
3884
+ {
3885
+ "input": "08420F316215",
3886
+ "carrier": "mrw",
3887
+ "confidence": "high",
3888
+ "candidates": [
3889
+ "mrw"
3890
+ ]
3891
+ },
3812
3892
  {
3813
3893
  "input": "08448877339657",
3814
3894
  "carrier": "unknown",
@@ -132,10 +132,10 @@ export declare function sharedDpdAppService(options?: Pick<AdapterEnvironment, '
132
132
  * background, and keeps one open for the life of the process, so that no lookup waits the tens of
133
133
  * seconds an opening takes. For a long-lived host, with the `fetcher` and `userAgent` it gives
134
134
  * `createTracker` or its adapter environment. With a `store`, a restart takes the saved session up
135
- * again, and the sessions no longer used are checked every hour until the service refuses them,
136
- * which the store records. `onSession` hears of each session taken up and each opening that ends,
137
- * so the host can tell how long openings take and when one fails. The timers do not keep the
138
- * process alive.
135
+ * again, and the sessions no longer used are checked every hour, also across restarts, until the
136
+ * service refuses them, which the store records. `onSession` hears of each session taken up and
137
+ * each opening that ends, so the host can tell how long openings take and when one fails. The
138
+ * timers do not keep the process alive.
139
139
  */
140
140
  export declare function warmDpdSession(environment?: Pick<AdapterEnvironment, 'fetcher' | 'userAgent'>, options?: {
141
141
  store?: DpdSessionStore;
@@ -16,9 +16,9 @@ const MAX_BYTES = 2_000_000;
16
16
  /** Opening a session has taken up to 75 seconds. */
17
17
  const SESSION_OPEN_MS = 120_000;
18
18
  /**
19
- * The service has accepted a session for over eighteen hours, used once an hour after its first
20
- * eight, and one left idle for four. From eight hours old the next one opens beside it, and
21
- * replaces it once open.
19
+ * The service has accepted a session for nearly twenty-seven hours, used once an hour after its
20
+ * first eight, and one left idle for four, and refused another from twenty-seven hours old. From
21
+ * eight hours old the next one opens beside it, and replaces it once open.
22
22
  */
23
23
  const SESSION_RENEW_MS = 8 * 3_600_000;
24
24
  /** A kept session that failed to open is tried again after this long. */
@@ -26,8 +26,9 @@ const SESSION_RETRY_MS = 15 * 60_000;
26
26
  /** A host's store has this long to give back its sessions before the service opens one. */
27
27
  const STORE_LOAD_MS = 5_000;
28
28
  /**
29
- * With a store, a session lookups no longer use is checked this often, until the service refuses
30
- * it twice in a row: while slow, it can refuse a session it accepts again later.
29
+ * With a store, a session lookups no longer use is checked once it was last asked about this
30
+ * long ago, counting a check saved before a restart, until the service refuses it twice in a row:
31
+ * while slow, it can refuse a session it accepts again later.
31
32
  */
32
33
  const CHECK_EVERY_MS = 3_600_000;
33
34
  /** And for at most this long after it opened. */
@@ -224,7 +225,7 @@ export class DpdAppService {
224
225
  else if (newest)
225
226
  older.unshift(newest);
226
227
  for (const session of older)
227
- this.#retired.set(session.token, { session: { ...session } });
228
+ this.#retired.set(session.token, { session: { ...session }, askedAt: session.checkedAt ?? session.openedAt });
228
229
  }
229
230
  /** Saves a session's state in the host's store, if there is one, without waiting for it. */
230
231
  #save(session) {
@@ -234,18 +235,22 @@ export class DpdAppService {
234
235
  }
235
236
  /** Follows a session lookups no longer use, when a store keeps what its checks find. */
236
237
  #retire(session, refusedAt) {
237
- if (this.#store && !this.#retired.has(session.token))
238
- this.#retired.set(session.token, { session: { ...session }, refusedSince: refusedAt });
238
+ if (this.#store && !this.#retired.has(session.token)) {
239
+ this.#retired.set(session.token, { session: { ...session }, askedAt: this.#now(), refusedSince: refusedAt });
240
+ }
239
241
  }
240
- /** Checks the sessions lookups no longer use, every hour. */
242
+ /** Checks the sessions lookups no longer use when the first of them is due. */
241
243
  #follow() {
242
- const timer = setTimeout(() => void this.#check().finally(() => this.#follow()), CHECK_EVERY_MS);
244
+ let due = this.#now() + CHECK_EVERY_MS;
245
+ for (const retired of this.#retired.values())
246
+ due = Math.min(due, retired.askedAt + CHECK_EVERY_MS);
247
+ const timer = setTimeout(() => void this.#check().finally(() => this.#follow()), Math.max(0, due - this.#now()));
243
248
  timer.unref?.();
244
249
  }
245
250
  /**
246
- * Asks the service once for each followed session whether it still accepts it. A refusal
247
- * confirmed by the next check is saved as the time it began; a reply that is not about the
248
- * session says nothing.
251
+ * Asks the service once for each followed session that is due whether it still accepts it. A
252
+ * refusal confirmed by the next check is saved as the time it began; a reply that is not about
253
+ * the session says nothing.
249
254
  */
250
255
  async #check() {
251
256
  let first = true;
@@ -255,9 +260,12 @@ export class DpdAppService {
255
260
  this.#retired.delete(token);
256
261
  continue;
257
262
  }
263
+ if (now - retired.askedAt < CHECK_EVERY_MS)
264
+ continue;
258
265
  if (!first)
259
266
  await new Promise((resolve) => { setTimeout(resolve, CHECK_GAP_MS).unref?.(); });
260
267
  first = false;
268
+ retired.askedAt = this.#now();
261
269
  let accepted;
262
270
  try {
263
271
  await this.call('getParcelShopByID', { SessionToken: token, ParcelShopID: '0', PudoID: CHECK_PUDO_ID, ParcelShopOnly: 'false' }, AbortSignal.timeout(CHECK_TIMEOUT_MS), CHECK_TIMEOUT_MS);
@@ -439,10 +447,10 @@ export function sharedDpdAppService(options = {}) {
439
447
  * background, and keeps one open for the life of the process, so that no lookup waits the tens of
440
448
  * seconds an opening takes. For a long-lived host, with the `fetcher` and `userAgent` it gives
441
449
  * `createTracker` or its adapter environment. With a `store`, a restart takes the saved session up
442
- * again, and the sessions no longer used are checked every hour until the service refuses them,
443
- * which the store records. `onSession` hears of each session taken up and each opening that ends,
444
- * so the host can tell how long openings take and when one fails. The timers do not keep the
445
- * process alive.
450
+ * again, and the sessions no longer used are checked every hour, also across restarts, until the
451
+ * service refuses them, which the store records. `onSession` hears of each session taken up and
452
+ * each opening that ends, so the host can tell how long openings take and when one fails. The
453
+ * timers do not keep the process alive.
446
454
  */
447
455
  export function warmDpdSession(environment = {}, options = {}) {
448
456
  sharedDpdAppService(environment).keep(options);
@@ -385,6 +385,205 @@
385
385
  "confidence": "high"
386
386
  },
387
387
  "note": "Archived lookup on the carrier's tracking link, which showed it delivered; the tracker no longer answers for it."
388
+ },
389
+ {
390
+ "number": "02695F393326",
391
+ "role": "shipment",
392
+ "evidence": "public_shipment_report",
393
+ "source": {
394
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-atraso-na-entrega-e-falta-de-informacao-sobre-encomenda-163605126",
395
+ "access": "full_page",
396
+ "date": "2026-10-02",
397
+ "dateKind": "publication"
398
+ },
399
+ "context": {
400
+ "assessment": "confirmed",
401
+ "reportedRole": "reported_shipment"
402
+ },
403
+ "expect": {
404
+ "carrier": "mrw",
405
+ "confidence": "high"
406
+ },
407
+ "note": "Portal da Queixa complaint naming MRW. MRW's tracker on www.mrw.pt returned its history in Portuguese, from a Spanish platform to a Portuguese delivery office; www.mrw.es returned the same rows in Spanish."
408
+ },
409
+ {
410
+ "number": "02680j313623",
411
+ "role": "shipment",
412
+ "evidence": "public_shipment_report",
413
+ "source": {
414
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-atraso-na-entrega-de-encomenda-e-dificuldade-de-contacto-numero-de-envio-02680j313623-162724226",
415
+ "access": "full_page",
416
+ "date": "2026-09-17",
417
+ "dateKind": "publication"
418
+ },
419
+ "context": {
420
+ "assessment": "confirmed",
421
+ "reportedRole": "reported_shipment"
422
+ },
423
+ "expect": {
424
+ "carrier": "mrw",
425
+ "confidence": "high"
426
+ },
427
+ "note": "Portal da Queixa complaint quoting the number in lower case. MRW's tracker on www.mrw.pt returned its history in Portuguese, ending at a Portuguese delivery office."
428
+ },
429
+ {
430
+ "number": "04316F631075",
431
+ "role": "shipment",
432
+ "evidence": "public_shipment_report",
433
+ "source": {
434
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-atraso-na-entrega-de-encomenda-e-falta-de-informacao-4-161417026",
435
+ "access": "full_page",
436
+ "date": "2026-08-26",
437
+ "dateKind": "comment"
438
+ },
439
+ "context": {
440
+ "assessment": "confirmed",
441
+ "reportedRole": "reported_shipment"
442
+ },
443
+ "expect": {
444
+ "carrier": "mrw",
445
+ "confidence": "high"
446
+ },
447
+ "note": "Quoted in the complainant's follow-up on a Portal da Queixa complaint about MRW. MRW's tracker on www.mrw.pt returned a Portuguese history that ends returned to sender after a failed delivery in Portugal."
448
+ },
449
+ {
450
+ "number": "01613j353188",
451
+ "role": "shipment",
452
+ "evidence": "public_shipment_report",
453
+ "source": {
454
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-atraso-na-entrega-ou-sem-paradeiro-161254526",
455
+ "access": "full_page",
456
+ "date": "2026-08-26",
457
+ "dateKind": "comment"
458
+ },
459
+ "context": {
460
+ "assessment": "confirmed",
461
+ "reportedRole": "reported_shipment"
462
+ },
463
+ "expect": {
464
+ "carrier": "mrw",
465
+ "confidence": "high"
466
+ },
467
+ "note": "Quoted in lower case in the complainant's follow-up on a Portal da Queixa complaint about MRW. MRW's tracker on www.mrw.pt returned its history in Portuguese, from a merchant's origin office to a Portuguese delivery office."
468
+ },
469
+ {
470
+ "number": "02680J332288",
471
+ "role": "shipment",
472
+ "evidence": "public_shipment_report",
473
+ "source": {
474
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-atraso-prolongado-na-entrega-de-encomenda-e-falta-de-atualizacao-do-estado-162929826",
475
+ "access": "full_page",
476
+ "date": "2026-09-21",
477
+ "dateKind": "publication"
478
+ },
479
+ "context": {
480
+ "assessment": "confirmed",
481
+ "reportedRole": "reported_shipment"
482
+ },
483
+ "expect": {
484
+ "carrier": "mrw",
485
+ "confidence": "high"
486
+ },
487
+ "note": "Portal da Queixa complaint naming MRW. MRW's tracker on www.mrw.pt returned its history in Portuguese, including a PointCorner deposit in Portugal."
488
+ },
489
+ {
490
+ "number": "08420F316215",
491
+ "role": "shipment",
492
+ "evidence": "public_shipment_report",
493
+ "source": {
494
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-entrega-de-encomenda-atrasada-e-falta-de-esclarecimentos-163001326",
495
+ "access": "full_page",
496
+ "date": "2026-09-22",
497
+ "dateKind": "publication"
498
+ },
499
+ "context": {
500
+ "assessment": "confirmed",
501
+ "reportedRole": "reported_shipment"
502
+ },
503
+ "expect": {
504
+ "carrier": "mrw",
505
+ "confidence": "high"
506
+ },
507
+ "note": "Portal da Queixa complaint naming MRW; the number starts with a Portuguese office code. MRW's tracker on www.mrw.pt returned its history in Portuguese, including an incomplete-address incident."
508
+ },
509
+ {
510
+ "number": "08080f060880",
511
+ "role": "shipment",
512
+ "evidence": "public_shipment_report",
513
+ "source": {
514
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-falta-de-aviso-previo-na-entrega-encomenda-mrw-08080f060880-160703126",
515
+ "access": "full_page",
516
+ "date": "2026-08-12",
517
+ "dateKind": "publication"
518
+ },
519
+ "context": {
520
+ "assessment": "confirmed",
521
+ "reportedRole": "reported_shipment"
522
+ },
523
+ "expect": {
524
+ "carrier": "mrw",
525
+ "confidence": "high"
526
+ },
527
+ "note": "Portal da Queixa complaint quoting the number in lower case; it starts with a Portuguese office code. MRW's tracker on www.mrw.pt returned a domestic Portuguese history in Portuguese, with a breakdown of each parcel's scans under some rows."
528
+ },
529
+ {
530
+ "number": "02692T001203",
531
+ "role": "shipment",
532
+ "evidence": "public_shipment_report",
533
+ "source": {
534
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-erro-na-morada-associada-a-entrega-161817826",
535
+ "access": "full_page",
536
+ "date": "2026-09-01",
537
+ "dateKind": "publication"
538
+ },
539
+ "context": {
540
+ "assessment": "confirmed",
541
+ "reportedRole": "reported_shipment"
542
+ },
543
+ "expect": {
544
+ "carrier": "mrw",
545
+ "confidence": "high"
546
+ },
547
+ "note": "Portal da Queixa complaint naming MRW; T series. MRW's tracker on www.mrw.pt returned a Portuguese history that ends returned to sender after an incomplete-address incident."
548
+ },
549
+ {
550
+ "number": "02692S954563",
551
+ "role": "shipment",
552
+ "evidence": "public_shipment_report",
553
+ "source": {
554
+ "url": "https://portaldaqueixa.com/brands/mrw/complaints/mrw-registo-indevido-de-ausencia-na-entrega-de-encomenda-161100726",
555
+ "access": "full_page",
556
+ "date": "2026-08-19",
557
+ "dateKind": "publication"
558
+ },
559
+ "context": {
560
+ "assessment": "confirmed",
561
+ "reportedRole": "reported_shipment"
562
+ },
563
+ "expect": {
564
+ "carrier": "mrw",
565
+ "confidence": "high"
566
+ },
567
+ "note": "Portal da Queixa complaint naming MRW. MRW's tracker on www.mrw.pt returned its history in Portuguese, from a Spanish origin office to a Portuguese delivery office."
568
+ },
569
+ {
570
+ "number": "02692I374753",
571
+ "role": "shipment",
572
+ "evidence": "public_shipment_report",
573
+ "source": {
574
+ "url": "https://web.archive.org/web/20260325153626/https://www.mrw.pt/seguimiento/track.asp?envio=02692I374753",
575
+ "access": "full_page",
576
+ "date": "2026-03-25",
577
+ "dateKind": "capture"
578
+ },
579
+ "context": {
580
+ "assessment": "confirmed"
581
+ },
582
+ "expect": {
583
+ "carrier": "mrw",
584
+ "confidence": "high"
585
+ },
586
+ "note": "Archived lookup on MRW Portugal's tracking link; MRW's tracker on www.mrw.pt returned its current status in Portuguese without a history."
388
587
  }
389
588
  ]
390
589
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-parcel-scraper",
3
- "version": "0.76.0-main.932",
3
+ "version": "0.76.0-main.934",
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",