universal-parcel-scraper 0.77.0-main.937 → 0.78.0-main.939

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
@@ -13,7 +13,7 @@ The engine behind [Peek](https://peektracker.com) ([GitHub](https://github.com/p
13
13
  [![License: Apache-2.0](https://img.shields.io/badge/core-Apache--2.0-blue.svg)](LICENSE)
14
14
 
15
15
  <!-- GENERATED:summary -->
16
- **3,500+ carriers** through **134 dedicated adapters** and **5 universal fallbacks**
16
+ **3,500+ carriers** through **135 dedicated adapters** and **5 universal fallbacks**
17
17
 
18
18
  <sub>207 catalog entries · 66 countries represented</sub>
19
19
  <!-- /GENERATED:summary -->
@@ -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,732 such statuses, each filed under one stage.
158
+ The carrier folders record 3,852 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
@@ -7194,7 +7194,8 @@
7194
7194
  "timezone": "Europe/Tallinn",
7195
7195
  "tracking": {
7196
7196
  "mode": "automatic",
7197
- "adapter": "universal"
7197
+ "adapter": "omniva",
7198
+ "recognitionRank": 64
7198
7199
  },
7199
7200
  "canaryUrl": "https://minu.omniva.ee/track",
7200
7201
  "trackingUrlTemplate": "https://minu.omniva.ee/track/{trackingNumber}",
@@ -7230,6 +7231,12 @@
7230
7231
  {
7231
7232
  "pattern": "^JJEE[A-Z]{3}\\d{11}$",
7232
7233
  "confidence": "high"
7234
+ },
7235
+ {
7236
+ "pattern": "^[A-Z]{2}\\d{9}EE$",
7237
+ "confidence": "low",
7238
+ "checksum": "s10",
7239
+ "preferred": true
7233
7240
  }
7234
7241
  ]
7235
7242
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "catalog": 207,
3
- "activeAdapters": 134,
3
+ "activeAdapters": 135,
4
4
  "countries": 66,
5
5
  "referenceCarriers": 100,
6
6
  "comparison": {
@@ -25371,6 +25371,7 @@
25371
25371
  "intl-post"
25372
25372
  ],
25373
25373
  "asked": [
25374
+ "omniva",
25374
25375
  "chronopost"
25375
25376
  ]
25376
25377
  },
@@ -25382,6 +25383,7 @@
25382
25383
  "intl-post"
25383
25384
  ],
25384
25385
  "asked": [
25386
+ "omniva",
25385
25387
  "chronopost"
25386
25388
  ]
25387
25389
  },
@@ -25599,6 +25601,7 @@
25599
25601
  "intl-post"
25600
25602
  ],
25601
25603
  "asked": [
25604
+ "omniva",
25602
25605
  "chronopost"
25603
25606
  ]
25604
25607
  },
@@ -25634,6 +25637,7 @@
25634
25637
  "intl-post"
25635
25638
  ],
25636
25639
  "asked": [
25640
+ "omniva",
25637
25641
  "chronopost"
25638
25642
  ]
25639
25643
  },
@@ -25653,6 +25657,7 @@
25653
25657
  "intl-post"
25654
25658
  ],
25655
25659
  "asked": [
25660
+ "omniva",
25656
25661
  "chronopost"
25657
25662
  ]
25658
25663
  },
@@ -25683,6 +25688,7 @@
25683
25688
  "intl-post"
25684
25689
  ],
25685
25690
  "asked": [
25691
+ "omniva",
25686
25692
  "chronopost"
25687
25693
  ]
25688
25694
  },
@@ -26573,6 +26579,7 @@
26573
26579
  "intl-post"
26574
26580
  ],
26575
26581
  "asked": [
26582
+ "omniva",
26576
26583
  "chronopost"
26577
26584
  ]
26578
26585
  },
@@ -26724,6 +26731,7 @@
26724
26731
  "intl-post"
26725
26732
  ],
26726
26733
  "asked": [
26734
+ "omniva",
26727
26735
  "chronopost"
26728
26736
  ]
26729
26737
  },
@@ -30508,6 +30516,7 @@
30508
30516
  "intl-post"
30509
30517
  ],
30510
30518
  "asked": [
30519
+ "omniva",
30511
30520
  "chronopost"
30512
30521
  ]
30513
30522
  },
@@ -32984,6 +32993,7 @@
32984
32993
  "intl-post"
32985
32994
  ],
32986
32995
  "asked": [
32996
+ "omniva",
32987
32997
  "chronopost"
32988
32998
  ]
32989
32999
  },
@@ -33006,6 +33016,7 @@
33006
33016
  "intl-post"
33007
33017
  ],
33008
33018
  "asked": [
33019
+ "omniva",
33009
33020
  "chronopost"
33010
33021
  ]
33011
33022
  },
@@ -33017,6 +33028,7 @@
33017
33028
  "intl-post"
33018
33029
  ],
33019
33030
  "asked": [
33031
+ "omniva",
33020
33032
  "chronopost"
33021
33033
  ]
33022
33034
  },
@@ -33424,6 +33436,7 @@
33424
33436
  "intl-post"
33425
33437
  ],
33426
33438
  "asked": [
33439
+ "omniva",
33427
33440
  "chronopost"
33428
33441
  ]
33429
33442
  },
@@ -33435,6 +33448,7 @@
33435
33448
  "intl-post"
33436
33449
  ],
33437
33450
  "asked": [
33451
+ "omniva",
33438
33452
  "chronopost"
33439
33453
  ]
33440
33454
  },
@@ -33446,6 +33460,7 @@
33446
33460
  "intl-post"
33447
33461
  ],
33448
33462
  "asked": [
33463
+ "omniva",
33449
33464
  "chronopost"
33450
33465
  ]
33451
33466
  },
@@ -33457,6 +33472,7 @@
33457
33472
  "intl-post"
33458
33473
  ],
33459
33474
  "asked": [
33475
+ "omniva",
33460
33476
  "chronopost"
33461
33477
  ]
33462
33478
  },
@@ -33468,6 +33484,7 @@
33468
33484
  "intl-post"
33469
33485
  ],
33470
33486
  "asked": [
33487
+ "omniva",
33471
33488
  "chronopost"
33472
33489
  ]
33473
33490
  },
@@ -5715,7 +5715,8 @@ var ye = {
5715
5715
  timezone: "Europe/Tallinn",
5716
5716
  tracking: {
5717
5717
  mode: "automatic",
5718
- adapter: "universal"
5718
+ adapter: "omniva",
5719
+ recognitionRank: 64
5719
5720
  },
5720
5721
  canaryUrl: "https://minu.omniva.ee/track",
5721
5722
  trackingUrlTemplate: "https://minu.omniva.ee/track/{trackingNumber}",
@@ -5735,13 +5736,22 @@ var ye = {
5735
5736
  pathPattern: "^/(?:era/jalgimine|abi/jalgimine|verslo/siuntos_sekimas)/?$",
5736
5737
  params: ["barcode"]
5737
5738
  }],
5738
- detectionRules: [{
5739
- pattern: "^JJEE\\d{17}$",
5740
- confidence: "high"
5741
- }, {
5742
- pattern: "^JJEE[A-Z]{3}\\d{11}$",
5743
- confidence: "high"
5744
- }]
5739
+ detectionRules: [
5740
+ {
5741
+ pattern: "^JJEE\\d{17}$",
5742
+ confidence: "high"
5743
+ },
5744
+ {
5745
+ pattern: "^JJEE[A-Z]{3}\\d{11}$",
5746
+ confidence: "high"
5747
+ },
5748
+ {
5749
+ pattern: "^[A-Z]{2}\\d{9}EE$",
5750
+ confidence: "low",
5751
+ checksum: "s10",
5752
+ preferred: !0
5753
+ }
5754
+ ]
5745
5755
  },
