universal-parcel-scraper 0.69.6-main.895 → 0.70.0-main.901
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/data/catalog.json +16 -1
- package/dist/browser/scraper.js +57 -32
- package/dist/carriers/colis-prive/adapter.d.ts +13 -7
- package/dist/carriers/colis-prive/adapter.js +41 -19
- package/dist/carriers/colis-prive/carrier.json +15 -0
- package/dist/carriers/dpd/carrier.json +1 -1
- package/dist/carriers/evri-uk/browser.js +1 -1
- package/dist/carriers/evri-uk/parser.js +8 -2
- package/dist/carriers/evri-uk/statuses.json +3 -0
- package/dist/carriers/hermes-de/adapter.js +19 -4
- package/dist/carriers/hermes-de/status.d.ts +2 -0
- package/dist/carriers/hermes-de/status.js +5 -1
- package/dist/carriers/hermes-de/statuses.json +2 -2
- package/dist/core/catalog/inputs.js +10 -2
- package/dist/core/catalog/postcode.d.ts +7 -0
- package/dist/core/catalog/postcode.js +15 -0
- package/dist/core/catalog/types.d.ts +1 -1
- package/dist/data/catalog.json +16 -1
- package/dist/generated/catalog.d.ts +14 -1
- package/dist/generated/catalog.js +16 -1
- package/dist/server/openapi.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ createTracker({ providers: ['ParcelsApp', 'Ship24', '17TRACK', 'Postal Ninja', '
|
|
|
155
155
|
<!-- GENERATED:stages -->
|
|
156
156
|
<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">
|
|
157
157
|
|
|
158
|
-
The carrier folders record 3,
|
|
158
|
+
The carrier folders record 3,350 such statuses, each filed under one stage.
|
|
159
159
|
<!-- /GENERATED:stages -->
|
|
160
160
|
|
|
161
161
|
Wording nobody recorded yet goes through a classifier that reads seven European languages.
|
package/data/catalog.json
CHANGED
|
@@ -943,7 +943,7 @@
|
|
|
943
943
|
],
|
|
944
944
|
"detectionRules": [
|
|
945
945
|
{
|
|
946
|
-
"pattern": "^06
|
|
946
|
+
"pattern": "^06[01]\\d{11}$",
|
|
947
947
|
"confidence": "low",
|
|
948
948
|
"preferred": true
|
|
949
949
|
},
|
|
@@ -1312,6 +1312,21 @@
|
|
|
1312
1312
|
"tracking": {
|
|
1313
1313
|
"mode": "automatic",
|
|
1314
1314
|
"adapter": "colis-prive",
|
|
1315
|
+
"requirements": [
|
|
1316
|
+
{
|
|
1317
|
+
"field": "postcode",
|
|
1318
|
+
"whenTrackingNumber": "^[A-Z0-9]{12}$",
|
|
1319
|
+
"validator": "colisPrivePostcode",
|
|
1320
|
+
"label": "Delivery postcode",
|
|
1321
|
+
"type": "text",
|
|
1322
|
+
"placeholder": "75001",
|
|
1323
|
+
"help": "Colis Privé needs the recipient postcode with the 12-character number: 5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg.",
|
|
1324
|
+
"pattern": "^(?:(?:0[1-9]|[1-8][0-9]|9[0-578])[0-9]{3}|(?:(?:[Bb][Ee]?|[Ll][Uu]?)[ \\-]?)?[1-9][0-9]{3})$",
|
|
1325
|
+
"maxLength": 7,
|
|
1326
|
+
"inputMode": "text",
|
|
1327
|
+
"autoComplete": "postal-code"
|
|
1328
|
+
}
|
|
1329
|
+
],
|
|
1315
1330
|
"recognitionRank": 30
|
|
1316
1331
|
},
|
|
1317
1332
|
"canaryUrl": "https://colisprive.com/moncolis/pages/DetailColis.aspx",
|
package/dist/browser/scraper.js
CHANGED
|
@@ -879,7 +879,7 @@ var ye = {
|
|
|
879
879
|
}],
|
|
880
880
|
detectionRules: [
|
|
881
881
|
{
|
|
882
|
-
pattern: "^06
|
|
882
|
+
pattern: "^06[01]\\d{11}$",
|
|
883
883
|
confidence: "low",
|
|
884
884
|
preferred: !0
|
|
885
885
|
},
|
|
@@ -1194,6 +1194,19 @@ var ye = {
|
|
|
1194
1194
|
tracking: {
|
|
1195
1195
|
mode: "automatic",
|
|
1196
1196
|
adapter: "colis-prive",
|
|
1197
|
+
requirements: [{
|
|
1198
|
+
field: "postcode",
|
|
1199
|
+
whenTrackingNumber: "^[A-Z0-9]{12}$",
|
|
1200
|
+
validator: "colisPrivePostcode",
|
|
1201
|
+
label: "Delivery postcode",
|
|
1202
|
+
type: "text",
|
|
1203
|
+
placeholder: "75001",
|
|
1204
|
+
help: "Colis Privé needs the recipient postcode with the 12-character number: 5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg.",
|
|
1205
|
+
pattern: "^(?:(?:0[1-9]|[1-8][0-9]|9[0-578])[0-9]{3}|(?:(?:[Bb][Ee]?|[Ll][Uu]?)[ \\-]?)?[1-9][0-9]{3})$",
|
|
1206
|
+
maxLength: 7,
|
|
1207
|
+
inputMode: "text",
|
|
1208
|
+
autoComplete: "postal-code"
|
|
1209
|
+
}],
|
|
1197
1210
|
recognitionRank: 30
|
|
1198
1211
|
},
|
|
1199
1212
|
canaryUrl: "https://colisprive.com/moncolis/pages/DetailColis.aspx",
|
|
@@ -11947,9 +11960,15 @@ var Yc = /^(?=.{3,12}$)(?=.*\d)[A-Z0-9]+(?:[ -][A-Z0-9]+)*$/;
|
|
|
11947
11960
|
function Xc(e) {
|
|
11948
11961
|
return e.trim().toUpperCase().replace(/\s+/g, " ");
|
|
11949
11962
|
}
|
|
11963
|
+
function Zc(e) {
|
|
11964
|
+
let t = e.toUpperCase().replace(/\s+/g, "");
|
|
11965
|
+
if (/^(?:0[1-9]|[1-8]\d|9[0-5]|97|98)\d{3}$/.test(t)) return t;
|
|
11966
|
+
let n = /^(?:(BE?|LU?)-?)?([1-9]\d{3})$/.exec(t);
|
|
11967
|
+
return n ? `${n[1]?.charAt(0) ?? ""}${n[2]}` : null;
|
|
11968
|
+
}
|
|
11950
11969
|
//#endregion
|
|
11951
11970
|
//#region core/catalog/inputs.ts
|
|
11952
|
-
function
|
|
11971
|
+
function Qc(e, t, n, r) {
|
|
11953
11972
|
let i = {
|
|
11954
11973
|
trackingUrl: n.trim() || null,
|
|
11955
11974
|
postcode: r.trim() || null
|
|
@@ -11960,7 +11979,7 @@ function Zc(e, t, n, r) {
|
|
|
11960
11979
|
field: "postcode",
|
|
11961
11980
|
validator: "internationalPostcode"
|
|
11962
11981
|
});
|
|
11963
|
-
for (let [e, t] of Object.entries(i)) if (t !== null && !o.has(e)) throw TypeError(e === "trackingUrl" ? "A tracking URL is not used for this carrier or tracking number" : "A delivery postcode is not used for this carrier");
|
|
11982
|
+
for (let [e, t] of Object.entries(i)) if (t !== null && !o.has(e)) throw TypeError(e === "trackingUrl" ? "A tracking URL is not used for this carrier or tracking number" : "A delivery postcode is not used for this carrier or tracking number");
|
|
11964
11983
|
for (let [n, r] of o) {
|
|
11965
11984
|
let a = i[n];
|
|
11966
11985
|
if (!a) {
|
|
@@ -11994,17 +12013,23 @@ function Zc(e, t, n, r) {
|
|
|
11994
12013
|
i[n] = e.replace(/\s+/g, "");
|
|
11995
12014
|
break;
|
|
11996
12015
|
}
|
|
12016
|
+
case "colisPrivePostcode": {
|
|
12017
|
+
let t = Zc(a);
|
|
12018
|
+
if (!t) throw TypeError(`${x(e).displayName} requires a French, Belgian or Luxembourg delivery postcode`);
|
|
12019
|
+
i[n] = t;
|
|
12020
|
+
break;
|
|
12021
|
+
}
|
|
11997
12022
|
default: r.validator;
|
|
11998
12023
|
}
|
|
11999
12024
|
}
|
|
12000
12025
|
return i;
|
|
12001
12026
|
}
|
|
12002
|
-
function
|
|
12027
|
+
function $c(e) {
|
|
12003
12028
|
let t = Xc(e);
|
|
12004
12029
|
if (!Yc.test(t)) throw TypeError("A valid delivery postcode is required");
|
|
12005
12030
|
return t;
|
|
12006
12031
|
}
|
|
12007
|
-
var
|
|
12032
|
+
var el = { carriers: [
|
|
12008
12033
|
{
|
|
12009
12034
|
carrier: "dhl",
|
|
12010
12035
|
direct: "Yes",
|
|
@@ -15970,80 +15995,80 @@ var $c = { carriers: [
|
|
|
15970
15995
|
}
|
|
15971
15996
|
}]
|
|
15972
15997
|
}
|
|
15973
|
-
] },
|
|
15998
|
+
] }, tl = [
|
|
15974
15999
|
"Ship24",
|
|
15975
16000
|
"ParcelsApp",
|
|
15976
16001
|
"17TRACK",
|
|
15977
16002
|
"Postal Ninja",
|
|
15978
16003
|
"UPU"
|
|
15979
|
-
],
|
|
16004
|
+
], nl = /* @__PURE__ */ new Set([
|
|
15980
16005
|
"no_history",
|
|
15981
16006
|
"summary_only",
|
|
15982
16007
|
"undated",
|
|
15983
16008
|
"sign_in",
|
|
15984
16009
|
"postcode_prompt"
|
|
15985
|
-
]),
|
|
15986
|
-
function
|
|
16010
|
+
]), rl = el.carriers, il = new Map(rl.map((e) => [e.carrier, e]));
|
|
16011
|
+
function al(e) {
|
|
15987
16012
|
return typeof e == "number" || e === "history" || typeof e == "object" && !!e;
|
|
15988
16013
|
}
|
|
15989
|
-
function
|
|
15990
|
-
let n = t.filter((t) => t.results[e] !== void 0), r = n.some((t) => !t.stale && (
|
|
15991
|
-
return n.some((t) =>
|
|
16014
|
+
function ol(e, t) {
|
|
16015
|
+
let n = t.filter((t) => t.results[e] !== void 0), r = n.some((t) => !t.stale && (nl.has(t.results[e]) || t.results[e] === "wrong_carrier") && Object.entries(t.results).some(([t, n]) => t !== e && t !== "UPU" && al(n)));
|
|
16016
|
+
return n.some((t) => al(t.results[e])) ? n.some((t) => typeof t.results[e] == "object") || r ? "partial" : "full" : n.some((t) => ["wrong_carrier", "refused"].includes(t.results[e])) ? "excluded" : n.some((t) => !t.stale && nl.has(t.results[e])) ? "empty" : "unknown";
|
|
15992
16017
|
}
|
|
15993
|
-
function
|
|
15994
|
-
let t = e ?
|
|
15995
|
-
return t ? Object.fromEntries(
|
|
16018
|
+
function sl(e) {
|
|
16019
|
+
let t = e ? il.get(e) : void 0;
|
|
16020
|
+
return t ? Object.fromEntries(tl.map((e) => [e, ol(e, t.references)])) : null;
|
|
15996
16021
|
}
|
|
15997
16022
|
//#endregion
|
|
15998
16023
|
//#region providers/plan.ts
|
|
15999
|
-
var
|
|
16024
|
+
var cl = [
|
|
16000
16025
|
"ParcelsApp",
|
|
16001
16026
|
"Ship24",
|
|
16002
16027
|
"17TRACK",
|
|
16003
16028
|
"UPU"
|
|
16004
|
-
],
|
|
16005
|
-
function
|
|
16029
|
+
], ll = /* @__PURE__ */ new Set(["17TRACK", "Postal Ninja"]);
|
|
16030
|
+
function ul(e) {
|
|
16006
16031
|
return e === "ParcelsApp" ? 45e3 : e === "UPU" ? 8e3 : 3e4;
|
|
16007
16032
|
}
|
|
16008
|
-
function
|
|
16033
|
+
function dl(e) {
|
|
16009
16034
|
return e && /^\d{10}$/.test(y(e)) ? "Ship24" : e && /^[CL][A-Z]\d{9}CN$/.test(y(e)) && ft(e) ? "17TRACK" : void 0;
|
|
16010
16035
|
}
|
|
16011
|
-
var
|
|
16036
|
+
var fl = {
|
|
16012
16037
|
full: 0,
|
|
16013
16038
|
partial: 1,
|
|
16014
16039
|
unknown: 2,
|
|
16015
16040
|
empty: 3,
|
|
16016
16041
|
excluded: 4
|
|
16017
16042
|
};
|
|
16018
|
-
function
|
|
16019
|
-
let n =
|
|
16020
|
-
return t === "UPU" ?
|
|
16043
|
+
function pl(e, t) {
|
|
16044
|
+
let n = fl[e?.[t] ?? "unknown"];
|
|
16045
|
+
return t === "UPU" ? fl.unknown : t === "Postal Ninja" ? Math.max(n, fl.empty) : n;
|
|
16021
16046
|
}
|
|
16022
|
-
var
|
|
16023
|
-
function
|
|
16047
|
+
var ml = (e) => e === "UPU" ? 2 : +(e === "Postal Ninja");
|
|
16048
|
+
function hl(e, t) {
|
|
16024
16049
|
let n = (e) => e !== "UPU" && t?.[e] === "excluded";
|
|
16025
|
-
return (e.some((e) => e !== "UPU" && !n(e)) ? e.filter((e) => !n(e)) : [...e]).sort((n, r) =>
|
|
16050
|
+
return (e.some((e) => e !== "UPU" && !n(e)) ? e.filter((e) => !n(e)) : [...e]).sort((n, r) => ml(n) - ml(r) || pl(t, n) - pl(t, r) || Number(ll.has(n)) - Number(ll.has(r)) || e.indexOf(n) - e.indexOf(r));
|
|
16026
16051
|
}
|
|
16027
|
-
function
|
|
16052
|
+
function gl(e = {}) {
|
|
16028
16053
|
let { trackingNumber: t, enablePostalNinja: n = !1 } = e, r = (n ? [
|
|
16029
16054
|
"ParcelsApp",
|
|
16030
16055
|
"Ship24",
|
|
16031
16056
|
"17TRACK",
|
|
16032
16057
|
"Postal Ninja",
|
|
16033
16058
|
"UPU"
|
|
16034
|
-
] : [...
|
|
16059
|
+
] : [...cl]).filter((e) => e !== "UPU" || t === void 0 || ft(t)), i = (e.carriers ?? []).find((e) => sl(e)) ?? null, a = sl(i), o = hl(r, a), s = dl(t);
|
|
16035
16060
|
return {
|
|
16036
16061
|
sources: s ? [s, ...o.filter((e) => e !== s)] : o,
|
|
16037
16062
|
carrier: i,
|
|
16038
16063
|
tier: (e) => a?.[e] ?? "unknown",
|
|
16039
|
-
rank: (e) =>
|
|
16064
|
+
rank: (e) => pl(a, e)
|
|
16040
16065
|
};
|
|
16041
16066
|
}
|
|
16042
|
-
function
|
|
16043
|
-
return
|
|
16067
|
+
function _l(e = !1, t) {
|
|
16068
|
+
return gl({
|
|
16044
16069
|
trackingNumber: t,
|
|
16045
16070
|
enablePostalNinja: e
|
|
16046
16071
|
}).sources;
|
|
16047
16072
|
}
|
|
16048
16073
|
//#endregion
|
|
16049
|
-
export { Ce as AMAZON_NUMBER_PATTERN, Pe as AUTOMATIC_CARRIER_IDS,
|
|
16074
|
+
export { Ce as AMAZON_NUMBER_PATTERN, Pe as AUTOMATIC_CARRIER_IDS, ll as BROWSER_SOURCES, f as BudgetExceededError, S as CARRIERS, ye as CARRIER_CATALOG, b as CARRIER_DEFINITIONS, be as CARRIER_IDS, Fe as CARRIER_NAMES, t as CarrierError, a as ChallengeError, Lo as EXPLICIT_OFFSET_PATTERN, r as IndeterminateError, l as InputRequiredError, u as InvalidInputError, vc as MAX_RECOGNITIONS, s as MaintenanceError, i as NoHistoryError, n as NotFoundError, o as RateLimitedError, Ue as SELECTABLE_CARRIERS, Se as STAGES, c as SchemaError, Mt as TRACKING_CANDIDATE_PATTERNS, Ie as TRACKING_LINK_RULES, d as TransportError, cl as UNIVERSAL_SOURCES, m as UpstreamHttpError, ee as UpstreamNetworkError, Me as activeRequirements, Ee as amazonMarketplace, De as amazonOrdersUrl, Oe as amazonShippingOrigin, ke as amazonShippingUrl, ds as calendarDay, as as canadaProvinceTimeZone, je as carrierAdapter, x as carrierDefinition, g as carrierErrorKind, He as carrierInfo, Le as carrierLinkRules, We as carrierRequirements, Ae as carrierTimezone, Dt as checksumRejections, lc as classifyStage, ic as classifyWording, $o as countryCode, es as countryTimeZone, Rc as deliveryHandoff, Ot as detectCarrier, C as detectCarrierMatch, ls as epochMillisTime, us as epochSecondsTime, te as errorTypeOf, xs as eventPoint, zo as explicitOffsetTime, mc as failureHint, pc as failureKind, ge as formatTrackingNumber, Lc as hasDirectHandoffAdapter, uc as inferStage, we as isAmazonTrackingNumber, he as isPlanzerSharedTrackingNumber, Xe as isValidCorreosSpainCheckLetter, v as isValidDhlExpressWaybill, $e as isValidDpdParcelNumber, ct as isValidGlsParcelNumber, lt as isValidHermesParcelNumber, me as isValidMondialRelayBarcode, fe as isValidPocztaPolskaBarcode, ft as isValidS10TrackingNumber, pe as isValidSscc, oe as isValidTntConsignmentNumber, bt as isValidUspsPackageBarcode, Vo as isoTime, It as keywordNumberInText, rc as languageStageStatus, qe as localizedCarrierUrl, Re as matchesDomain, Uo as mislabeledLocalTime, Wo as mislabeledWallTime, Qc as normalizeCarrierInputs, Ss as normalizeCarrierResult, $c as normalizeDeliveryPostcode, y as normalizeTrackingNumber, hl as orderUniversalSources, Ht as parseTrackingInput, dl as priorityUniversalSource, xc as recognitionCandidates, yc as recognitionNumberShape, Cc as recognizeAll, Nt as recognizedNumberInText, cs as regionHasTown, Ne as requiredRequirements, Ge as requirementSatisfied, Te as requiresAmazonAccount, fc as resolveResult, cc as resultHasUpdate, sc as resultStage, _ as retryAfterMsOf, qo as settleGuessedClocks, Dc as settleRecognition, Go as sharedClockZone, dc as stageSource, mt as supportsSwissPostHandoff, ts as timeZoneCountry, os as townKey, nc as trackingLanguageStage, Be as trackingLink, ze as trackingNumberForLink, Ke as tracksAutomatically, gl as universalPlan, ul as universalSourceBudget, _l as universalSources, rs as usStateTimeZone, yt as uspsPackageIdentifier, At as validTrackingNumber, Jc as validateDachserTrackingUrl, qc as validatePlanzerSharedUrl, ac as wordingStage, Bo as zonedTime };
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* before any text is read, so nothing identifying a person can reach the result.
|
|
8
8
|
*
|
|
9
9
|
* The lookup credential is the 12-character shipment number followed by the
|
|
10
|
-
* recipient's postcode: five digits in France,
|
|
11
|
-
* and four digits. The postcode
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* recipient's postcode: five digits in France, four in Belgium, bare or after
|
|
11
|
+
* B, or L and four digits in Luxembourg. The postcode comes at the end of the
|
|
12
|
+
* number, as in Colis Privé's own links, or as a separate input. It is what
|
|
13
|
+
* makes the page reachable, so it is part of the tracking credential: it is
|
|
14
|
+
* never logged, put in an issue or written into a fixture.
|
|
14
15
|
*/
|
|
15
16
|
import { type AdapterFactory, type TrackingContext } from '../../core/adapter/index.js';
|
|
16
17
|
import { NotFoundError } from '../../core/errors/index.js';
|
|
@@ -23,8 +24,13 @@ export { classifyStatus } from './status.js';
|
|
|
23
24
|
export declare class ColisPriveTrackingError extends NotFoundError {
|
|
24
25
|
constructor();
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The value the page is opened with: the shipment and the postcode, written
|
|
29
|
+
* after it or given apart. A bare shipment is a missing credential, as the
|
|
30
|
+
* page never opens without its postcode.
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeColisPriveCredential(raw: string, rawPostcode?: string): string;
|
|
33
|
+
export declare function colisPriveTrackingUrl(rawCredential: string, rawPostcode?: string): string;
|
|
28
34
|
export declare function parseColisPriveTrackingHtml(html: string, rawCredential: string): CarrierResult;
|
|
29
35
|
export interface ColisPriveTrackerOptions {
|
|
30
36
|
timeoutMs?: number;
|
|
@@ -36,6 +42,6 @@ export declare class ColisPriveTracker {
|
|
|
36
42
|
#private;
|
|
37
43
|
readonly timeoutMs: number;
|
|
38
44
|
constructor(options?: ColisPriveTrackerOptions);
|
|
39
|
-
fetch(rawCredential: string, context?: TrackingContext): Promise<CarrierResult>;
|
|
45
|
+
fetch(rawCredential: string, rawPostcode?: string, context?: TrackingContext): Promise<CarrierResult>;
|
|
40
46
|
}
|
|
41
47
|
export declare const adapter: AdapterFactory;
|
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
* before any text is read, so nothing identifying a person can reach the result.
|
|
8
8
|
*
|
|
9
9
|
* The lookup credential is the 12-character shipment number followed by the
|
|
10
|
-
* recipient's postcode: five digits in France,
|
|
11
|
-
* and four digits. The postcode
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* recipient's postcode: five digits in France, four in Belgium, bare or after
|
|
11
|
+
* B, or L and four digits in Luxembourg. The postcode comes at the end of the
|
|
12
|
+
* number, as in Colis Privé's own links, or as a separate input. It is what
|
|
13
|
+
* makes the page reachable, so it is part of the tracking credential: it is
|
|
14
|
+
* never logged, put in an issue or written into a fixture.
|
|
14
15
|
*/
|
|
15
16
|
import { load } from 'cheerio';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
17
|
+
import { lookupBudget, recognizeFromLookup } from '../../core/adapter/index.js';
|
|
18
|
+
import { colisPrivePostcode } from '../../core/catalog/postcode.js';
|
|
19
|
+
import { InputRequiredError, InvalidInputError, NotFoundError, SchemaError } from '../../core/errors/index.js';
|
|
18
20
|
import { UpstreamHttpError, clean, decodeText, fetchBounded, userAgentOf } from '../../core/transport/index.js';
|
|
19
21
|
import { calendarDay } from '../../core/time/index.js';
|
|
20
22
|
import { classifyStatus, comparableText } from './status.js';
|
|
@@ -25,6 +27,14 @@ const TIMEZONE = 'Europe/Paris';
|
|
|
25
27
|
const MAX_RESPONSE_BYTES = 500_000;
|
|
26
28
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
27
29
|
const MAX_EVENTS_TO_RETURN = 100;
|
|
30
|
+
const CREDENTIAL_MESSAGE = 'Colis Privé tracking requires the 12-character shipment number and the recipient postcode '
|
|
31
|
+
+ '(5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg)';
|
|
32
|
+
const SHIPMENT = /^[A-Z0-9]{12}$/;
|
|
33
|
+
/** The shipment with its postcode at the end, as Colis Privé's links carry it. */
|
|
34
|
+
function combined(number) {
|
|
35
|
+
const postcode = number.slice(12);
|
|
36
|
+
return SHIPMENT.test(number.slice(0, 12)) && colisPrivePostcode(postcode) === postcode;
|
|
37
|
+
}
|
|
28
38
|
function shipmentPart(credential) {
|
|
29
39
|
return credential.slice(0, 12);
|
|
30
40
|
}
|
|
@@ -60,12 +70,24 @@ export class ColisPriveTrackingError extends NotFoundError {
|
|
|
60
70
|
this.name = 'ColisPriveTrackingError';
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
/**
|
|
74
|
+
* The value the page is opened with: the shipment and the postcode, written
|
|
75
|
+
* after it or given apart. A bare shipment is a missing credential, as the
|
|
76
|
+
* page never opens without its postcode.
|
|
77
|
+
*/
|
|
78
|
+
export function normalizeColisPriveCredential(raw, rawPostcode = '') {
|
|
79
|
+
const number = raw.trim().toLocaleUpperCase('en-US');
|
|
80
|
+
if (rawPostcode.trim()) {
|
|
81
|
+
const postcode = colisPrivePostcode(rawPostcode);
|
|
82
|
+
if (!SHIPMENT.test(number) || !postcode)
|
|
83
|
+
throw new InvalidInputError(PROVIDER, CREDENTIAL_MESSAGE);
|
|
84
|
+
return `${number}${postcode}`;
|
|
67
85
|
}
|
|
68
|
-
|
|
86
|
+
if (SHIPMENT.test(number))
|
|
87
|
+
throw new InputRequiredError(PROVIDER, 'the recipient postcode', CREDENTIAL_MESSAGE);
|
|
88
|
+
if (!combined(number))
|
|
89
|
+
throw new InvalidInputError(PROVIDER, CREDENTIAL_MESSAGE);
|
|
90
|
+
return number;
|
|
69
91
|
}
|
|
70
92
|
/** The day a row announces for a confirmed appointment or a relay drop-off: "… le DD/MM/YYYY". */
|
|
71
93
|
function plannedDay(description) {
|
|
@@ -75,8 +97,8 @@ function plannedDay(description) {
|
|
|
75
97
|
return match ? calendarDay(Number(match[3]), Number(match[2]), Number(match[1])) : null;
|
|
76
98
|
}
|
|
77
99
|
const SETTLED = new Set(['delivered', 'returned', 'exception', 'failed_attempt', 'ready_for_pickup']);
|
|
78
|
-
export function colisPriveTrackingUrl(rawCredential) {
|
|
79
|
-
const credential = normalizeColisPriveCredential(rawCredential);
|
|
100
|
+
export function colisPriveTrackingUrl(rawCredential, rawPostcode = '') {
|
|
101
|
+
const credential = normalizeColisPriveCredential(rawCredential, rawPostcode);
|
|
80
102
|
const url = new URL(TRACKING_ENDPOINT);
|
|
81
103
|
url.searchParams.set('numColis', credential);
|
|
82
104
|
url.searchParams.set('lang', 'fr');
|
|
@@ -164,8 +186,8 @@ export class ColisPriveTracker {
|
|
|
164
186
|
this.#fetcher = options.fetcher;
|
|
165
187
|
this.#userAgent = userAgentOf(options.userAgent);
|
|
166
188
|
}
|
|
167
|
-
async fetch(rawCredential, context = {}) {
|
|
168
|
-
const credential = normalizeColisPriveCredential(rawCredential);
|
|
189
|
+
async fetch(rawCredential, rawPostcode = '', context = {}) {
|
|
190
|
+
const credential = normalizeColisPriveCredential(rawCredential, rawPostcode);
|
|
169
191
|
const budget = lookupBudget(context, this.timeoutMs);
|
|
170
192
|
const { response, bytes } = await fetchBounded(colisPriveTrackingUrl(credential), {
|
|
171
193
|
signal: budget.signal,
|
|
@@ -197,9 +219,9 @@ export const adapter = (environment) => {
|
|
|
197
219
|
return {
|
|
198
220
|
id: 'colis-prive',
|
|
199
221
|
steps: ['direct'],
|
|
200
|
-
// The postcode
|
|
201
|
-
track: (input, context) => tracker.fetch(input.number, context),
|
|
202
|
-
//
|
|
203
|
-
recognize: (number, context) => recognizeFromLookup(() => tracker.fetch(number, context), () =>
|
|
222
|
+
// The postcode ends the number, or a bare number carries it apart.
|
|
223
|
+
track: (input, context) => tracker.fetch(input.number, input.postcode ?? '', context),
|
|
224
|
+
// Recognition gets no postcode, so a bare 12-character number reads as unknown.
|
|
225
|
+
recognize: (number, context) => recognizeFromLookup(() => tracker.fetch(number, '', context), () => combined(number.trim().toLocaleUpperCase('en-US'))),
|
|
204
226
|
};
|
|
205
227
|
};
|
|
@@ -19,6 +19,21 @@
|
|
|
19
19
|
"tracking": {
|
|
20
20
|
"mode": "automatic",
|
|
21
21
|
"adapter": "colis-prive",
|
|
22
|
+
"requirements": [
|
|
23
|
+
{
|
|
24
|
+
"field": "postcode",
|
|
25
|
+
"whenTrackingNumber": "^[A-Z0-9]{12}$",
|
|
26
|
+
"validator": "colisPrivePostcode",
|
|
27
|
+
"label": "Delivery postcode",
|
|
28
|
+
"type": "text",
|
|
29
|
+
"placeholder": "75001",
|
|
30
|
+
"help": "Colis Privé needs the recipient postcode with the 12-character number: 5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg.",
|
|
31
|
+
"pattern": "^(?:(?:0[1-9]|[1-8][0-9]|9[0-578])[0-9]{3}|(?:(?:[Bb][Ee]?|[Ll][Uu]?)[ \\-]?)?[1-9][0-9]{3})$",
|
|
32
|
+
"maxLength": 7,
|
|
33
|
+
"inputMode": "text",
|
|
34
|
+
"autoComplete": "postal-code"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
22
37
|
"steps": [
|
|
23
38
|
"direct"
|
|
24
39
|
],
|
|
@@ -113,7 +113,7 @@ export async function requestEvriUkInPage({ number, budgetMs }) {
|
|
|
113
113
|
}
|
|
114
114
|
const stageCode = event.trackingStage.trackingStageCode.trim();
|
|
115
115
|
const description = event.trackingPoint.description;
|
|
116
|
-
const safeDescription = stageCode === '5_COURIER' ? 'Delivered'
|
|
116
|
+
const safeDescription = stageCode === '5_COURIER' || stageCode === '5_SHOP' ? 'Delivered'
|
|
117
117
|
: /\b(?:delivered (?:to|by)|signed (?:for )?by)\b/i.test(description) ? 'Delivery update' : description;
|
|
118
118
|
const eta = record(event.eta) && scalar(event.eta.start, 64) && scalar(event.eta.end, 64)
|
|
119
119
|
? { start: event.eta.start, end: event.eta.end } : undefined;
|
|
@@ -5,14 +5,18 @@ import { explicitOffsetTime } from '../../core/time/index.js';
|
|
|
5
5
|
import { clean, cleanScalar } from '../../core/transport/text.js';
|
|
6
6
|
import { isRecord } from '../../core/types.js';
|
|
7
7
|
// Evri's rail codes. A parcel's `trackingListV2` lists the rail it is on;
|
|
8
|
-
// a customer return runs 0, 2, 4.
|
|
8
|
+
// a customer return runs 0, 2, 4. A parcel sent to a ParcelShop or locker
|
|
9
|
+
// waits on 4_SHOP or 4_LOCKER, which Evri's page calls awaiting collection,
|
|
10
|
+
// and 5_SHOP is its collection.
|
|
9
11
|
const STAGES = new Map([
|
|
10
12
|
['0', 'registered'], ['1', 'registered'], ['2', 'in_transit'], ['3', 'in_transit'], ['4', 'returned'],
|
|
11
13
|
['4_COURIER', 'out_for_delivery'], ['5_COURIER', 'delivered'],
|
|
14
|
+
['4_SHOP', 'ready_for_pickup'], ['4_LOCKER', 'ready_for_pickup'], ['5_SHOP', 'delivered'],
|
|
12
15
|
]);
|
|
13
16
|
// Tracking points that their rail code files too broadly: a drop-off or
|
|
14
17
|
// collection is the acceptance, and a courier's failed visit stays on the
|
|
15
|
-
// out-for-delivery rail.
|
|
18
|
+
// out-for-delivery rail. The collection rails are never refined: a
|
|
19
|
+
// ParcelShop arrival there is the parcel waiting for its recipient.
|
|
16
20
|
const POINTS = new Map([
|
|
17
21
|
['ACCEPTED_BY_HERMES', 'accepted'], ['ARRIVED_PARCELSHOP', 'accepted'], ['COLLECTED_BY_EVRI', 'accepted'],
|
|
18
22
|
['QUADIENT_LOCKER_DROPOFF', 'accepted'], ['PROCESSING_RETURN', 'returned'],
|
|
@@ -22,6 +26,8 @@ function stageOf(stageCode, pointCode) {
|
|
|
22
26
|
const rail = STAGES.get(stageCode);
|
|
23
27
|
if (!rail)
|
|
24
28
|
return undefined;
|
|
29
|
+
if (rail === 'ready_for_pickup' || stageCode === '5_SHOP')
|
|
30
|
+
return rail;
|
|
25
31
|
if (rail === 'out_for_delivery' && pointCode.startsWith('NOT_DELIVERED_'))
|
|
26
32
|
return 'failed_attempt';
|
|
27
33
|
return POINTS.get(pointCode) ?? rail;
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
{ "code": "COURIER_REATTEMPT_CUSTOMER_NOT_AVAILABLE", "language": "en", "stage": "failed_attempt", "confirmedBy": "live", "firstSeen": "2026-10-08", "note": "On the courier rail: \"unable to deliver your parcel today\"." },
|
|
28
28
|
{ "code": "REDELIVER_WORKDAY_COURIER", "language": "en", "stage": "failed_attempt", "confirmedBy": "live", "firstSeen": "2026-10-08", "note": "Same wording as the missed visit." },
|
|
29
29
|
{ "code": "NOT_DELIVERED_BUSINESS_CLOSED", "language": "en", "stage": "failed_attempt", "confirmedBy": "live", "firstSeen": "2026-10-08", "note": "Every NOT_DELIVERED_ point on the courier rail is a failed attempt." },
|
|
30
|
+
{ "code": "4_SHOP", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "bundle", "firstSeen": "2026-10-11", "note": "Rail code: evri.com's tracking page reads 4_SHOP as awaiting collection at a ParcelShop. Every point on this rail is ready for pickup." },
|
|
31
|
+
{ "code": "4_LOCKER", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "bundle", "firstSeen": "2026-10-11", "note": "Rail code: awaiting collection from an Evri locker, opened with the recipient's code." },
|
|
32
|
+
{ "code": "5_SHOP", "language": "en", "stage": "delivered", "confirmedBy": "bundle", "firstSeen": "2026-10-11", "note": "Rail code: evri.com's page reads it as collected from the ParcelShop or locker; the wording becomes \"Delivered\"." },
|
|
30
33
|
{ "code": "DELIVERED", "language": "en", "stage": "delivered", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
31
34
|
{ "code": "DELIVERED_LETTERBOX", "language": "en", "stage": "delivered", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
32
35
|
{ "code": "DELIVERED_OUTBUILDING", "language": "en", "stage": "delivered", "confirmedBy": "live", "firstSeen": "2026-10-08" },
|
|
@@ -3,7 +3,7 @@ import { lookupBudget, recognizeFromLookup } from '../../core/adapter/index.js';
|
|
|
3
3
|
import { InvalidInputError, NotFoundError, SchemaError, UpstreamHttpError } from '../../core/errors/index.js';
|
|
4
4
|
import { clean, fetchBounded, parseJsonBytes, userAgentOf } from '../../core/transport/index.js';
|
|
5
5
|
import { isRecord } from '../../core/types.js';
|
|
6
|
-
import { COLLECTED_AT_PARCELSHOP, hermesGermanyMilestone, IGNORED_BOOKING_STATUS } from './status.js';
|
|
6
|
+
import { COLLECTED_AT_PARCELSHOP, hermesGermanyMilestone, IGNORED_BOOKING_STATUS, IN_PARCELBOX } from './status.js';
|
|
7
7
|
// Public recipient protocol inspected 2026-09-08:
|
|
8
8
|
// https://gcp-prd.my-deliveries.de/tnt/bundle/tnt-bundle-v2.js
|
|
9
9
|
// Separate from Hermes Einrichtungs-Service (myhes.de). The postcode-protected
|
|
@@ -113,9 +113,24 @@ export function parseHermesGermanyResponse(payload, trackingNumber) {
|
|
|
113
113
|
const atg = isRecord(parcel.atg) ? parcel.atg : {};
|
|
114
114
|
const sender = clean(atg.companyName) || null;
|
|
115
115
|
const eta = etaDate(parcel.eta ?? parcel.deliveryForecast ?? attributes.deliveredTimestamp);
|
|
116
|
-
|
|
116
|
+
// The tracking page offers the Hermes Box pickup code while
|
|
117
|
+
// `readyForParcelBoxCollection` is true: the parcel waits in the locker,
|
|
118
|
+
// even when the newest row or the delivered flag reads as a delivery.
|
|
119
|
+
// Once Hermes clears it, a box delivery that is still the newest row has
|
|
120
|
+
// been taken out.
|
|
121
|
+
const view = isRecord(parcel.viewParameters) ? parcel.viewParameters : {};
|
|
122
|
+
const boxReady = view.readyForParcelBoxCollection;
|
|
123
|
+
const latestCode = latest.event.provider_code;
|
|
124
|
+
const readsDelivered = !latest.metadata || latest.metadata.stage === 'delivered';
|
|
125
|
+
const inBox = latestCode === IN_PARCELBOX ? boxReady !== false : boxReady === true && readsDelivered;
|
|
126
|
+
const takenOut = latestCode === IN_PARCELBOX && boxReady === false;
|
|
127
|
+
if (inBox) {
|
|
128
|
+
latest.metadata = hermesGermanyMilestone(IN_PARCELBOX);
|
|
129
|
+
latest.event.stage = 'ready_for_pickup';
|
|
130
|
+
}
|
|
131
|
+
const deliveredByCode = latest.metadata?.status === 'delivered' || takenOut;
|
|
117
132
|
const deliveredByFlag = attributes.delivered === true;
|
|
118
|
-
const delivered = deliveredByCode || deliveredByFlag;
|
|
133
|
+
const delivered = !inBox && (deliveredByCode || deliveredByFlag);
|
|
119
134
|
if (!latest.metadata && !delivered) {
|
|
120
135
|
return {
|
|
121
136
|
status: 'unknown',
|
|
@@ -128,7 +143,7 @@ export function parseHermesGermanyResponse(payload, trackingNumber) {
|
|
|
128
143
|
};
|
|
129
144
|
}
|
|
130
145
|
const status = delivered ? 'delivered' : latest.metadata.status;
|
|
131
|
-
const stage = latest.metadata?.stage ?? (delivered ? 'delivered' : 'in_transit');
|
|
146
|
+
const stage = takenOut ? 'delivered' : latest.metadata?.stage ?? (delivered ? 'delivered' : 'in_transit');
|
|
132
147
|
const deliveredAt = delivered ? latest.event.time : null;
|
|
133
148
|
const pickupPoint = parcelShop(parcel, latest);
|
|
134
149
|
return {
|
|
@@ -21,6 +21,8 @@ export interface Milestone {
|
|
|
21
21
|
/** `parcelStatus` → milestone, read from the public recipient bundle on 2026-09-08. */
|
|
22
22
|
export declare const STATUSES: Record<string, Milestone>;
|
|
23
23
|
export declare const IGNORED_BOOKING_STATUS: Set<string>;
|
|
24
|
+
/** The code for a parcel put into a Hermes Box, which waits there for its recipient. */
|
|
25
|
+
export declare const IN_PARCELBOX = "DELIVERED_PARCELBOX";
|
|
24
26
|
/** Codes for a parcel the recipient collected at a ParcelShop. */
|
|
25
27
|
export declare const COLLECTED_AT_PARCELSHOP: Set<string>;
|
|
26
28
|
/** The milestone for a `parcelStatus`, or undefined when the code is not mapped. */
|
|
@@ -30,11 +30,13 @@ export const STATUSES = {
|
|
|
30
30
|
DELIVERED_DROPOFF: milestone('delivered', 'delivered', 'Delivered to the agreed safe place'),
|
|
31
31
|
DELIVERED_MAILBOX: milestone('delivered', 'delivered', 'Delivered to the mailbox'),
|
|
32
32
|
DELIVERED_PARCELSHOP: milestone('delivered', 'delivered', 'Collected at the ParcelShop'),
|
|
33
|
-
DELIVERED_PARCELBOX: milestone('delivered', 'delivered', 'Delivered to the parcel box'),
|
|
34
33
|
PICKED_UP_BY_RECIPIENT: milestone('delivered', 'delivered', 'Collected by the recipient'),
|
|
35
34
|
COLLECTED: milestone('delivered', 'delivered', 'Collected by the recipient'),
|
|
36
35
|
PARCELSHOP_COLLECTED_BY_CUSTOMER: milestone('delivered', 'delivered', 'Collected at the ParcelShop'),
|
|
37
36
|
INTERNATIONAL_DELIVERED: milestone('delivered', 'delivered', 'Delivered'),
|
|
37
|
+
// A Hermes Box is a pickup locker: the recipient takes the parcel out with
|
|
38
|
+
// a pickup code within 72 hours, or it goes back to the sender.
|
|
39
|
+
DELIVERED_PARCELBOX: milestone('ready_for_pickup', 'out_for_delivery', 'Ready for collection from the Hermes Box'),
|
|
38
40
|
READY_FOR_PICKUP: milestone('ready_for_pickup', 'out_for_delivery', 'Ready for collection'),
|
|
39
41
|
PARCELSHOP_ITEMS_FOR_COLLECTION: milestone('ready_for_pickup', 'out_for_delivery', 'Ready for collection at the ParcelShop'),
|
|
40
42
|
READY_FOR_COLLECTION: milestone('ready_for_pickup', 'out_for_delivery', 'Ready for collection'),
|
|
@@ -51,6 +53,8 @@ export const STATUSES = {
|
|
|
51
53
|
// Delivery preference bookings (a safe place, a ParcelShop) record the
|
|
52
54
|
// recipient's wish, not a movement, and must never move the parcel backwards.
|
|
53
55
|
export const IGNORED_BOOKING_STATUS = new Set(['EDL_BOOKED_DROPOFF', 'EDL_BOOKED_PARCELSHOP']);
|
|
56
|
+
/** The code for a parcel put into a Hermes Box, which waits there for its recipient. */
|
|
57
|
+
export const IN_PARCELBOX = 'DELIVERED_PARCELBOX';
|
|
54
58
|
/** Codes for a parcel the recipient collected at a ParcelShop. */
|
|
55
59
|
export const COLLECTED_AT_PARCELSHOP = new Set(['PARCELSHOP_COLLECTED_BY_CUSTOMER', 'DELIVERED_PARCELSHOP']);
|
|
56
60
|
/** The milestone for a `parcelStatus`, or undefined when the code is not mapped. */
|
|
@@ -184,10 +184,10 @@
|
|
|
184
184
|
},
|
|
185
185
|
{
|
|
186
186
|
"code": "DELIVERED_PARCELBOX",
|
|
187
|
-
"stage": "
|
|
187
|
+
"stage": "ready_for_pickup",
|
|
188
188
|
"confirmedBy": "official-doc",
|
|
189
189
|
"firstSeen": "2026-09-08",
|
|
190
|
-
"note": "
|
|
190
|
+
"note": "Put into a Hermes Box, a pickup locker. Hermes's Hermes Box terms (myhermes.de/content/pdf/hermesbox_nutzungsbedingungen.pdf) have the recipient generate a pickup code on the tracking page and take the parcel out within 72 hours, or it goes back to the sender."
|
|
191
191
|
},
|
|
192
192
|
{
|
|
193
193
|
"code": "PICKED_UP_BY_RECIPIENT",
|
|
@@ -3,7 +3,7 @@ import { activeRequirements, carrierDefinition } from './index.js';
|
|
|
3
3
|
import { isValidMondialRelayBarcode } from '../detection/index.js';
|
|
4
4
|
import { validateDachserTrackingUrl } from './urls.js';
|
|
5
5
|
import { validatePlanzerSharedUrl } from './urls.js';
|
|
6
|
-
import { DELIVERY_POSTCODE, deliveryPostcodeText } from './postcode.js';
|
|
6
|
+
import { DELIVERY_POSTCODE, colisPrivePostcode, deliveryPostcodeText } from './postcode.js';
|
|
7
7
|
export { AUTOMATIC_CARRIER_IDS, CARRIER_NAMES, activeRequirements, carrierAdapter, carrierDefinition, carrierTimezone, requiredRequirements, } from './index.js';
|
|
8
8
|
export { isValidS10TrackingNumber, supportsSwissPostHandoff, } from '../detection/index.js';
|
|
9
9
|
export function normalizeCarrierInputs(carrierId, trackingNumber, trackingUrl, postcode) {
|
|
@@ -29,7 +29,7 @@ export function normalizeCarrierInputs(carrierId, trackingNumber, trackingUrl, p
|
|
|
29
29
|
if (field === 'trackingUrl') {
|
|
30
30
|
throw new TypeError('A tracking URL is not used for this carrier or tracking number');
|
|
31
31
|
}
|
|
32
|
-
throw new TypeError('A delivery postcode is not used for this carrier');
|
|
32
|
+
throw new TypeError('A delivery postcode is not used for this carrier or tracking number');
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
for (const [field, requirement] of requirements) {
|
|
@@ -93,6 +93,14 @@ export function normalizeCarrierInputs(carrierId, trackingNumber, trackingUrl, p
|
|
|
93
93
|
supplied[field] = rawPostcode.replace(/\s+/g, '');
|
|
94
94
|
break;
|
|
95
95
|
}
|
|
96
|
+
case 'colisPrivePostcode': {
|
|
97
|
+
const normalized = colisPrivePostcode(value);
|
|
98
|
+
if (!normalized) {
|
|
99
|
+
throw new TypeError(`${carrierDefinition(carrierId).displayName} requires a French, Belgian or Luxembourg delivery postcode`);
|
|
100
|
+
}
|
|
101
|
+
supplied[field] = normalized;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
96
104
|
default:
|
|
97
105
|
requirement.validator;
|
|
98
106
|
}
|
|
@@ -2,3 +2,10 @@
|
|
|
2
2
|
export declare const DELIVERY_POSTCODE: RegExp;
|
|
3
3
|
/** A typed postcode as it is checked and sent: trimmed, in capitals, with single spaces. */
|
|
4
4
|
export declare function deliveryPostcodeText(value: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* A Colis Privé recipient postcode as its page takes it after the shipment
|
|
7
|
+
* number: France's five digits, or Belgium's or Luxembourg's four, bare or
|
|
8
|
+
* after B or L. A written country prefix (B-1000, LU 1234) becomes that
|
|
9
|
+
* letter. Null for anything else.
|
|
10
|
+
*/
|
|
11
|
+
export declare function colisPrivePostcode(value: string): string | null;
|
|
@@ -4,3 +4,18 @@ export const DELIVERY_POSTCODE = /^(?=.{3,12}$)(?=.*\d)[A-Z0-9]+(?:[ -][A-Z0-9]+
|
|
|
4
4
|
export function deliveryPostcodeText(value) {
|
|
5
5
|
return value.trim().toUpperCase().replace(/\s+/g, ' ');
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* A Colis Privé recipient postcode as its page takes it after the shipment
|
|
9
|
+
* number: France's five digits, or Belgium's or Luxembourg's four, bare or
|
|
10
|
+
* after B or L. A written country prefix (B-1000, LU 1234) becomes that
|
|
11
|
+
* letter. Null for anything else.
|
|
12
|
+
*/
|
|
13
|
+
export function colisPrivePostcode(value) {
|
|
14
|
+
const text = value.toUpperCase().replace(/\s+/g, '');
|
|
15
|
+
if (/^(?:0[1-9]|[1-8]\d|9[0-5]|97|98)\d{3}$/.test(text))
|
|
16
|
+
return text;
|
|
17
|
+
const match = /^(?:(BE?|LU?)-?)?([1-9]\d{3})$/.exec(text);
|
|
18
|
+
if (!match)
|
|
19
|
+
return null;
|
|
20
|
+
return `${match[1]?.charAt(0) ?? ''}${match[2]}`;
|
|
21
|
+
}
|
|
@@ -11,7 +11,7 @@ import type { CarrierId } from '../../generated/catalog.js';
|
|
|
11
11
|
import type { DetectionConfidence } from '../detection/types.js';
|
|
12
12
|
export type CarrierTrackingMode = 'automatic' | 'link-only';
|
|
13
13
|
export type CarrierInputField = 'trackingUrl' | 'postcode';
|
|
14
|
-
export type CarrierInputValidator = 'planzerSharedUrl' | 'dachserCapabilityUrl' | 'swissPostcode' | 'francePostcode' | 'germanyPostcode' | 'swissOrFrancePostcode' | 'internationalPostcode' | 'paackPostcode';
|
|
14
|
+
export type CarrierInputValidator = 'planzerSharedUrl' | 'dachserCapabilityUrl' | 'swissPostcode' | 'francePostcode' | 'germanyPostcode' | 'swissOrFrancePostcode' | 'internationalPostcode' | 'paackPostcode' | 'colisPrivePostcode';
|
|
15
15
|
/** What the "add a parcel" forms render for a carrier that needs extra input. */
|
|
16
16
|
export interface CarrierInputRequirement {
|
|
17
17
|
field: CarrierInputField;
|
package/dist/data/catalog.json
CHANGED
|
@@ -943,7 +943,7 @@
|
|
|
943
943
|
],
|
|
944
944
|
"detectionRules": [
|
|
945
945
|
{
|
|
946
|
-
"pattern": "^06
|
|
946
|
+
"pattern": "^06[01]\\d{11}$",
|
|
947
947
|
"confidence": "low",
|
|
948
948
|
"preferred": true
|
|
949
949
|
},
|
|
@@ -1312,6 +1312,21 @@
|
|
|
1312
1312
|
"tracking": {
|
|
1313
1313
|
"mode": "automatic",
|
|
1314
1314
|
"adapter": "colis-prive",
|
|
1315
|
+
"requirements": [
|
|
1316
|
+
{
|
|
1317
|
+
"field": "postcode",
|
|
1318
|
+
"whenTrackingNumber": "^[A-Z0-9]{12}$",
|
|
1319
|
+
"validator": "colisPrivePostcode",
|
|
1320
|
+
"label": "Delivery postcode",
|
|
1321
|
+
"type": "text",
|
|
1322
|
+
"placeholder": "75001",
|
|
1323
|
+
"help": "Colis Privé needs the recipient postcode with the 12-character number: 5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg.",
|
|
1324
|
+
"pattern": "^(?:(?:0[1-9]|[1-8][0-9]|9[0-578])[0-9]{3}|(?:(?:[Bb][Ee]?|[Ll][Uu]?)[ \\-]?)?[1-9][0-9]{3})$",
|
|
1325
|
+
"maxLength": 7,
|
|
1326
|
+
"inputMode": "text",
|
|
1327
|
+
"autoComplete": "postal-code"
|
|
1328
|
+
}
|
|
1329
|
+
],
|
|
1315
1330
|
"recognitionRank": 30
|
|
1316
1331
|
},
|
|
1317
1332
|
"canaryUrl": "https://colisprive.com/moncolis/pages/DetailColis.aspx",
|
|
@@ -591,7 +591,7 @@ export declare const CARRIER_CATALOG: {
|
|
|
591
591
|
readonly params: readonly ["parcelNumber", "parcelnumber"];
|
|
592
592
|
}];
|
|
593
593
|
readonly detectionRules: readonly [{
|
|
594
|
-
readonly pattern: "^06
|
|
594
|
+
readonly pattern: "^06[01]\\d{11}$";
|
|
595
595
|
readonly confidence: "low";
|
|
596
596
|
readonly preferred: true;
|
|
597
597
|
}, {
|
|
@@ -840,6 +840,19 @@ export declare const CARRIER_CATALOG: {
|
|
|
840
840
|
readonly tracking: {
|
|
841
841
|
readonly mode: "automatic";
|
|
842
842
|
readonly adapter: "colis-prive";
|
|
843
|
+
readonly requirements: readonly [{
|
|
844
|
+
readonly field: "postcode";
|
|
845
|
+
readonly whenTrackingNumber: "^[A-Z0-9]{12}$";
|
|
846
|
+
readonly validator: "colisPrivePostcode";
|
|
847
|
+
readonly label: "Delivery postcode";
|
|
848
|
+
readonly type: "text";
|
|
849
|
+
readonly placeholder: "75001";
|
|
850
|
+
readonly help: "Colis Privé needs the recipient postcode with the 12-character number: 5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg.";
|
|
851
|
+
readonly pattern: "^(?:(?:0[1-9]|[1-8][0-9]|9[0-578])[0-9]{3}|(?:(?:[Bb][Ee]?|[Ll][Uu]?)[ \\-]?)?[1-9][0-9]{3})$";
|
|
852
|
+
readonly maxLength: 7;
|
|
853
|
+
readonly inputMode: "text";
|
|
854
|
+
readonly autoComplete: "postal-code";
|
|
855
|
+
}];
|
|
843
856
|
readonly recognitionRank: 30;
|
|
844
857
|
};
|
|
845
858
|
readonly canaryUrl: "https://colisprive.com/moncolis/pages/DetailColis.aspx";
|
|
@@ -944,7 +944,7 @@ export const CARRIER_CATALOG = {
|
|
|
944
944
|
],
|
|
945
945
|
"detectionRules": [
|
|
946
946
|
{
|
|
947
|
-
"pattern": "^06
|
|
947
|
+
"pattern": "^06[01]\\d{11}$",
|
|
948
948
|
"confidence": "low",
|
|
949
949
|
"preferred": true
|
|
950
950
|
},
|
|
@@ -1313,6 +1313,21 @@ export const CARRIER_CATALOG = {
|
|
|
1313
1313
|
"tracking": {
|
|
1314
1314
|
"mode": "automatic",
|
|
1315
1315
|
"adapter": "colis-prive",
|
|
1316
|
+
"requirements": [
|
|
1317
|
+
{
|
|
1318
|
+
"field": "postcode",
|
|
1319
|
+
"whenTrackingNumber": "^[A-Z0-9]{12}$",
|
|
1320
|
+
"validator": "colisPrivePostcode",
|
|
1321
|
+
"label": "Delivery postcode",
|
|
1322
|
+
"type": "text",
|
|
1323
|
+
"placeholder": "75001",
|
|
1324
|
+
"help": "Colis Privé needs the recipient postcode with the 12-character number: 5 digits in France, 4 in Belgium, or L and 4 digits in Luxembourg.",
|
|
1325
|
+
"pattern": "^(?:(?:0[1-9]|[1-8][0-9]|9[0-578])[0-9]{3}|(?:(?:[Bb][Ee]?|[Ll][Uu]?)[ \\-]?)?[1-9][0-9]{3})$",
|
|
1326
|
+
"maxLength": 7,
|
|
1327
|
+
"inputMode": "text",
|
|
1328
|
+
"autoComplete": "postal-code"
|
|
1329
|
+
}
|
|
1330
|
+
],
|
|
1316
1331
|
"recognitionRank": 30
|
|
1317
1332
|
},
|
|
1318
1333
|
"canaryUrl": "https://colisprive.com/moncolis/pages/DetailColis.aspx",
|
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.
|
|
3
|
+
"version": "0.70.0-main.901",
|
|
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",
|