universal-parcel-scraper 0.35.0-main.384 → 0.35.1-main.386

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
@@ -2439,13 +2439,13 @@
2439
2439
  "recognitionRank": 24
2440
2440
  },
2441
2441
  "canaryUrl": "https://dinapaqweb.tipsa-dinapaq.com/dinapaqweb/detalle_envio.php",
2442
- "trackingUrlTemplate": "https://www.tip-sa.com/cliente/datos_prestashop.php?id={trackingNumber}",
2442
+ "trackingUrlTemplate": "https://www.tip-sa.com/cliente/datos_env.php?id={trackingNumber}",
2443
2443
  "linkRules": [
2444
2444
  {
2445
2445
  "domains": [
2446
2446
  "tip-sa.com"
2447
2447
  ],
2448
- "path": "^/cliente/datos(?:_prestashop)?\\.php$",
2448
+ "path": "^/cliente/datos(?:_prestashop|_env)?\\.php$",
2449
2449
  "params": [
2450
2450
  "id"
2451
2451
  ]
@@ -1972,10 +1972,10 @@ var ce = {
1972
1972
  recognitionRank: 24
1973
1973
  },
1974
1974
  canaryUrl: "https://dinapaqweb.tipsa-dinapaq.com/dinapaqweb/detalle_envio.php",
1975
- trackingUrlTemplate: "https://www.tip-sa.com/cliente/datos_prestashop.php?id={trackingNumber}",
1975
+ trackingUrlTemplate: "https://www.tip-sa.com/cliente/datos_env.php?id={trackingNumber}",
1976
1976
  linkRules: [{
1977
1977
  domains: ["tip-sa.com"],
1978
- path: "^/cliente/datos(?:_prestashop)?\\.php$",
1978
+ path: "^/cliente/datos(?:_prestashop|_env)?\\.php$",
1979
1979
  params: ["id"]
1980
1980
  }],
1981
1981
  detectionRules: [{
@@ -3,9 +3,10 @@ import { BudgetExceededError, NotFoundError, SchemaError } from '../../core/erro
3
3
  import { runSteps } from '../../core/runner/index.js';
4
4
  import { NOOP_RECORDER } from '../../core/telemetry/index.js';
5
5
  import { decodeText, fetchBounded } from '../../core/transport/index.js';
6
- import { normalizeTipsaNumber, parseTipsaDetail, tipsaDetailUrl } from './parser.js';
7
- // The shop link (www.tip-sa.com/cliente/datos_prestashop.php) redirects here.
8
- const LOOKUP = 'https://aplicaciones.tip-sa.com/cliente/datos_prestashop.php';
6
+ import { normalizeTipsaNumber, parseTipsaDetail, tipsaDetailUrl, tipsaLookupNotFound } from './parser.js';
7
+ // The shop link (www.tip-sa.com/cliente/datos_env.php) redirects here. Unlike
8
+ // datos_prestashop.php, it does not ask for the destination postcode.
9
+ const LOOKUP = 'https://aplicaciones.tip-sa.com/cliente/datos_env.php';
9
10
  const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/154.0.0.0 Safari/537.36';
10
11
  const LOOKUP_MAX_BYTES = 20_000;
11
12
  const DETAIL_MAX_BYTES = 1_000_000;
@@ -21,12 +22,12 @@ export class TipsaTracker {
21
22
  return runSteps({ carrier: 'tipsa', budgetMs, signal: context.signal,
22
23
  recorder: this.options.recorder ?? NOOP_RECORDER }, [{ id: 'direct', run: async ({ signal, remainingMs }) => {
23
24
  const deadline = performance.now() + remainingMs;
24
- const read = async (url, maxBytes, encoding) => {
25
+ const read = async (url, maxBytes) => {
25
26
  signal.throwIfAborted();
26
27
  const left = deadline - performance.now();
27
28
  if (left <= 0)
28
29
  throw new BudgetExceededError('TIPSA', budgetMs);
29
- const { response, bytes } = await fetchBounded(url, { signal, headers: {
30
+ const fetched = await fetchBounded(url, { signal, headers: {
30
31
  'User-Agent': USER_AGENT,
31
32
  Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
32
33
  'Accept-Language': 'es-ES,es;q=0.9,en;q=0.8',
@@ -34,14 +35,20 @@ export class TipsaTracker {
34
35
  allowHttpStatuses: REDIRECTS, fetcher: this.options.fetcher });
35
36
  if (performance.now() >= deadline)
36
37
  throw new BudgetExceededError('TIPSA', budgetMs);
37
- if (REDIRECTS.includes(response.status))
38
- throw new SchemaError('TIPSA', 'TIPSA tracking page moved');
39
- return decodeText(bytes, encoding);
38
+ return fetched;
40
39
  };
41
- const detail = tipsaDetailUrl(await read(`${LOOKUP}?id=${number}`, LOOKUP_MAX_BYTES, 'utf-8'));
42
- if (!detail)
43
- throw new NotFoundError('TIPSA');
44
- return parseTipsaDetail(await read(detail, DETAIL_MAX_BYTES, 'iso-8859-1'), number);
40
+ // A known reference redirects to its shipment page; an unknown one
41
+ // answers a page that refreshes to TIPSA's not-located page.
42
+ const lookup = await read(`${LOOKUP}?id=${number}`, LOOKUP_MAX_BYTES);
43
+ if (!REDIRECTS.includes(lookup.response.status)) {
44
+ if (tipsaLookupNotFound(decodeText(lookup.bytes, 'utf-8')))
45
+ throw new NotFoundError('TIPSA');
46
+ throw new SchemaError('TIPSA', 'TIPSA lookup did not name a shipment page');
47
+ }
48
+ const detail = await read(tipsaDetailUrl(lookup.response.headers.get('location') ?? ''), DETAIL_MAX_BYTES);
49
+ if (REDIRECTS.includes(detail.response.status))
50
+ throw new SchemaError('TIPSA', 'TIPSA tracking page moved');
51
+ return parseTipsaDetail(decodeText(detail.bytes, 'iso-8859-1'), number);
45
52
  } }]);
46
53
  }
47
54
  }
@@ -29,7 +29,7 @@
29
29
  "location"
30
30
  ],
31
31
  "portal": {
32
- "url": "https://www.tip-sa.com/cliente/datos_prestashop.php?id={trackingNumber}",
32
+ "url": "https://www.tip-sa.com/cliente/datos_env.php?id={trackingNumber}",
33
33
  "canaryUrl": "https://dinapaqweb.tipsa-dinapaq.com/dinapaqweb/detalle_envio.php",
34
34
  "shows": [
35
35
  "status",
@@ -70,7 +70,7 @@
70
70
  "domains": [
71
71
  "tip-sa.com"
72
72
  ],
73
- "path": "^/cliente/datos(?:_prestashop)?\\.php$",
73
+ "path": "^/cliente/datos(?:_prestashop|_env)?\\.php$",
74
74
  "params": [
75
75
  "id"
76
76
  ]
@@ -3,12 +3,15 @@ export declare const TIPSA_ZONE = "Europe/Madrid";
3
3
  /** The 22-digit reference: charge agency, origin agency and waybill, six, six and ten digits. */
4
4
  export declare function normalizeTipsaNumber(raw: string): string;
5
5
  /**
6
- * The shipment page the lookup's meta refresh names, or null when TIPSA knows
7
- * no shipment by that reference (it then names the page with empty
8
- * parameters). Only TIPSA's own detail page with a service id and a
9
- * `dd/MM/yy` date is followed.
6
+ * The shipment page a lookup redirects to. Only TIPSA's own detail page with
7
+ * a service id and a `dd/MM/yy` date is followed.
10
8
  */
11
- export declare function tipsaDetailUrl(html: string): string | null;
9
+ export declare function tipsaDetailUrl(location: string): string;
10
+ /**
11
+ * Whether a lookup that did not redirect is TIPSA's answer for a reference it
12
+ * cannot locate: a page that refreshes to its "envío no localizado" page.
13
+ */
14
+ export declare function tipsaLookupNotFound(html: string): boolean;
12
15
  /**
13
16
  * The history table of TIPSA's shipment page. Its clock is Madrid's for every
14
17
  * agency, Portuguese ones included. Recipient, sender, reference and proof of
@@ -19,36 +19,32 @@ export function normalizeTipsaNumber(raw) {
19
19
  throw new InvalidInputError(PROVIDER, 'TIPSA requires the full 22-digit reference');
20
20
  }
21
21
  /**
22
- * The shipment page the lookup's meta refresh names, or null when TIPSA knows
23
- * no shipment by that reference (it then names the page with empty
24
- * parameters). Only TIPSA's own detail page with a service id and a
25
- * `dd/MM/yy` date is followed.
22
+ * The shipment page a lookup redirects to. Only TIPSA's own detail page with
23
+ * a service id and a `dd/MM/yy` date is followed.
26
24
  */
27
- export function tipsaDetailUrl(html) {
28
- const target = /<meta\s+http-equiv=["']refresh["']\s+content=["']\s*0\s*;\s*URL=([^"'\s]{1,512})["']/i.exec(html)?.[1];
29
- if (!target)
30
- throw new SchemaError(PROVIDER, 'TIPSA lookup did not name a shipment page');
25
+ export function tipsaDetailUrl(location) {
31
26
  let url;
32
27
  try {
33
- url = new URL(target.replaceAll('&amp;', '&'));
28
+ url = new URL(location);
34
29
  }
35
30
  catch {
36
31
  throw new SchemaError(PROVIDER, 'TIPSA shipment page address is invalid');
37
32
  }
38
33
  const keys = [...url.searchParams.keys()];
39
34
  if (url.origin !== DETAIL_ORIGIN || url.pathname.toLowerCase() !== DETAIL_PATH || url.username || url.password || url.hash
40
- || keys.length !== 2 || !keys.includes('servicio') || !keys.includes('fecha')) {
41
- throw new SchemaError(PROVIDER, 'TIPSA shipment page address changed');
42
- }
43
- const service = url.searchParams.get('servicio');
44
- const date = url.searchParams.get('fecha');
45
- if (!service && !date)
46
- return null;
47
- if (!SERVICE.test(service) || !/^\d{2}\/\d{2}\/\d{2}$/.test(date)) {
35
+ || keys.length !== 2 || !keys.includes('servicio') || !keys.includes('fecha')
36
+ || !SERVICE.test(url.searchParams.get('servicio')) || !/^\d{2}\/\d{2}\/\d{2}$/.test(url.searchParams.get('fecha'))) {
48
37
  throw new SchemaError(PROVIDER, 'TIPSA shipment page address changed');
49
38
  }
50
39
  return url.toString();
51
40
  }
41
+ /**
42
+ * Whether a lookup that did not redirect is TIPSA's answer for a reference it
43
+ * cannot locate: a page that refreshes to its "envío no localizado" page.
44
+ */
45
+ export function tipsaLookupNotFound(html) {
46
+ return /<meta\s+http-equiv=["']refresh["']\s+content=["']\s*\d+\s*;\s*URL=error_env\.html["']/i.test(html);
47
+ }
52
48
  /**
53
49
  * The history table of TIPSA's shipment page. Its clock is Madrid's for every
54
50
  * agency, Portuguese ones included. Recipient, sender, reference and proof of
@@ -2439,13 +2439,13 @@
2439
2439
  "recognitionRank": 24
2440
2440
  },
2441
2441
  "canaryUrl": "https://dinapaqweb.tipsa-dinapaq.com/dinapaqweb/detalle_envio.php",
2442
- "trackingUrlTemplate": "https://www.tip-sa.com/cliente/datos_prestashop.php?id={trackingNumber}",
2442
+ "trackingUrlTemplate": "https://www.tip-sa.com/cliente/datos_env.php?id={trackingNumber}",
2443
2443
  "linkRules": [
2444
2444
  {
2445
2445
  "domains": [
2446
2446
  "tip-sa.com"
2447
2447
  ],
2448
- "path": "^/cliente/datos(?:_prestashop)?\\.php$",
2448
+ "path": "^/cliente/datos(?:_prestashop|_env)?\\.php$",
2449
2449
  "params": [
2450
2450
  "id"
2451
2451
  ]
@@ -1620,10 +1620,10 @@ export declare const CARRIER_CATALOG: {
1620
1620
  readonly recognitionRank: 24;
1621
1621
  };
1622
1622
  readonly canaryUrl: "https://dinapaqweb.tipsa-dinapaq.com/dinapaqweb/detalle_envio.php";
1623
- readonly trackingUrlTemplate: "https://www.tip-sa.com/cliente/datos_prestashop.php?id={trackingNumber}";
1623
+ readonly trackingUrlTemplate: "https://www.tip-sa.com/cliente/datos_env.php?id={trackingNumber}";
1624
1624
  readonly linkRules: readonly [{
1625
1625
  readonly domains: readonly ["tip-sa.com"];
1626
- readonly path: "^/cliente/datos(?:_prestashop)?\\.php$";
1626
+ readonly path: "^/cliente/datos(?:_prestashop|_env)?\\.php$";
1627
1627
  readonly params: readonly ["id"];
1628
1628
  }];
1629
1629
  readonly detectionRules: readonly [{
@@ -2440,13 +2440,13 @@ export const CARRIER_CATALOG = {
2440
2440
  "recognitionRank": 24
2441
2441
  },
2442
2442
  "canaryUrl": "https://dinapaqweb.tipsa-dinapaq.com/dinapaqweb/detalle_envio.php",
2443
- "trackingUrlTemplate": "https://www.tip-sa.com/cliente/datos_prestashop.php?id={trackingNumber}",
2443
+ "trackingUrlTemplate": "https://www.tip-sa.com/cliente/datos_env.php?id={trackingNumber}",
2444
2444
  "linkRules": [
2445
2445
  {
2446
2446
  "domains": [
2447
2447
  "tip-sa.com"
2448
2448
  ],
2449
- "path": "^/cliente/datos(?:_prestashop)?\\.php$",
2449
+ "path": "^/cliente/datos(?:_prestashop|_env)?\\.php$",
2450
2450
  "params": [
2451
2451
  "id"
2452
2452
  ]
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.35.0"
5
+ "version": "0.35.1"
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.35.0-main.384",
3
+ "version": "0.35.1-main.386",
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",