5746
5756
  "orlen-paczka": {
5747
5757
  displayName: "ORLEN Paczka",
@@ -6976,7 +6986,11 @@ var ye = {
6976
6986
  nationex: [],
6977
6987
  "nova-poshta": ["nova-poshta-domestic"],
6978
6988
  omgo: ["omgo-1"],
6979
- omniva: ["omniva-1", "omniva-2"],
6989
+ omniva: [
6990
+ "omniva-1",
6991
+ "omniva-2",
6992
+ "omniva-s10"
6993
+ ],
6980
6994
  "orlen-paczka": ["orlen-paczka-1"],
6981
6995
  "osm-worldwide": ["osm-worldwide-1"],
6982
6996
  paquetexpress: [],
@@ -11738,6 +11752,7 @@ var _c = {
11738
11752
  "ninja-van": 28,
11739
11753
  "nz-post": 32,
11740
11754
  omgo: 1,
11755
+ omniva: 64,
11741
11756
  ontrac: 21,
11742
11757
  "orlen-paczka": 49,
11743
11758
  "poczta-polska": 31,
@@ -0,0 +1,14 @@
1
+ import { type AdapterFactory, type TrackingContext } from '../../core/adapter/index.js';
2
+ import type { CarrierResult } from '../../core/result/index.js';
3
+ import { type StepRecorder } from '../../core/telemetry/index.js';
4
+ export declare const OMNIVA_ENDPOINT = "https://mobile-be.omniva.ee/api/v2/shipment/trackSingleShipment/";
5
+ export declare class OmnivaTracker {
6
+ private readonly options;
7
+ constructor(options?: {
8
+ fetcher?: typeof fetch;
9
+ recorder?: StepRecorder;
10
+ userAgent?: string;
11
+ });
12
+ fetch(raw: string, context?: TrackingContext): Promise<CarrierResult>;
13
+ }
14
+ export declare const adapter: AdapterFactory;
@@ -0,0 +1,60 @@
1
+ import { accepted, recognizeFromLookup } from '../../core/adapter/index.js';
2
+ import { ChallengeError, SchemaError, TransportError, UpstreamHttpError } from '../../core/errors/index.js';
3
+ import { runSteps } from '../../core/runner/index.js';
4
+ import { firstAttemptMs, networkRetryStep } from '../../core/runner/networkRetry.js';
5
+ import { NOOP_RECORDER } from '../../core/telemetry/index.js';
6
+ import { decodeText, fetchBounded, parseJsonBytes, userAgentOf } from '../../core/transport/index.js';
7
+ import { normalizeOmnivaNumber, OMNIVA_MAX_BYTES, parseOmniva, PROVIDER } from './parser.js';
8
+ // The guest lookup of Omniva's app. The web trackers' own API wants a
9
+ // Turnstile token; this one answers without a key, cookie or token.
10
+ export const OMNIVA_ENDPOINT = 'https://mobile-be.omniva.ee/api/v2/shipment/trackSingleShipment/';
11
+ // A Cloudflare challenge, or the application gateway's own 403 page.
12
+ const BLOCKED = /<title>\s*(?:Just a moment|Attention Required|403 Forbidden)|cf-chl-|challenge-platform|cf-turnstile|Application-Gateway/i;
13
+ export class OmnivaTracker {
14
+ options;
15
+ constructor(options = {}) {
16
+ this.options = options;
17
+ }
18
+ async fetch(raw, context = {}) {
19
+ const number = normalizeOmnivaNumber(raw);
20
+ const read = async (signal, timeoutMs) => {
21
+ let bytes;
22
+ try {
23
+ ({ bytes } = await fetchBounded(OMNIVA_ENDPOINT + encodeURIComponent(number), { signal,
24
+ headers: { Accept: 'application/json', 'User-Agent': userAgentOf(this.options.userAgent) } }, { provider: PROVIDER, timeoutMs: Math.max(1, Math.floor(timeoutMs)), maxBytes: OMNIVA_MAX_BYTES, fetcher: this.options.fetcher }));
25
+ }
26
+ catch (error) {
27
+ // An unknown number is an empty object; a missing route is not a missing item.
28
+ if (error instanceof UpstreamHttpError && [404, 410].includes(error.status)) {
29
+ throw new TransportError(PROVIDER, 'Omniva tracking endpoint is unavailable', { cause: error });
30
+ }
31
+ throw error;
32
+ }
33
+ let payload;
34
+ try {
35
+ payload = parseJsonBytes(bytes, PROVIDER);
36
+ }
37
+ catch (cause) {
38
+ if (BLOCKED.test(decodeText(bytes.subarray(0, 64_000))))
39
+ throw new ChallengeError(PROVIDER, 'Omniva answered with a block page', { cause });
40
+ throw new SchemaError(PROVIDER, 'Omniva returned invalid tracking JSON', { cause });
41
+ }
42
+ return parseOmniva(payload, number);
43
+ };
44
+ // A request that fails to reach Omniva, or hangs, gets one more try: the
45
+ // first gets half of the budget, so the retry fits in the rest.
46
+ return runSteps({ carrier: 'omniva', budgetMs: context.budgetMs ?? 15_000, signal: context.signal,
47
+ recorder: this.options.recorder ?? NOOP_RECORDER }, [
48
+ { id: 'direct', run: ({ signal, remainingMs }) => read(signal, firstAttemptMs(remainingMs)) },
49
+ networkRetryStep(({ signal, remainingMs }) => read(signal, remainingMs)),
50
+ ]);
51
+ }
52
+ }
53
+ export const adapter = (environment) => {
54
+ const tracker = new OmnivaTracker({ fetcher: environment.fetcher, recorder: environment.recorder, userAgent: environment.userAgent });
55
+ return {
56
+ id: 'omniva', recordsSteps: true, steps: ['direct', 'retry'],
57
+ track: (input, context) => tracker.fetch(input.number, context),
58
+ recognize: (number, context) => recognizeFromLookup(() => tracker.fetch(number, context), () => accepted(() => normalizeOmnivaNumber(number))),
59
+ };
60
+ };
@@ -19,18 +19,54 @@
19
19
  "timezone": "Europe/Tallinn",
20
20
  "tracking": {
21
21
  "mode": "automatic",
22
- "adapter": "universal",
23
- "steps": []
22
+ "adapter": "omniva",
23
+ "steps": [
24
+ "direct",
25
+ "retry"
26
+ ],
27
+ "recognition": {
28
+ "rank": 64
29
+ }
24
30
  },
25
- "capabilities": [],
31
+ "capabilities": [
32
+ "history",
33
+ "location",
34
+ "provider_code",
35
+ "delivered_at",
36
+ "pickup_point",
37
+ "destination_country"
38
+ ],
26
39
  "portal": {
27
40
  "url": "https://minu.omniva.ee/track/{trackingNumber}",
28
41
  "searchUrl": "https://minu.omniva.ee/track",
29
42
  "canaryUrl": "https://minu.omniva.ee/track",
30
- "shows": [],
31
- "scraped": [],
32
- "discarded": [],
33
- "unavailable": []
43
+ "shows": [
44
+ "status",
45
+ "history",
46
+ "location",
47
+ "eta",
48
+ "delivered_at",
49
+ "pickup_point"
50
+ ],
51
+ "scraped": [
52
+ "status",
53
+ "history",
54
+ "location",
55
+ "provider_code",
56
+ "delivered_at",
57
+ "pickup_point",
58
+ "destination_country"
59
+ ],
60
+ "discarded": [
61
+ "postcodes",
62
+ "event_details"
63
+ ],
64
+ "unavailable": [
65
+ "eta",
66
+ "weight",
67
+ "service_name",
68
+ "proof_of_delivery"
69
+ ]
34
70
  },
35
71
  "links": [
36
72
  {
@@ -65,6 +101,13 @@
65
101
  "id": "omniva-2",
66
102
  "pattern": "^JJEE[A-Z]{3}\\d{11}$",
67
103
  "confidence": "high"
104
+ },
105
+ {
106
+ "id": "omniva-s10",
107
+ "pattern": "^[A-Z]{2}\\d{9}EE$",
108
+ "confidence": "low",
109
+ "preferred": true,
110
+ "checksum": "s10"
68
111
  }
69
112
  ]
70
113
  }
@@ -133,7 +133,7 @@
133
133
  "carrier": "unknown",
134
134
  "confidence": "none"
135
135
  },
136
- "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows; the capture was not opened. Omniva documents no layout of ten characters starting ZA."
136
+ "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows, and an archived reply of the tracker's API holds its history. Omniva documents no layout of ten characters starting ZA, so no rule claims it."
137
137
  },
138
138
  {
139
139
  "number": "CE213390194EE",
@@ -165,13 +165,13 @@
165
165
  "access": "indexed_excerpt"
166
166
  },
167
167
  "context": {
168
- "assessment": "candidate"
168
+ "assessment": "confirmed"
169
169
  },
170
170
  "expect": {
171
171
  "carrier": "intl-post",
172
172
  "confidence": "high"
173
173
  },
174
- "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows; the capture was not opened."
174
+ "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows. Omniva's tracking service answers for it; detection selects the generic postal carrier."
175
175
  },
176
176
  {
177
177
  "number": "LL002789060EE",
@@ -184,13 +184,13 @@
184
184
  "access": "indexed_excerpt"
185
185
  },
186
186
  "context": {
187
- "assessment": "candidate"
187
+ "assessment": "confirmed"
188
188
  },
189
189
  "expect": {
190
190
  "carrier": "intl-post",
191
191
  "confidence": "high"
192
192
  },
193
- "note": "Looked up on Omniva's own Latvian tracker, as an archived address shows; the capture was not opened."
193
+ "note": "Looked up on Omniva's own Latvian tracker, as an archived address shows. Omniva's tracking service answers for it; detection selects the generic postal carrier."
194
194
  },
195
195
  {
196
196
  "number": "CC404213205EE",
@@ -203,13 +203,13 @@
203
203
  "access": "indexed_excerpt"
204
204
  },
205
205
  "context": {
206
- "assessment": "candidate"
206
+ "assessment": "confirmed"
207
207
  },
208
208
  "expect": {
209
209
  "carrier": "intl-post",
210
210
  "confidence": "high"
211
211
  },
212
- "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows; the capture was not opened."
212
+ "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows. Omniva's tracking service answers for it; detection selects the generic postal carrier."
213
213
  },
214
214
  {
215
215
  "number": "JJEE51086300000202035",
@@ -222,13 +222,13 @@
222
222
  "access": "indexed_excerpt"
223
223
  },
224
224
  "context": {
225
- "assessment": "candidate"
225
+ "assessment": "confirmed"
226
226
  },
227
227
  "expect": {
228
228
  "carrier": "omniva",
229
229
  "confidence": "high"
230
230
  },
231
- "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows; the capture was not opened."
231
+ "note": "Looked up on Omniva's own Lithuanian tracker, as an archived address shows. Omniva's tracking service returns its history."
232
232
  }
233
233
  ]
234
234
  }
