universal-parcel-scraper 0.776.0-main.4145 → 0.776.2-main.4151
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 +1 -1
- package/dist/carriers/correios-br/status.js +2 -0
- package/dist/carriers/correios-br/statuses.json +1 -0
- package/dist/carriers/dpd-uk/identity.js +9 -3
- package/dist/carriers/dpd-uk/parser.js +1 -0
- package/dist/carriers/dpd-uk/statuses.json +1 -0
- package/dist/carriers/magyar-posta/adapter.d.ts +1 -1
- package/dist/carriers/magyar-posta/adapter.js +23 -7
- package/dist/providers/shared/result.js +6 -0
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -161,7 +161,7 @@ the command line's `track --source ParcelsApp` does the same.
|
|
|
161
161
|
<!-- GENERATED:stages -->
|
|
162
162
|
<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">
|
|
163
163
|
|
|
164
|
-
The carrier folders record 17,
|
|
164
|
+
The carrier folders record 17,129 such statuses, each filed under one stage.
|
|
165
165
|
<!-- /GENERATED:stages -->
|
|
166
166
|
|
|
167
167
|
Wording nobody recorded yet goes through a classifier that reads seven European languages.
|
|
@@ -30,6 +30,8 @@ const codes = {
|
|
|
30
30
|
'PAR/21': { status: 'in_transit', stage: 'customs' },
|
|
31
31
|
// The import waits for the recipient's tax number before customs can clear it.
|
|
32
32
|
'FC/16': { status: 'in_transit', stage: 'customs' },
|
|
33
|
+
// The import waits for customs to settle the product's classification code.
|
|
34
|
+
'FC/40': { status: 'in_transit', stage: 'customs' },
|
|
33
35
|
'PAR/24': { status: 'exception', stage: 'returned' },
|
|
34
36
|
};
|
|
35
37
|
export function classifyCorreiosStatus(code) {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
{ "code": "PAR/07", "wording": "Análise concluída - importação autorizada", "language": "pt", "stage": "in_transit", "confirmedBy": "live" },
|
|
15
15
|
{ "code": "PAR/21", "wording": "Informações enviadas para análise da autoridade aduaneira/órgãos anuentes", "language": "pt", "stage": "customs", "confirmedBy": "live" },
|
|
16
16
|
{ "code": "FC/16", "wording": "Necessidade de informar CPF/CNPJ do destinatário", "language": "pt", "stage": "customs", "confirmedBy": "live", "note": "The import waits for the recipient's tax number (CPF or CNPJ) before customs can clear it." },
|
|
17
|
+
{ "code": "FC/40", "wording": "Objeto em processo de identificação do código de classificação do produto", "language": "pt", "stage": "customs", "confirmedBy": "live", "note": "The import waits for customs to settle the product's classification code, after Correios received the item." },
|
|
17
18
|
{ "code": "BDI/01", "wording": "Objeto entregue ao destinatário", "language": "pt", "stage": "delivered", "confirmedBy": "live" },
|
|
18
19
|
{ "code": "BDE/10", "wording": "Objeto não entregue - cliente mudou-se", "language": "pt", "stage": "failed_attempt", "confirmedBy": "live" },
|
|
19
20
|
{ "code": "BDE/20", "wording": "Objeto não entregue - carteiro não atendido", "language": "pt", "stage": "failed_attempt", "confirmedBy": "live" },
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { dpdUkWording, withoutDriverName } from './wording.js';
|
|
2
|
-
|
|
2
|
+
// DPD UK rewords a depot scan as the parcel moves on: it has arrived at the depot, it is there
|
|
3
|
+
// ready for delivery, then it is at the depot. The three are one scan.
|
|
4
|
+
const DEPOT = /^your parcel (?:has arrived at|is at) our depot(?: ready for delivery)?$/;
|
|
5
|
+
function wording(scan) {
|
|
6
|
+
const text = dpdUkWording(withoutDriverName(scan.description));
|
|
7
|
+
return DEPOT.test(text) ? 'your parcel is at our depot' : text;
|
|
8
|
+
}
|
|
3
9
|
/** One scan, by its wording and place. DPD UK's scans have no code. */
|
|
4
10
|
function sameScan(incoming, stored) {
|
|
5
11
|
return wording(incoming) !== '' && wording(incoming) === wording(stored) && incoming.location.trim() === stored.location.trim();
|
|
6
12
|
}
|
|
7
|
-
// The round's notice drops the hour it gave for the delivery once the round is over,
|
|
8
|
-
// earlier releases kept the driver's name a scan gives. The scan keeps its row
|
|
13
|
+
// The round's notice drops the hour it gave for the delivery once the round is over, a depot
|
|
14
|
+
// scan is reworded, and earlier releases kept the driver's name a scan gives. The scan keeps its row.
|
|
9
15
|
export const sameInstantIdentityPolicy = {
|
|
10
16
|
sourceCarrierId: 'dpd-uk', storedSources: ['dpd-uk'], requireProviderCode: false, matchEachScan: true, matches: sameScan,
|
|
11
17
|
};
|
|
@@ -57,6 +57,7 @@ const VERIFIED_WORDING = {
|
|
|
57
57
|
'the parcel is on the vehicle for delivery': 'out_for_delivery',
|
|
58
58
|
'your parcel is at our depot': 'in_transit',
|
|
59
59
|
'your parcel has arrived at our depot': 'in_transit',
|
|
60
|
+
'your parcel is at our depot ready for delivery': 'in_transit',
|
|
60
61
|
"your parcel has left our sortation facility and is on it's onwards journey": 'in_transit',
|
|
61
62
|
'the parcel has arrived at the delivery depot': 'in_transit',
|
|
62
63
|
'the parcel is in transit to its final destination': 'in_transit',
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
{ "wording": "We have your parcel and it's on its way to you", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
9
9
|
{ "wording": "Your parcel is at our depot", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
10
10
|
{ "wording": "Your parcel has arrived at our depot", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
11
|
+
{ "wording": "Your parcel is at our depot ready for delivery", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-28", "note": "A depot scan as DPD rewords it once the parcel is ready for its round; later it reads \"Your parcel is at our depot\"." },
|
|
11
12
|
{ "wording": "Your parcel has left our sortation facility and is on it's onwards journey", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-08", "note": "On a parcel leaving the UK." },
|
|
12
13
|
{ "wording": "The parcel is in transit to its final destination", "language": "en", "stage": "in_transit", "confirmedBy": "live", "firstSeen": "2026-10-08", "note": "The partner's wording on a parcel delivered abroad, as are the other entries starting with \"The parcel\"." },
|
|
13
14
|
{ "wording": "The parcel is undergoing customs clearance", "language": "en", "stage": "customs", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
@@ -20,7 +20,7 @@ export declare function magyarPostaBrowser(trawl: TrawlClient, raw: string, sign
|
|
|
20
20
|
* the number, bound to the whole number. The reply is the item, as later reads
|
|
21
21
|
* of the id return it.
|
|
22
22
|
*/
|
|
23
|
-
export declare function magyarPostaAppBrowser(trawl: TrawlClient, raw: string, applicationId: string, signal: AbortSignal, remainingMs: number): Promise<CarrierResult>;
|
|
23
|
+
export declare function magyarPostaAppBrowser(trawl: TrawlClient, raw: string, applicationId: string, signal: AbortSignal, remainingMs: number, rememberApplication?: () => void, supportsRegistrationCache?: () => void): Promise<CarrierResult>;
|
|
24
24
|
export declare class MagyarPostaTracker {
|
|
25
25
|
#private;
|
|
26
26
|
private readonly options;
|
|
@@ -82,7 +82,7 @@ export async function magyarPostaBrowser(trawl, raw, signal, remainingMs) {
|
|
|
82
82
|
* the number, bound to the whole number. The reply is the item, as later reads
|
|
83
83
|
* of the id return it.
|
|
84
84
|
*/
|
|
85
|
-
export async function magyarPostaAppBrowser(trawl, raw, applicationId, signal, remainingMs) {
|
|
85
|
+
export async function magyarPostaAppBrowser(trawl, raw, applicationId, signal, remainingMs, rememberApplication, supportsRegistrationCache) {
|
|
86
86
|
const number = normalizeMagyarPostaNumber(raw);
|
|
87
87
|
const timeoutMs = Math.floor(remainingMs - 2000);
|
|
88
88
|
if (timeoutMs < 1)
|
|
@@ -90,6 +90,10 @@ export async function magyarPostaAppBrowser(trawl, raw, applicationId, signal, r
|
|
|
90
90
|
const page = await trawl.scrape({ url: magyarPostaAppTrackingUrl(applicationId, number), skipHttp: true, maxTier: 3,
|
|
91
91
|
captureResponses: [MAGYAR_POSTA_APP_TRACKING], maxTimeout: timeoutMs, settleTimeout: timeoutMs }, { provider: PROVIDER, timeoutMs, signal, requireSolved: false, captureWindow: 'last', maxBytes: 2 * MAGYAR_POSTA_MAX_BYTES });
|
|
92
92
|
signal.throwIfAborted();
|
|
93
|
+
if (page.capturedResponses.some(row => row.headers['x-magyar-posta-registration-cache'] === 'memory'
|
|
94
|
+
&& ((row.url === MAGYAR_POSTA_APP_PAGE && row.error === REFUSED)
|
|
95
|
+
|| (row.url === MAGYAR_POSTA_APP_TRACKING && row.trackingNumberSubmitted === number))))
|
|
96
|
+
supportsRegistrationCache?.();
|
|
93
97
|
if (page.capturedResponses.some(row => row.url === MAGYAR_POSTA_APP_PAGE && row.error === REFUSED)) {
|
|
94
98
|
throw new ChallengeError(PROVIDER, 'Google refused Magyar Posta\'s audio challenge', { reason: 'verification_refused' });
|
|
95
99
|
}
|
|
@@ -100,10 +104,13 @@ export async function magyarPostaAppBrowser(trawl, raw, applicationId, signal, r
|
|
|
100
104
|
// No addition left the widget page: its page failed, or the widget issued no token.
|
|
101
105
|
if (!reply)
|
|
102
106
|
throw httpFailure(page.statusCode) ?? new ChallengeError(PROVIDER, 'Magyar Posta\'s app widget issued no token');
|
|
107
|
+
if (reply.error === 'Magyar Posta returned no bound cached history')
|
|
108
|
+
throw new SchemaError(PROVIDER, 'Magyar Posta returned unbound cached history');
|
|
103
109
|
if (reply.error || reply.truncated || reply.base64Encoded)
|
|
104
110
|
throw new TransportError(PROVIDER, 'Magyar Posta returned an incomplete app reply');
|
|
105
111
|
const payload = reply.body === null ? null : magyarPostaAppJson(reply.body);
|
|
106
|
-
if (reply.
|
|
112
|
+
if (reply.headers['x-magyar-posta-registration'] !== 'retained' && reply.status === 502
|
|
113
|
+
&& typeof payload === 'object' && payload !== null && payload.message === UNADDED) {
|
|
107
114
|
throw new UnaddedError(PROVIDER, 'Magyar Posta\'s app did not add the number');
|
|
108
115
|
}
|
|
109
116
|
const failure = magyarPostaAppFailure(reply.status, reply.headers['retry-after']);
|
|
@@ -112,12 +119,18 @@ export async function magyarPostaAppBrowser(trawl, raw, applicationId, signal, r
|
|
|
112
119
|
const result = parseMagyarPostaApp(payload, number);
|
|
113
120
|
if (!result)
|
|
114
121
|
throw new SchemaError(PROVIDER, 'Magyar Posta\'s app added the number without returning it');
|
|
122
|
+
// A retained registration belongs to the helper, not to this new app id.
|
|
123
|
+
// Its capability stays in the helper's memory rather than entering results.
|
|
124
|
+
if (reply.headers['x-magyar-posta-registration'] !== 'retained')
|
|
125
|
+
rememberApplication?.();
|
|
115
126
|
return result;
|
|
116
127
|
}
|
|
117
128
|
export class MagyarPostaTracker {
|
|
118
129
|
options;
|
|
119
130
|
/** Until when Google's refusal holds, for the app's widget and for the tracker's checkbox, which have their own keys. */
|
|
120
131
|
#refusedUntil = { app: 0, web: 0 };
|
|
132
|
+
/** A helper with retained registrations checks those before its own widget cooldown. */
|
|
133
|
+
#registrationCache = false;
|
|
121
134
|
/** The application id each added number is read under, oldest first. */
|
|
122
135
|
#added = new Map();
|
|
123
136
|
/** The id new numbers go to, generated when first needed, and how many it took. */
|
|
@@ -178,7 +191,7 @@ export class MagyarPostaTracker {
|
|
|
178
191
|
const id = await this.#applicationId(signal, remainingMs);
|
|
179
192
|
const route = id ? 'app' : 'web';
|
|
180
193
|
// Another solve soon after a refusal would hold a browser for the same answer.
|
|
181
|
-
if (Date.now() < this.#refusedUntil[route]) {
|
|
194
|
+
if (Date.now() < this.#refusedUntil[route] && !(route === 'app' && this.#registrationCache)) {
|
|
182
195
|
throw new ChallengeError(PROVIDER, 'Google refused Magyar Posta\'s audio challenge recently', { reason: 'verification_refused' });
|
|
183
196
|
}
|
|
184
197
|
if (id && (this.#unadded.get(number) ?? 0) > Date.now()) {
|
|
@@ -190,10 +203,13 @@ export class MagyarPostaTracker {
|
|
|
190
203
|
if (!id)
|
|
191
204
|
return await magyarPostaBrowser(trawl, number, signal, left);
|
|
192
205
|
const current = this.#current;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
let added = false;
|
|
207
|
+
const result = await magyarPostaAppBrowser(trawl, number, id, signal, left, () => { added = true; }, () => { this.#registrationCache = true; });
|
|
208
|
+
if (added) {
|
|
209
|
+
if (current && this.#current === current)
|
|
210
|
+
current.count++;
|
|
211
|
+
this.#remember(number, id);
|
|
212
|
+
}
|
|
197
213
|
return result;
|
|
198
214
|
}
|
|
199
215
|
catch (error) {
|
|
@@ -246,6 +246,12 @@ function providerWordingStage(description) {
|
|
|
246
246
|
return 'registered';
|
|
247
247
|
if (/^export declaration in progress[.!]?$/i.test(value))
|
|
248
248
|
return 'customs';
|
|
249
|
+
// KTD's steps as ParcelsApp relays them: the first carrier's local warehouse taking the parcel in and
|
|
250
|
+
// sending it on to KTD's warehouse, the parcel leaving that, its export cleared and its flight taking off.
|
|
251
|
+
if (/^package received and scanned at the carrier['’]s local warehouse[.!]?$/i.test(value))
|
|
252
|
+
return 'accepted';
|
|
253
|
+
if (/^package leaves the carrier['’]s local warehouse[.!]?$|^your item has left the [a-z ]{1,40} warehouse[.!]?$|^export declaration completed[.!]?$|^take ?off[.!]?$/i.test(value))
|
|
254
|
+
return 'in_transit';
|
|
249
255
|
// MRW's own wording for a parcel left at one of its pickup points, relayed by a provider,
|
|
250
256
|
// and for its pickup from the sender still to come, as MRW's own map reads them.
|
|
251
257
|
if (/^depositado en punto mrw[.!]?$/i.test(value))
|
package/dist/server/openapi.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-parcel-scraper",
|
|
3
|
-
"version": "0.776.
|
|
3
|
+
"version": "0.776.2-main.4151",
|
|
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",
|