@@ -0,0 +1,15 @@
1
+ import type { CarrierResult } from '../../core/result/index.js';
2
+ export declare const PROVIDER = "Omniva";
3
+ export declare const OMNIVA_MAX_BYTES = 512000;
4
+ /**
5
+ * Omniva tracks UPU S10 items, its own and those from other posts, and its
6
+ * parcel ids: JJEE and seventeen digits, or JJEE, three letters and eleven
7
+ * digits. Its Lithuanian tracker also answered ZA and eight digits.
8
+ */
9
+ export declare function normalizeOmnivaNumber(raw: string): string;
10
+ /**
11
+ * The tracking service's reply for one number: its scans in no set order,
12
+ * each with an event code, English wording and a UTC clock, and the item's
13
+ * state. A number it does not know comes back as an empty object.
14
+ */
15
+ export declare function parseOmniva(payload: unknown, rawNumber: string): CarrierResult;
@@ -0,0 +1,149 @@
1
+ import { isValidS10TrackingNumber, normalizeTrackingNumber } from '../../core/detection/index.js';
2
+ import { IndeterminateError, InvalidInputError, NotFoundError, SchemaError } from '../../core/errors/index.js';
3
+ import { lastMovement } from '../../core/result/pickup.js';
4
+ import { languageStageStatus } from '../../core/status/index.js';
5
+ import { countryCode, explicitOffsetTime } from '../../core/time/index.js';
6
+ import { clean } from '../../core/transport/index.js';
7
+ import { isRecord } from '../../core/types.js';
8
+ import { isOmnivaNotice, omnivaEventStage, omnivaState, RETURN_CANCELLED, RETURN_STARTS } from './status.js';
9
+ export const PROVIDER = 'Omniva';
10
+ // A long history is a few tens of kilobytes.
11
+ export const OMNIVA_MAX_BYTES = 512_000;
12
+ const MAX_ROWS = 500;
13
+ const MAX_EVENTS = 100;
14
+ const CODE = /^[A-Z][A-Z0-9_]{1,79}$/;
15
+ /**
16
+ * Omniva tracks UPU S10 items, its own and those from other posts, and its
17
+ * parcel ids: JJEE and seventeen digits, or JJEE, three letters and eleven
18
+ * digits. Its Lithuanian tracker also answered ZA and eight digits.
19
+ */
20
+ export function normalizeOmnivaNumber(raw) {
21
+ const number = normalizeTrackingNumber(raw);
22
+ if (/^JJEE(?:\d{17}|[A-Z]{3}\d{11})$/.test(number) || /^ZA\d{8}$/.test(number) || isValidS10TrackingNumber(number))
23
+ return number;
24
+ throw new InvalidInputError(PROVIDER, 'Omniva tracks S10 item codes and its JJEE parcel ids');
25
+ }
26
+ // Omniva's own and its partners' sites, by their service point type. Other
27
+ // types are a sender's or recipient's address or a home letterbox.
28
+ const PLACES = new Set(['PARCEL_MACHINE', 'POST_OFFICE', 'POST_STATION', 'PICK_UP_POINT', 'PARTNER_PICK_UP_POINT', 'PARCEL_BOX',
29
+ 'DISTRIBUTION_POINT', 'LOGISTIC_CENTER', 'WORKSTATION', 'IMPC', 'WAREHOUSE', 'SUPPORT_POINT']);
30
+ // The sites a recipient collects an item from.
31
+ const PICKUP = new Set(['PARCEL_MACHINE', 'POST_OFFICE', 'POST_STATION', 'PICK_UP_POINT', 'PARTNER_PICK_UP_POINT', 'PARCEL_BOX']);
32
+ /** A site's English name, else its own, as the trackers show it. Postcodes are left out. */
33
+ function placeOf(value) {
34
+ if (!isRecord(value))
35
+ return null;
36
+ const type = clean(value.servicePointTypeCode, 64);
37
+ if (!PLACES.has(type))
38
+ return null;
39
+ const english = isRecord(value.translations) && isRecord(value.translations.en) ? clean(value.translations.en.name, 160) : '';
40
+ const name = english || clean(value.locationName, 160);
41
+ // A sorting workstation can carry an internal code, such as IPS_GENERAL, for a name.
42
+ return name && !/^[A-Z0-9]+(?:_[A-Z0-9]+)+$/.test(name) ? { name, type } : null;
43
+ }
44
+ /**
45
+ * The tracking service's reply for one number: its scans in no set order,
46
+ * each with an event code, English wording and a UTC clock, and the item's
47
+ * state. A number it does not know comes back as an empty object.
48
+ */
49
+ export function parseOmniva(payload, rawNumber) {
50
+ const number = normalizeOmnivaNumber(rawNumber);
51
+ if (!isRecord(payload))
52
+ throw new SchemaError(PROVIDER, 'Omniva did not return a shipment');
53
+ if (!Object.keys(payload).length)
54
+ throw new NotFoundError(PROVIDER);
55
+ if (typeof payload.shipmentBarcode !== 'string' || payload.shipmentBarcode.trim().toUpperCase() !== number) {
56
+ throw new SchemaError(PROVIDER, 'Omniva answered for a different shipment');
57
+ }
58
+ const rows = payload.events ?? [];
59
+ if (!Array.isArray(rows) || rows.length > MAX_ROWS)
60
+ throw new SchemaError(PROVIDER, 'Omniva returned an invalid shipment history');
61
+ const stateCode = clean(payload.currentStateCode, 80);
62
+ if (stateCode && !CODE.test(stateCode))
63
+ throw new SchemaError(PROVIDER, 'Omniva returned an invalid shipment state');
64
+ const scans = [];
65
+ const seen = new Set();
66
+ rows.forEach((row, index) => {
67
+ if (!isRecord(row))
68
+ throw new SchemaError(PROVIDER, 'Omniva returned an invalid scan');
69
+ const code = clean(row.eventCode, 80);
70
+ const at = explicitOffsetTime(row.eventDate);
71
+ if (!CODE.test(code) || !at)
72
+ throw new SchemaError(PROVIDER, 'Omniva returned a scan without a code or clock');
73
+ const place = placeOf(row.location);
74
+ const event = { time: at.iso, description: clean(row.eventName, 300) || code, provider_code: code,
75
+ ...(place ? { location: place.name } : {}) };
76
+ const identity = JSON.stringify(event);
77
+ if (seen.has(identity))
78
+ return;
79
+ seen.add(identity);
80
+ scans.push({ event, at: at.timestamp, code, place, index });
81
+ });
82
+ // Newest first; scans at the same instant follow the reply's order, reversed.
83
+ scans.sort((left, right) => right.at - left.at || right.index - left.index);
84
+ // Oldest first: a notice keeps the stage of the scan before it, and a
85
+ // delivery on a return reaches the sender. The reply flags a return even
86
+ // when its history no longer holds the scan that started it.
87
+ const returnFlag = payload.returningShipment === true;
88
+ let returning = returnFlag && !scans.some(scan => RETURN_STARTS.has(scan.code));
89
+ let previous;
90
+ for (const scan of [...scans].reverse()) {
91
+ if (RETURN_STARTS.has(scan.code))
92
+ returning = true;
93
+ else if (scan.code === RETURN_CANCELLED)
94
+ returning = false;
95
+ if (isOmnivaNotice(scan.code)) {
96
+ if (previous)
97
+ Object.assign(scan.event, { stage: previous, stage_source: 'none' });
98
+ continue;
99
+ }
100
+ const mapped = omnivaEventStage(scan.code);
101
+ const stage = mapped === 'delivered' && returning ? 'returned' : mapped;
102
+ if (stage)
103
+ Object.assign(scan.event, { stage, stage_source: 'carrier_map' });
104
+ previous = stage;
105
+ }
106
+ const events = scans.map(scan => scan.event);
107
+ const state = stateCode ? omnivaState(stateCode) : undefined;
108
+ // The trackers headline the state. A delivered state on a return is the sender's collection.
109
+ const stateStage = state?.stage === 'delivered' && returnFlag ? 'returned' : state?.stage;
110
+ const decider = scans.find(scan => !isOmnivaNotice(scan.code)) ?? scans[0];
111
+ if (!decider && !stateCode)
112
+ throw new IndeterminateError(PROVIDER, 'Omniva returned a shipment without a state or scans');
113
+ const stage = decider?.event.stage ?? stateStage;
114
+ const fromState = Boolean(stage) && !decider?.event.stage;
115
+ // The site an item waits at is its pickup point; a delivered item was
116
+ // collected there when it was delivered at one, or waited there last. The
117
+ // recipient's chosen site stands in while the scan names none. A return's
118
+ // site is the sender's.
119
+ const deliveryPlace = placeOf(payload.deliveryLocation);
120
+ let point = '';
121
+ if (!returning && !returnFlag) {
122
+ if (stage === 'ready_for_pickup') {
123
+ point = (decider?.place && PICKUP.has(decider.place.type) ? decider.place : deliveryPlace && PICKUP.has(deliveryPlace.type) ? deliveryPlace : null)?.name ?? '';
124
+ }
125
+ else if (stage === 'delivered' && decider) {
126
+ const earlier = scans.slice(scans.indexOf(decider) + 1);
127
+ const waited = lastMovement(earlier.map(scan => scan.event)) === 'ready_for_pickup'
128
+ ? earlier.find(scan => scan.event.stage === 'ready_for_pickup' && !isOmnivaNotice(scan.code))?.place : null;
129
+ point = [decider.place, waited].find(place => place && PICKUP.has(place.type))?.name ?? '';
130
+ }
131
+ }
132
+ const destination = isRecord(payload.deliveryLocation) ? countryCode(payload.deliveryLocation.countryCode) : null;
133
+ const delivered = stage === 'delivered' && decider?.event.stage === 'delivered' ? decider.event.time : undefined;
134
+ // An old item's history keeps only its delivery.
135
+ const trimmed = events.length > 0 && events.every(event => event.stage === 'delivered' || event.stage === 'returned');
136
+ return {
137
+ status: stage ? languageStageStatus(stage) : 'unknown',
138
+ ...(stage ? { current_stage: stage, current_stage_source: fromState ? 'carrier_map' : decider.event.stage_source } : {}),
139
+ last_status_text: decider?.event.description ?? state?.wording ?? stateCode,
140
+ ...(events.length ? {} : { provider_code: stateCode, summary_only: true }),
141
+ last_update: events[0]?.time ?? null,
142
+ expected_delivery: null,
143
+ ...(delivered ? { delivered_at: delivered } : {}),
144
+ ...(point ? { pickup_point: point } : {}),
145
+ ...(destination ? { destination_country: destination } : {}),
146
+ events: events.slice(0, MAX_EVENTS),
147
+ ...(events.length > MAX_EVENTS || trimmed ? { history_truncated: true } : {}),
148
+ };
149
+ }
@@ -0,0 +1,15 @@
1
+ import type { Stage } from '../../core/status/index.js';
2
+ import type { CarrierStatusMap } from '../../core/status/statusMap.js';
3
+ /** Scans that start a return: a delivery after one reaches the sender. */
4
+ export declare const RETURN_STARTS: ReadonlySet<string>;
5
+ /** The scan that calls a return off. */
6
+ export declare const RETURN_CANCELLED = "TRT_SHIPMENT_RETURN_CANCELLED";
7
+ export declare function omnivaEventStage(code: string): Stage | undefined;
8
+ export declare function isOmnivaNotice(code: string): boolean;
9
+ /** The English wording and stage of an item's state, if the map knows it. */
10
+ export declare function omnivaState(code: string): {
11
+ wording: string;
12
+ stage: Stage;
13
+ } | undefined;
14
+ /** Event codes and state codes do not overlap: events carry a TRT or CARGO prefix. */
15
+ export declare const statusMap: CarrierStatusMap;
@@ -0,0 +1,164 @@
1
+ // Omniva names each scan with an event code from its trackers' dictionary and
2
+ // words it in English. TRT codes are its own network's, with the UPU EDI
3
+ // events of a cross-border item under their UPU letters; CARGO codes come from
4
+ // its freight partners.
5
+ const EVENTS = {
6
+ TRT_SHIPMENT_REGISTERED: 'registered',
7
+ TRT_SHIPMENT_REGISTERED_IN_OMNIVA: 'registered',
8
+ TRT_SHIPMENT_HANDOVER_TO_PM: 'accepted',
9
+ TRT_SHIPMENT_HANDOVER_TO_PO: 'accepted',
10
+ TRT_SHIPMENT_PICKED_UP_BY_COURIER: 'accepted',
11
+ TRT_SHIPMENT_EMA: 'accepted',
12
+ TRT_SHIPMENT_DEPARTED_FROM_PM: 'in_transit',
13
+ TRT_SHIPMENT_ARRIVED_TO_PO: 'in_transit',
14
+ TRT_SHIPMENT_SORTED: 'in_transit',
15
+ TRT_SHIPMENT_REDIRECTION: 'in_transit',
16
+ TRT_SHIPMENT_MOVEMENT_RESTORED: 'in_transit',
17
+ // The return is called off and the item goes on to its recipient.
18
+ TRT_SHIPMENT_RETURN_CANCELLED: 'in_transit',
19
+ TRT_SHIPMENT_RELEASED_FROM_CUSTOMS: 'in_transit',
20
+ TRT_SHIPMENT_EMB: 'in_transit',
21
+ TRT_SHIPMENT_EMC: 'in_transit',
22
+ TRT_SHIPMENT_EMD: 'in_transit',
23
+ TRT_SHIPMENT_EMF: 'in_transit',
24
+ TRT_SHIPMENT_EMG: 'in_transit',
25
+ TRT_SHIPMENT_EMJ: 'in_transit',
26
+ TRT_SHIPMENT_EDC: 'in_transit',
27
+ TRT_SHIPMENT_EDD: 'in_transit',
28
+ TRT_SHIPMENT_EDE: 'in_transit',
29
+ CARGO_SHIPMENT_ARRIVED_TO_TRANSIT_HUB: 'in_transit',
30
+ CARGO_SHIPMENT_DEPARTED_FROM_TRANSIT_HUB: 'in_transit',
31
+ CARGO_SHIPMENT_IN_TRANSIT_TO_DESTINATION_COUNTRY: 'in_transit',
32
+ CARGO_SHIPMENT_ARRIVING_TO_DESTINATION_COUNTRY: 'in_transit',
33
+ CARGO_SHIPMENT_ARRIVED_TO_DESTINATION_COUNTRY: 'in_transit',
34
+ CARGO_SHIPMENT_ARRIVED_TO_CUSTOMS_CLEARANCE_HUB: 'in_transit',
35
+ CARGO_SHIPMENT_RELEASED_FROM_CUSTOMS: 'in_transit',
36
+ CARGO_SHIPMENT_HANDOVER_FROM_LINEHAUL_EVENT: 'in_transit',
37
+ CARGO_SHIPMENT_HANDED_TO_LAST_MILE: 'in_transit',
38
+ CARGO_SHIPMENT_ARRIVED_TO_SORTING: 'in_transit',
39
+ CARGO_SHIPMENT_DEPARTED_FROM_SORTING: 'in_transit',
40
+ CARGO_SHIPMENT_ARRIVED_TO_LAST_DELIVERY_OFFICE: 'in_transit',
41
+ CARGO_SHIPMENT_DEPARTED_FROM_RETURNING_COUNTRY: 'in_transit',
42
+ CARGO_SHIPMENT_ARRIVED_TO_RETURNING_COUNTRY_HUB: 'in_transit',
43
+ TRT_SHIPMENT_TAKEN_TO_CUSTOMS: 'customs',
44
+ TRT_SHIPMENT_HELD_IN_CUSTOMS: 'customs',
45
+ TRT_SHIPMENT_ASSIGN_CUSTOMS_INSPECTION: 'customs',
46
+ // An item from outside the EU waits at customs for its recipient's
47
+ // declaration or payment; these notices ask for them.
48
+ TRT_SHIPMENT_ARRIVAL_CUSTOMS_NOTIFICATION: 'customs',
49
+ TRT_SHIPMENT_CD_REMINDER_NOTIFICATION: 'customs',
50
+ TRT_SHIPMENT_CD_ADD_PAYMENT_NOTIFICATION: 'customs',
51
+ TRT_SHIPMENT_CD_ADD_PAYMENT_REMINDER_NOTIFICATION: 'customs',
52
+ TRT_SHIPMENT_CUSTOMS_PAYMENT_REQ_REMINDER: 'customs',
53
+ TRT_SHIPMENT_EDB: 'customs',
54
+ TRT_SHIPMENT_EME: 'customs',
55
+ CARGO_SHIPMENT_PRESENTED_TO_CUSTOMS: 'customs',
56
+ CARGO_SHIPMENT_HELD_IN_CUSTOMS: 'customs',
57
+ TRT_SHIPMENT_COURIER_DELIVERY_STARTED: 'out_for_delivery',
58
+ TRT_SHIPMENT_EDG: 'out_for_delivery',
59
+ CARGO_SHIPMENT_OUT_FOR_DELIVERY: 'out_for_delivery',
60
+ TRT_SHIPMENT_FAILED_DELIVERY_ATTEMPT: 'failed_attempt',
61
+ TRT_SHIPMENT_EMH: 'failed_attempt',
62
+ CARGO_SHIPMENT_DELIVERY_FAILED: 'failed_attempt',
63
+ TRT_SHIPMENT_STORED_IN_PM: 'ready_for_pickup',
64
+ TRT_SHIPMENT_ARRIVED_TO_FINAL_PO: 'ready_for_pickup',
65
+ TRT_SHIPMENT_EDH: 'ready_for_pickup',
66
+ TRT_SHIPMENT_DELIVERED: 'delivered',
67
+ TRT_SHIPMENT_EMI: 'delivered',
68
+ CARGO_SHIPMENT_DELIVERED: 'delivered',
69
+ CARGO_SHIPMENT_RETURNED: 'returned',
70
+ // A return under way: the item is not delivered and not back yet.
71
+ TRT_SHIPMENT_RETURN: 'exception',
72
+ CARGO_SHIPMENT_POSTED_IN_RETURNING_COUNTRY: 'exception',
73
+ TRT_SHIPMENT_CANCELLED: 'exception',
74
+ TRT_SHIPMENT_MOVEMENT_STOPPED: 'exception',
75
+ TRT_SHIPMENT_MISSING_IN_PM: 'exception',
76
+ TRT_SHIPMENT_PREDECLARED_MISSING: 'exception',
77
+ TRT_SHIPMENT_PROHIBIT: 'exception',
78
+ TRT_SHIPMENT_EDX: 'exception',
79
+ TRT_SHIPMENT_EXX: 'exception',
80
+ CARGO_SHIPMENT_CANCELLED: 'exception',
81
+ CARGO_SHIPMENT_DESTROYED: 'exception',
82
+ CARGO_SHIPMENT_MISSING_IN_PM: 'exception',
83
+ CARGO_SHIPMENT_NOT_ARRIVED_IN_BAG_EVENT: 'exception',
84
+ };
85
+ /** Scans that start a return: a delivery after one reaches the sender. */
86
+ export const RETURN_STARTS = new Set(['TRT_SHIPMENT_RETURN', 'CARGO_SHIPMENT_POSTED_IN_RETURNING_COUNTRY']);
87
+ /** The scan that calls a return off. */
88
+ export const RETURN_CANCELLED = 'TRT_SHIPMENT_RETURN_CANCELLED';
89
+ // Messages to the recipient and bookkeeping: no movement, so the item keeps its stage.
90
+ const NOTICES = new Map([
91
+ ['TRT_SHIPMENT_ARRIVAL_PM_NOTIFICATION', 'The recipient is told the item waits in a parcel machine.'],
92
+ ['TRT_SHIPMENT_ARRIVAL_PO_NOTIFICATION', 'The recipient is told the item waits at a post office.'],
93
+ ['TRT_SHIPMENT_REMINDER_PM_NOTIFICATION', 'A reminder that the item waits in a parcel machine.'],
94
+ ['TRT_SHIPMENT_REMINDER_PO_NOTIFICATION', 'A reminder that the item waits at a post office.'],
95
+ ['TRT_SHIPMENT_RECIPIENT_NOTIFICATION_EVENT', 'A message to the recipient.'],
96
+ ['TRT_COURIER_PRE_DELIVERY_RECIPIENT_NOTIFICATION', 'The recipient is told the courier is coming.'],
97
+ ['TRT_COURIER_CALLED_TO_RECEIVER', 'The courier contacted the recipient about the delivery.'],
98
+ ['TRT_SHIPMENT_RECIPIENT_CONTACT_UPDATED', 'The recipient\'s contact details changed.'],
99
+ ['TRT_SHIPMENT_DELIVERY_PARTNER_BARCODE_ASSIGNED', 'A delivery partner\'s number was attached to the item.'],
100
+ ['CARGO_SHIPMENT_DELIVERY_PARTNER_BARCODE_ASSIGNED', 'A delivery partner\'s number was attached to the item.'],
101
+ ['TRT_SHIPMENT_MEASURED', 'The item was measured.'],
102
+ ]);
103
+ // Codes whose meaning for the delivery is unclear, left without a stage.
104
+ const UNSTAGED = new Map([
105
+ ['TRT_SHIPMENT_EDA', 'Held at the inward office of exchange, for customs or for another reason the event does not give.'],
106
+ ['TRT_SHIPMENT_EDF', 'Held at the delivery depot, for a reason the event does not give.'],
107
+ ['TRT_SHIPMENT_DOC_RETURN_SENDER', 'A document about the item went back to the sender, not the item itself.'],
108
+ ['TRT_SHIPMENT_RETURNED_BY_CUSTOMER', 'A customer return sent through Omniva; no reply showed where it stands in the delivery.'],
109
+ ['TRT_SHIPMENT_COURIER_DELIVERY_CANCELLED', 'The courier round was called off; the event does not say what happens next.'],
110
+ ['TRT_SHIPMENT_DECLARATION_CANCELLED', 'A customs declaration was withdrawn; the item may still be held or move on.'],
111
+ ['CARGO_SHIPMENT_DELIVERED_TO_RETURNS_CENTER', 'Reached a returns centre, which may end a customer return or start one to the sender.'],
112
+ ['CARGO_SHIPMENT_WAITING_FOR_PAYMENT', 'Waits for a payment the event does not name, such as duties or a delivery charge.'],
113
+ ['CARGO_SHIPMENT_ARRIVED_TO_LAST_PICKUP_POINT', 'Either waiting for the recipient or reaching the last hub before delivery; no reply showed which.'],
114
+ ]);
115
+ /**
116
+ * The item's state, which Omniva's trackers show as the headline. The trackers
117
+ * know seven; the dictionary lists the older ones too.
118
+ */
119
+ const STATES = new Map([
120
+ ['REGISTERED', { wording: 'Registered', stage: 'registered' }],
121
+ ['REGISTERED_IN_OMNIVA', { wording: 'Shipment registered', stage: 'registered' }],
122
+ ['IPS_POSTED_EMA', { wording: 'Item is posted by the sender/collected by the Post', stage: 'accepted' }],
123
+ ['IN_PROGRESS', { wording: 'In progress', stage: 'in_transit' }],
124
+ ['IN_TRANSIT', { wording: 'Shipment is on the way', stage: 'in_transit' }],
125
+ ['IN_POST_OFFICE', { wording: 'Shipment is at the postal facility', stage: 'in_transit' }],
126
+ ['IN_LOGISTIC_CENTER', { wording: 'Shipment is at the sorting centre', stage: 'in_transit' }],
127
+ ['IPS_DEPARTED_EMC', { wording: 'Departure from sender country', stage: 'in_transit' }],
128
+ ['IPS_ARRIVED_EMD', { wording: 'Arrived to destination country', stage: 'in_transit' }],
129
+ ['RELEASED_FROM_CUSTOMS', { wording: 'Released from customs', stage: 'in_transit' }],
130
+ ['MOVEMENT_RESTORED', { wording: 'Movement restored', stage: 'in_transit' }],
131
+ ['RETURN_CANCELLED', { wording: 'Return cancelled', stage: 'in_transit' }],
132
+ ['REDIRECTED', { wording: 'Redirected', stage: 'in_transit' }],
133
+ ['IN_CUSTOMS', { wording: 'In customs', stage: 'customs' }],
134
+ ['TAKEN_TO_CUSTOMS', { wording: 'In customs', stage: 'customs' }],
135
+ ['ON_COURIER_DELIVERY', { wording: 'On your way', stage: 'out_for_delivery' }],
136
+ ['FAILED_DELIVERY', { wording: 'Failed delivery attempt', stage: 'failed_attempt' }],
137
+ ['READY_FOR_PICKUP', { wording: 'Ready for pickup', stage: 'ready_for_pickup' }],
138
+ ['IN_PARCEL_MACHINE', { wording: 'Shipment delivered to the parcel machine', stage: 'ready_for_pickup' }],
139
+ ['DELIVERED', { wording: 'Delivered', stage: 'delivered' }],
140
+ ['RETURNED', { wording: 'Returned', stage: 'returned' }],
141
+ ['CANCELLED', { wording: 'Cancelled', stage: 'exception' }],
142
+ ['WRITTEN_OFF', { wording: 'Shipment written off', stage: 'exception' }],
143
+ ['STOPPED', { wording: 'Shipment stopped', stage: 'exception' }],
144
+ ['MISSING', { wording: 'Shipment missing', stage: 'exception' }],
145
+ ['LOST', { wording: 'Shipment missing', stage: 'exception' }],
146
+ ]);
147
+ export function omnivaEventStage(code) {
148
+ return Object.hasOwn(EVENTS, code) ? EVENTS[code] : undefined;
149
+ }
150
+ export function isOmnivaNotice(code) {
151
+ return NOTICES.has(code);
152
+ }
153
+ /** The English wording and stage of an item's state, if the map knows it. */
154
+ export function omnivaState(code) {
155
+ return STATES.get(code);
156
+ }
157
+ /** Event codes and state codes do not overlap: events carry a TRT or CARGO prefix. */
158
+ export const statusMap = {
159
+ stage: code => (code ? omnivaEventStage(code) ?? omnivaState(code)?.stage : undefined),
160
+ gaps: [
161
+ ...[...NOTICES].map(([code, note]) => ({ code, note: `${note} Not a movement: the item keeps the stage it had.` })),
162
+ ...[...UNSTAGED].map(([code, note]) => ({ code, note })),
163
+ ],
164
+ };
@@ -1,4 +1,125 @@
1
1
  {
2
2
  "carrier": "omniva",
3
- "entries": []
3
+ "entries": [
4
+ { "code": "TRT_SHIPMENT_REGISTERED", "wording": "Shipment registered. Shipment has not been handed over to Omniva", "language": "en", "stage": "registered", "confirmedBy": "live", "note": "The sender registered the item; Omniva does not have it yet." },
5
+ { "code": "TRT_COURIER_CALLED_TO_RECEIVER", "wording": "Courier has reached out to the recipient for delivery", "language": "en", "confirmedBy": "portal", "note": "The courier contacted the recipient about the delivery. Not a movement: the item keeps the stage it had. Seen in archived replies of the web trackers' API." },
6
+ { "code": "TRT_SHIPMENT_DOC_RETURN_SENDER", "wording": "Shipment document returned to sender", "language": "en", "confirmedBy": "portal", "note": "A document about the item went back to the sender, not the item itself. Listed in the trackers' event dictionary." },
7
+ { "code": "TRT_SHIPMENT_FAILED_DELIVERY_ATTEMPT", "wording": "Delivery attempt made", "language": "en", "stage": "failed_attempt", "confirmedBy": "portal", "note": "The wording reads like a delivery, but the code is a failed attempt. A parameter gives the reason, which the adapter drops. Seen in archived replies of the web trackers' API." },
8
+ { "code": "TRT_SHIPMENT_PROHIBIT", "wording": "Shipment prohibit", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
9
+ { "code": "TRT_SHIPMENT_ARRIVED_TO_FINAL_PO", "wording": "Shipment is ready for pickup at the post office", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "portal", "note": "Its site names the post office the item waits at. Seen in archived replies of the web trackers' API." },
10
+ { "code": "TRT_SHIPMENT_ARRIVED_TO_PO", "wording": "Shipment has arrived at a postal facility for further processing", "language": "en", "stage": "in_transit", "confirmedBy": "live", "note": "Its site names the terminal or sorting line, by its English name when it has one." },
11
+ { "code": "TRT_SHIPMENT_CANCELLED", "wording": "Shipment has been cancelled", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
12
+ { "code": "TRT_SHIPMENT_CUSTOMS_PAYMENT_REQ_REMINDER", "wording": "Shipment customs payment req reminder", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
13
+ { "code": "TRT_SHIPMENT_DECLARATION_CANCELLED", "wording": "Shipment declaration has been cancelled", "language": "en", "confirmedBy": "portal", "note": "A customs declaration was withdrawn; the item may still be held or move on. Listed in the trackers' event dictionary." },
14
+ { "code": "TRT_SHIPMENT_DELIVERED", "wording": "Shipment delivered", "language": "en", "stage": "delivered", "confirmedBy": "live", "note": "Its site names the parcel machine or post office the item was collected from. After a return starts, the adapter reads it as returned." },
15
+ { "code": "TRT_SHIPMENT_HANDOVER_TO_PM", "wording": "Sender has placed the shipment in the parcel machine", "language": "en", "stage": "accepted", "confirmedBy": "live", "note": "Its site names the parcel machine the sender used." },
16
+ { "code": "TRT_SHIPMENT_HANDOVER_TO_PO", "wording": "Shipment has been handed over to the post office for processing", "language": "en", "stage": "accepted", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
17
+ { "code": "TRT_SHIPMENT_HELD_IN_CUSTOMS", "wording": "Shipment held in customs", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
18
+ { "code": "TRT_SHIPMENT_ASSIGN_CUSTOMS_INSPECTION", "wording": "Shipment assign customs inspection", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
19
+ { "code": "TRT_SHIPMENT_PICKED_UP_BY_COURIER", "wording": "The courier has picked up the shipment", "language": "en", "stage": "accepted", "confirmedBy": "live", "note": "The courier collected the item from the sender." },
20
+ { "code": "TRT_SHIPMENT_REDIRECTION", "wording": "Shipment has been redirected to a different location", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Seen in archived replies of the web trackers' API." },
21
+ { "code": "TRT_SHIPMENT_RELEASED_FROM_CUSTOMS", "wording": "Shipment released from the customs and is on the way", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Seen in archived replies of the web trackers' API." },
22
+ { "code": "TRT_SHIPMENT_RETURN", "wording": "Shipment is being returned to the sender", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "A return under way. A delivery after it reaches the sender, so the adapter reads it as returned. Seen in archived replies of the web trackers' API." },
23
+ { "code": "TRT_SHIPMENT_RETURN_CANCELLED", "wording": "Shipment return has been cancelled", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Ends a return: a delivery after it reaches the recipient. Listed in the trackers' event dictionary." },
24
+ { "code": "TRT_SHIPMENT_STORED_IN_PM", "wording": "Shipment is ready for collection from the parcel machine", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "portal", "note": "Its site names the parcel machine the item waits in. Seen in archived replies of the web trackers' API." },
25
+ { "code": "TRT_SHIPMENT_TAKEN_TO_CUSTOMS", "wording": "Shipment has been sent for customs clearance", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
26
+ { "code": "TRT_SHIPMENT_SORTED", "wording": "Shipment is sorted", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
27
+ { "code": "TRT_SHIPMENT_ARRIVAL_PM_NOTIFICATION", "wording": "A notification of shipment arrival has been sent", "language": "en", "confirmedBy": "portal", "note": "The recipient is told the item waits in a parcel machine. Not a movement: the item keeps the stage it had. Seen in archived replies of the web trackers' API." },
28
+ { "code": "TRT_SHIPMENT_ARRIVAL_PO_NOTIFICATION", "wording": "Notification of shipment arrival has been sent", "language": "en", "confirmedBy": "portal", "note": "The recipient is told the item waits at a post office. Not a movement: the item keeps the stage it had. Seen in archived replies of the web trackers' API." },
29
+ { "code": "TRT_SHIPMENT_REMINDER_PM_NOTIFICATION", "wording": "A notification of shipment arrival has been resent", "language": "en", "confirmedBy": "portal", "note": "A reminder that the item waits in a parcel machine. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
30
+ { "code": "TRT_SHIPMENT_REMINDER_PO_NOTIFICATION", "wording": "A notification of shipment arrival has been resent", "language": "en", "confirmedBy": "portal", "note": "A reminder that the item waits at a post office. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
31
+ { "code": "TRT_SHIPMENT_ARRIVAL_CUSTOMS_NOTIFICATION", "wording": "A notification of shipment arrival to customs has been sent", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Asks the recipient to declare an item held at customs. Seen in archived replies of the web trackers' API." },
32
+ { "code": "TRT_SHIPMENT_CD_REMINDER_NOTIFICATION", "wording": "Customs notification has been resent", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Asks the recipient again to declare an item held at customs. Seen in archived replies of the web trackers' API." },
33
+ { "code": "TRT_SHIPMENT_CD_ADD_PAYMENT_NOTIFICATION", "wording": "A notification of additional payment for customs declaration has been sent", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
34
+ { "code": "TRT_SHIPMENT_CD_ADD_PAYMENT_REMINDER_NOTIFICATION", "wording": "Reminder of additional payment for a customs declaration", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
35
+ { "code": "TRT_SHIPMENT_COURIER_DELIVERY_STARTED", "wording": "The shipment has been handed over to the courier", "language": "en", "stage": "out_for_delivery", "confirmedBy": "portal", "note": "The courier set out to deliver the item. Seen in archived replies of the web trackers' API." },
36
+ { "code": "TRT_SHIPMENT_COURIER_DELIVERY_CANCELLED", "wording": "Courier delivery has been cancelled", "language": "en", "confirmedBy": "portal", "note": "The courier round was called off; the event does not say what happens next. Listed in the trackers' event dictionary." },
37
+ { "code": "TRT_SHIPMENT_EDA", "wording": "Held at inward Office of Exchange", "language": "en", "confirmedBy": "portal", "note": "Held at the inward office of exchange, for customs or for another reason the event does not give. Listed in the trackers' event dictionary." },
38
+ { "code": "TRT_SHIPMENT_EDB", "wording": "Shipment is under customs review", "language": "en", "stage": "customs", "confirmedBy": "live", "note": "A UPU event from the destination post, without a site." },
39
+ { "code": "TRT_SHIPMENT_EDC", "wording": "Shipment released from the customs and is moving forward", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
40
+ { "code": "TRT_SHIPMENT_EDD", "wording": "Shipment is being processed at sorting center", "language": "en", "stage": "in_transit", "confirmedBy": "live" },
41
+ { "code": "TRT_SHIPMENT_EDF", "wording": "Shipment is on hold at the delivery depot", "language": "en", "confirmedBy": "portal", "note": "Held at the delivery depot, for a reason the event does not give. Listed in the trackers' event dictionary." },
42
+ { "code": "TRT_SHIPMENT_EDG", "wording": "Shipment is on its way for final delivery", "language": "en", "stage": "out_for_delivery", "confirmedBy": "live" },
43
+ { "code": "TRT_SHIPMENT_EDH", "wording": "Shipment is ready for pickup at the post office", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
44
+ { "code": "TRT_SHIPMENT_EDX", "wording": "Import process terminated", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
45
+ { "code": "TRT_SHIPMENT_EMA", "wording": "Shipment has been posted by the sender or collected by the postal service", "language": "en", "stage": "accepted", "confirmedBy": "live" },
46
+ { "code": "TRT_SHIPMENT_EMB", "wording": "Arrival at outward Office of Exchange", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
47
+ { "code": "TRT_SHIPMENT_EMC", "wording": "Shipment is on the way to the destination country", "language": "en", "stage": "in_transit", "confirmedBy": "live" },
48
+ { "code": "TRT_SHIPMENT_EMD", "wording": "Arrival to destination country", "language": "en", "stage": "in_transit", "confirmedBy": "live" },
49
+ { "code": "TRT_SHIPMENT_EME", "wording": "Shipment is being held by customs for further inspection", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
50
+ { "code": "TRT_SHIPMENT_EMF", "wording": "Departure from inward Office of Exchange", "language": "en", "stage": "in_transit", "confirmedBy": "live" },
51
+ { "code": "TRT_SHIPMENT_EMG", "wording": "Shipment has reached the postal facility for further sorting", "language": "en", "stage": "in_transit", "confirmedBy": "live" },
52
+ { "code": "TRT_SHIPMENT_EMH", "wording": "Delivery attempt failed", "language": "en", "stage": "failed_attempt", "confirmedBy": "live", "note": "A UPU event from the destination post, without a site." },
53
+ { "code": "TRT_SHIPMENT_EXX", "wording": "Export cancellation", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
54
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_TRANSIT_HUB", "wording": "Shipment arrived to transit hub", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
55
+ { "code": "CARGO_SHIPMENT_DEPARTED_FROM_TRANSIT_HUB", "wording": "Shipment has left the transit hub and is moving forward", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
56
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_DESTINATION_COUNTRY", "wording": "Shipment arrived to destination country", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
57
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_LAST_PICKUP_POINT", "wording": "Shipment has reached the final pickup location", "language": "en", "confirmedBy": "portal", "note": "Either waiting for the recipient or reaching the last hub before delivery; no reply showed which. Listed in the trackers' event dictionary." },
58
+ { "code": "CARGO_SHIPMENT_DELIVERED", "wording": "Shipment has been successfully delivered", "language": "en", "stage": "delivered", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
59
+ { "code": "CARGO_SHIPMENT_DELIVERY_FAILED", "wording": "Shipment delivery failed", "language": "en", "stage": "failed_attempt", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
60
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_LAST_DELIVERY_OFFICE", "wording": "Shipment is in transit and has reached an intermediate hub", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
61
+ { "code": "CARGO_SHIPMENT_RETURNED", "wording": "Shipment has been returned to the sender", "language": "en", "stage": "returned", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
62
+ { "code": "CARGO_SHIPMENT_PRESENTED_TO_CUSTOMS", "wording": "Shipment is under customs review", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
63
+ { "code": "CARGO_SHIPMENT_RELEASED_FROM_CUSTOMS", "wording": "Shipment released from the customs and is on the way", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
64
+ { "code": "CARGO_SHIPMENT_HELD_IN_CUSTOMS", "wording": "Shipment held in customs", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
65
+ { "code": "TRT_SHIPMENT_DEPARTED_FROM_PM", "wording": "Shipment departed from parcel machine", "language": "en", "stage": "in_transit", "confirmedBy": "live" },
66
+ { "code": "TRT_SHIPMENT_MOVEMENT_STOPPED", "wording": "Shipment movement stopped", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
67
+ { "code": "TRT_SHIPMENT_MOVEMENT_RESTORED", "wording": "Shipment movement restored", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
68
+ { "code": "TRT_SHIPMENT_EMI", "wording": "Shipment delivered", "language": "en", "stage": "delivered", "confirmedBy": "live", "note": "A UPU event from the destination post, without a site." },
69
+ { "code": "CARGO_SHIPMENT_DESTROYED", "wording": "Shipment destroyed", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
70
+ { "code": "TRT_SHIPMENT_RETURNED_BY_CUSTOMER", "wording": "Shipment is being returned via Omniva", "language": "en", "confirmedBy": "portal", "note": "A customer return sent through Omniva; no reply showed where it stands in the delivery. Listed in the trackers' event dictionary." },
71
+ { "code": "TRT_COURIER_PRE_DELIVERY_RECIPIENT_NOTIFICATION", "wording": "A notification of courier arrival has been sent", "language": "en", "confirmedBy": "portal", "note": "The recipient is told the courier is coming. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
72
+ { "code": "CARGO_SHIPMENT_POSTED_IN_RETURNING_COUNTRY", "wording": "Shipment has been processed for return", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "A return under way. A delivery after it reaches the sender. Listed in the trackers' event dictionary." },
73
+ { "code": "CARGO_SHIPMENT_DEPARTED_FROM_RETURNING_COUNTRY", "wording": "Shipment has left the return hub and is in transit", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
74
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_RETURNING_COUNTRY_HUB", "wording": "Shipment has arrived in the return hub of the sender's country", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
75
+ { "code": "TRT_SHIPMENT_RECIPIENT_CONTACT_UPDATED", "wording": "Recipient contact details updated", "language": "en", "confirmedBy": "portal", "note": "The recipient's contact details changed. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
76
+ { "code": "TRT_SHIPMENT_DELIVERY_PARTNER_BARCODE_ASSIGNED", "wording": "Delivery partner barcode assigned to shipment", "language": "en", "confirmedBy": "portal", "note": "A delivery partner's number was attached to the item. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
77
+ { "code": "CARGO_SHIPMENT_DELIVERY_PARTNER_BARCODE_ASSIGNED", "wording": "Delivery partner barcode assigned to shipment", "language": "en", "confirmedBy": "portal", "note": "A delivery partner's number was attached to the item. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
78
+ { "code": "TRT_SHIPMENT_MISSING_IN_PM", "wording": "Shipment is missing from the parcel machine, please check with support", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
79
+ { "code": "CARGO_SHIPMENT_DELIVERED_TO_RETURNS_CENTER", "wording": "Shipment delivered to returns center", "language": "en", "confirmedBy": "portal", "note": "Reached a returns centre, which may end a customer return or start one to the sender. Listed in the trackers' event dictionary." },
80
+ { "code": "TRT_SHIPMENT_EMJ", "wording": "Shipment has reached the transit processing center", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
81
+ { "code": "TRT_SHIPMENT_REGISTERED_IN_OMNIVA", "wording": "Shipment registered", "language": "en", "stage": "registered", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
82
+ { "code": "CARGO_SHIPMENT_CANCELLED", "wording": "Shipment cancelled", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
83
+ { "code": "CARGO_SHIPMENT_WAITING_FOR_PAYMENT", "wording": "Shipment waiting for payment", "language": "en", "confirmedBy": "portal", "note": "Waits for a payment the event does not name, such as duties or a delivery charge. Listed in the trackers' event dictionary." },
84
+ { "code": "CARGO_SHIPMENT_IN_TRANSIT_TO_DESTINATION_COUNTRY", "wording": "Shipment in transit to destination country", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
85
+ { "code": "CARGO_SHIPMENT_HANDOVER_FROM_LINEHAUL_EVENT", "wording": "Shipment handover from linehaul", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
86
+ { "code": "TRT_SHIPMENT_RECIPIENT_NOTIFICATION_EVENT", "wording": "Recipient has been notified", "language": "en", "confirmedBy": "portal", "note": "A message to the recipient. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
87
+ { "code": "CARGO_SHIPMENT_ARRIVING_TO_DESTINATION_COUNTRY", "wording": "Shipment arriving to destination country", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
88
+ { "code": "CARGO_SHIPMENT_MISSING_IN_PM", "wording": "Shipment missing in the parcel machine", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
89
+ { "code": "CARGO_SHIPMENT_NOT_ARRIVED_IN_BAG_EVENT", "wording": "Shipment not arrived in bag", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
90
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_CUSTOMS_CLEARANCE_HUB", "wording": "Shipment arrived to custom clearance hub", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
91
+ { "code": "CARGO_SHIPMENT_HANDED_TO_LAST_MILE", "wording": "Shipment handed over to last mile", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
92
+ { "code": "TRT_SHIPMENT_PREDECLARED_MISSING", "wording": "Predeclared shipment is missing", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
93
+ { "code": "TRT_SHIPMENT_MEASURED", "wording": "Shipment measured", "language": "en", "confirmedBy": "portal", "note": "The item was measured. Not a movement: the item keeps the stage it had. Listed in the trackers' event dictionary." },
94
+ { "code": "TRT_SHIPMENT_EDE", "wording": "Item out of sorting centre", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
95
+ { "code": "CARGO_SHIPMENT_ARRIVED_TO_SORTING", "wording": "Shipment arrived to sorting hub", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
96
+ { "code": "CARGO_SHIPMENT_DEPARTED_FROM_SORTING", "wording": "Shipment departed from sorting hub", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
97
+ { "code": "CARGO_SHIPMENT_OUT_FOR_DELIVERY", "wording": "Shipment out for delivery", "language": "en", "stage": "out_for_delivery", "confirmedBy": "portal", "note": "Listed in the trackers' event dictionary." },
98
+ { "code": "REGISTERED", "wording": "Registered", "language": "en", "stage": "registered", "confirmedBy": "live", "note": "The item's state, which the adapter reads when the reply has no scans." },
99
+ { "code": "IN_TRANSIT", "wording": "Shipment is on the way", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
100
+ { "code": "IN_CUSTOMS", "wording": "In customs", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
101
+ { "code": "IN_POST_OFFICE", "wording": "Shipment is at the postal facility", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
102
+ { "code": "IN_PARCEL_MACHINE", "wording": "Shipment delivered to the parcel machine", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
103
+ { "code": "ON_COURIER_DELIVERY", "wording": "On your way", "language": "en", "stage": "out_for_delivery", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
104
+ { "code": "DELIVERED", "wording": "Delivered", "language": "en", "stage": "delivered", "confirmedBy": "live", "note": "The item's state, which the adapter reads when the reply has no scans." },
105
+ { "code": "CANCELLED", "wording": "Cancelled", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
106
+ { "code": "IN_LOGISTIC_CENTER", "wording": "Shipment is at the sorting centre", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
107
+ { "code": "WRITTEN_OFF", "wording": "Shipment written off", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
108
+ { "code": "STOPPED", "wording": "Shipment stopped", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
109
+ { "code": "MISSING", "wording": "Shipment missing", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
110
+ { "code": "LOST", "wording": "Shipment missing", "language": "en", "stage": "exception", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
111
+ { "code": "RETURNED", "wording": "Returned", "language": "en", "stage": "returned", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
112
+ { "code": "REDIRECTED", "wording": "Redirected", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
113
+ { "code": "IPS_DEPARTED_EMC", "wording": "Departure from sender country", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
114
+ { "code": "IPS_ARRIVED_EMD", "wording": "Arrived to destination country", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
115
+ { "code": "TAKEN_TO_CUSTOMS", "wording": "In customs", "language": "en", "stage": "customs", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
116
+ { "code": "RELEASED_FROM_CUSTOMS", "wording": "Released from customs", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
117
+ { "code": "MOVEMENT_RESTORED", "wording": "Movement restored", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
118
+ { "code": "IPS_POSTED_EMA", "wording": "Item is posted by the sender/collected by the Post", "language": "en", "stage": "accepted", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
119
+ { "code": "RETURN_CANCELLED", "wording": "Return cancelled", "language": "en", "stage": "in_transit", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
120
+ { "code": "FAILED_DELIVERY", "wording": "Failed delivery attempt", "language": "en", "stage": "failed_attempt", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
121
+ { "code": "REGISTERED_IN_OMNIVA", "wording": "Shipment registered", "language": "en", "stage": "registered", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Listed in the trackers' state dictionary." },
122
+ { "code": "IN_PROGRESS", "wording": "In progress", "language": "en", "stage": "in_transit", "confirmedBy": "live", "note": "The item's state, which the adapter reads when the reply has no scans." },
123
+ { "code": "READY_FOR_PICKUP", "wording": "Ready for pickup", "language": "en", "stage": "ready_for_pickup", "confirmedBy": "portal", "note": "The item's state, which the adapter reads when the reply has no scans. Seen in archived replies of the web trackers' API." }
124
+ ]
4
125
  }
@@ -37,6 +37,7 @@ import { statusMap as jntCargo } from '../../carriers/j-and-t-cargo/status.js';
37
37
  import { statusMap as laPoste } from '../../carriers/la-poste/status.js';
38
38
  import { statusMap as oldDominion } from '../../carriers/old-dominion/status.js';
39
39
  import { statusMap as omgo } from '../../carriers/omgo/status.js';
40
+ import { statusMap as omniva } from '../../carriers/omniva/status.js';
40
41
  import { statusMap as orlenPaczka } from '../../carriers/orlen-paczka/status.js';
41
42
  import { statusMap as postlogistics } from '../../carriers/postlogistics/status.js';
42
43
  import { statusMap as ppl } from '../../carriers/ppl/status.js';
@@ -59,8 +60,8 @@ export const STATUS_MAPS = {
59
60
  'acs-courier': acsCourier, aliexpress, 'an-post': anPost, 'box-now': boxNow, 'china-post': chinaPost, chronopost, 'correos-spain': correosSpain,
60
61
  'dhl-express': dhlExpress, dpd, 'dpd-de': dpdDe, 'dpd-fr': dpdFr, 'dpd-ie': dpdIe, 'dpd-pl': dpdPl, econt, elta, emile, 'fan-courier': fanCourier,
61
62
  foxpost, 'geniki-taxydromiki': geniki, 'gls-it': glsIt, 'gofo-fr': gofoFr, 'gofo-it': gofoIt, 'j-and-t': jAndT, 'j-and-t-cargo': jntCargo,
62
- 'la-poste': laPoste, 'old-dominion': oldDominion, omgo, 'orlen-paczka': orlenPaczka, postlogistics, ppl, sagawa, sameday, speedx, speedy, sto,
63
- 'swiss-post': swissPost, 'thailand-post': thailandPost, tnt, ups, venipak, yunexpress,
63
+ 'la-poste': laPoste, 'old-dominion': oldDominion, omgo, omniva, 'orlen-paczka': orlenPaczka, postlogistics, ppl, sagawa, sameday, speedx, speedy,
64
+ sto, 'swiss-post': swissPost, 'thailand-post': thailandPost, tnt, ups, venipak, yunexpress,
64
65
  };
65
66
  /** The carrier the app files the universal providers' scans under. */
66
67
  const UNIVERSAL_CARRIER = 'unknown';
@@ -7194,7 +7194,8 @@
7194
7194
  "timezone": "Europe/Tallinn",
7195
7195
  "tracking": {
7196
7196
  "mode": "automatic",
7197
- "adapter": "universal"
7197
+ "adapter": "omniva",
7198
+ "recognitionRank": 64
7198
7199
  },
7199
7200
  "canaryUrl": "https://minu.omniva.ee/track",
7200
7201
  "trackingUrlTemplate": "https://minu.omniva.ee/track/{trackingNumber}",
@@ -7230,6 +7231,12 @@
7230
7231
  {
7231
7232
  "pattern": "^JJEE[A-Z]{3}\\d{11}$",
7232
7233
  "confidence": "high"
7234
+ },
7235
+ {
7236
+ "pattern": "^[A-Z]{2}\\d{9}EE$",
7237
+ "confidence": "low",
7238
+ "checksum": "s10",
7239
+ "preferred": true
7233
7240
  }
7234
7241
  ]
7235
7242
  },
@@ -4841,7 +4841,8 @@ export declare const CARRIER_CATALOG: {
4841
4841
  readonly timezone: "Europe/Tallinn";
4842
4842
  readonly tracking: {
4843
4843
  readonly mode: "automatic";
4844
- readonly adapter: "universal";
4844
+ readonly adapter: "omniva";
4845
+ readonly recognitionRank: 64;
4845
4846
  };
4846
4847
  readonly canaryUrl: "https://minu.omniva.ee/track";
4847
4848
  readonly trackingUrlTemplate: "https://minu.omniva.ee/track/{trackingNumber}";
@@ -4860,6 +4861,11 @@ export declare const CARRIER_CATALOG: {
4860
4861
  }, {
4861
4862
  readonly pattern: "^JJEE[A-Z]{3}\\d{11}$";
4862
4863
  readonly confidence: "high";
4864
+ }, {
4865
+ readonly pattern: "^[A-Z]{2}\\d{9}EE$";
4866
+ readonly confidence: "low";
4867
+ readonly checksum: "s10";
4868
+ readonly preferred: true;
4863
4869
  }];
4864
4870
  };
4865
4871
  readonly "orlen-paczka": {
@@ -7195,7 +7195,8 @@ export const CARRIER_CATALOG = {
7195
7195
  "timezone": "Europe/Tallinn",
7196
7196
  "tracking": {
7197
7197
  "mode": "automatic",
7198
- "adapter": "universal"
7198
+ "adapter": "omniva",
7199
+ "recognitionRank": 64
7199
7200
  },
7200
7201
  "canaryUrl": "https://minu.omniva.ee/track",
7201
7202
  "trackingUrlTemplate": "https://minu.omniva.ee/track/{trackingNumber}",
@@ -7231,6 +7232,12 @@ export const CARRIER_CATALOG = {
7231
7232
  {
7232
7233
  "pattern": "^JJEE[A-Z]{3}\\d{11}$",
7233
7234
  "confidence": "high"
7235
+ },
7236
+ {
7237
+ "pattern": "^[A-Z]{2}\\d{9}EE$",
7238
+ "confidence": "low",
7239
+ "checksum": "s10",
7240
+ "preferred": true
7234
7241
  }
7235
7242
  ]
7236
7243
  },
@@ -8716,7 +8723,7 @@ export const DETECTION_RULE_IDS = {
8716
8723
  "nationex": [],
8717
8724
  "nova-poshta": ["nova-poshta-domestic"],
8718
8725
  "omgo": ["omgo-1"],
8719
- "omniva": ["omniva-1", "omniva-2"],
8726
+ "omniva": ["omniva-1", "omniva-2", "omniva-s10"],
8720
8727
  "orlen-paczka": ["orlen-paczka-1"],
8721
8728
  "osm-worldwide": ["osm-worldwide-1"],
8722
8729
  "paquetexpress": [],
@@ -48,6 +48,7 @@ export const CARRIER_RECOGNITION_RANKS = {
48
48
  "ninja-van": 28,
49
49
  "nz-post": 32,
50
50
  "omgo": 1,
51
+ "omniva": 64,
51
52
  "ontrac": 21,
52
53
  "orlen-paczka": 49,
53
54
  "poczta-polska": 31,
@@ -88,6 +88,7 @@ import { adapter as novaPoshta } from '../carriers/nova-poshta/adapter.js';
88
88
  import { adapter as nzPost } from '../carriers/nz-post/adapter.js';
89
89
  import { adapter as oldDominion } from '../carriers/old-dominion/adapter.js';
90
90
  import { adapter as omgo } from '../carriers/omgo/adapter.js';
91
+ import { adapter as omniva } from '../carriers/omniva/adapter.js';
91
92
  import { adapter as ontrac } from '../carriers/ontrac/adapter.js';
92
93
  import { adapter as orlenPaczka } from '../carriers/orlen-paczka/adapter.js';
93
94
  import { adapter as paack } from '../carriers/paack/adapter.js';
@@ -224,6 +225,7 @@ export const REGISTRY = {
224
225
  "nz-post": nzPost,
225
226
  "old-dominion": oldDominion,
226
227
  "omgo": omgo,
228
+ "omniva": omniva,
227
229
  "ontrac": ontrac,
228
230
  "orlen-paczka": orlenPaczka,
229
231
  "paack": paack,
@@ -404,7 +406,7 @@ export const REGISTRY = {
404
406
  "nz-post": "nz-post",
405
407
  "old-dominion": "old-dominion",
406
408
  "omgo": "omgo",
407
- "omniva": "universal",
409
+ "omniva": "omniva",
408
410
  "ontrac": "ontrac",
409
411
  "orlen-paczka": "orlen-paczka",
410
412
  "osm-worldwide": "universal",
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Universal Parcel Scraper",
5
- "version": "0.77.0"
5
+ "version": "0.78.0"
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.77.0-main.937",
3
+ "version": "0.78.0-main.939",
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",