iobroker.parcelapp 0.11.1 → 0.12.0
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 +10 -4
- package/admin/i18n/de.json +1 -0
- package/admin/i18n/en.json +1 -0
- package/admin/i18n/es.json +1 -0
- package/admin/i18n/fr.json +1 -0
- package/admin/i18n/it.json +1 -0
- package/admin/i18n/nl.json +1 -0
- package/admin/i18n/pl.json +1 -0
- package/admin/i18n/pt.json +1 -0
- package/admin/i18n/ru.json +1 -0
- package/admin/i18n/uk.json +1 -0
- package/admin/i18n/zh-cn.json +1 -0
- package/build/lib/delivery-view.js +266 -0
- package/build/lib/delivery-view.js.map +7 -0
- package/build/lib/state-manager.js +72 -272
- package/build/lib/state-manager.js.map +3 -3
- package/build/main.js +60 -35
- package/build/main.js.map +2 -2
- package/io-package.json +16 -15
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ For details and how to disable it, see the [Sentry plugin documentation](https:/
|
|
|
40
40
|
- **ioBroker Admin >= 8.0.11**
|
|
41
41
|
- **parcel.app Premium subscription** — required for API access
|
|
42
42
|
|
|
43
|
+
> The adapter CANNOT be installed via GitHub: The adapter must be installed via the ioBroker repository (stable or latest).
|
|
44
|
+
|
|
43
45
|
---
|
|
44
46
|
|
|
45
47
|
## Configuration
|
|
@@ -142,6 +144,14 @@ sendTo("parcelapp.0", "addDelivery", {
|
|
|
142
144
|
Placeholder for the next version (at the beginning of the line):
|
|
143
145
|
### **WORK IN PROGRESS**
|
|
144
146
|
-->
|
|
147
|
+
|
|
148
|
+
### 0.12.0 (2026-09-06)
|
|
149
|
+
|
|
150
|
+
- Fixed: A package that reappeared after a database hiccup kept datapoints without a name or description until the adapter was restarted.
|
|
151
|
+
- Fixed: A delivery window written as "September 6, 2026 14:30" was ignored, so window, estimate and the count of packages expected today stayed empty for those carriers.
|
|
152
|
+
- New: The documentation explains why a delivery window can stay empty, and an unreadable date from the carrier can now be reported so the format gets added.
|
|
153
|
+
- New: The last known location of a package explains itself in the object tree: it is where the carrier last scanned it, not a live position.
|
|
154
|
+
|
|
145
155
|
### 0.11.1 (2026-09-04)
|
|
146
156
|
|
|
147
157
|
- Fixed: The last-changed timestamp of a package kept its old label and had no description as long as the package did not move.
|
|
@@ -165,10 +175,6 @@ sendTo("parcelapp.0", "addDelivery", {
|
|
|
165
175
|
|
|
166
176
|
- Fixed: Stopping or restarting the adapter was cut short — the stopped instance kept claiming a live connection to parcel.app instead of showing as disconnected.
|
|
167
177
|
|
|
168
|
-
### 0.10.2 (2026-08-22)
|
|
169
|
-
|
|
170
|
-
- Changed: Internal cleanup. No user-facing changes.
|
|
171
|
-
|
|
172
178
|
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
173
179
|
|
|
174
180
|
## Support
|
package/admin/i18n/de.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Erwartetes Zustellfenster; ein über mehrere Tage reichendes Fenster trägt auf beiden Seiten das Datum.",
|
|
52
52
|
"descDeliveryEstimate": "Erwartete Zustellung in Worten - heute, morgen oder in einigen Tagen.",
|
|
53
53
|
"descLastEvent": "Jüngste Sendungsmeldung des Zustellers mit Datum.",
|
|
54
|
+
"descLastLocation": "Wo der Zusteller die Sendung zuletzt erfasst hat - keine Live-Position.",
|
|
54
55
|
"descLastUpdated": "Zeitpunkt der letzten Änderung der Sendungsdaten - nicht der letzte Abruf.",
|
|
55
56
|
"descActiveCount": "Anzahl der Pakete, die noch nicht zugestellt sind.",
|
|
56
57
|
"descTodayCount": "Anzahl der Pakete, die heute erwartet werden.",
|
package/admin/i18n/en.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Expected delivery time window; a window spanning several days carries the date on both sides.",
|
|
52
52
|
"descDeliveryEstimate": "Expected delivery in words - today, tomorrow or in a number of days.",
|
|
53
53
|
"descLastEvent": "Most recent tracking event reported by the carrier, with its date.",
|
|
54
|
+
"descLastLocation": "Where the carrier last scanned the package - not a live position.",
|
|
54
55
|
"descLastUpdated": "When the tracking data last changed - not when the adapter last polled.",
|
|
55
56
|
"descActiveCount": "Number of packages that have not been delivered yet.",
|
|
56
57
|
"descTodayCount": "Number of packages expected to arrive today.",
|
package/admin/i18n/es.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Ventana de entrega prevista; una ventana de varios días lleva la fecha a ambos lados.",
|
|
52
52
|
"descDeliveryEstimate": "Entrega prevista en palabras - hoy, mañana o dentro de unos días.",
|
|
53
53
|
"descLastEvent": "Evento de seguimiento más reciente comunicado por el transportista, con su fecha.",
|
|
54
|
+
"descLastLocation": "Dónde escaneó el transportista el paquete por última vez, no es la posición actual.",
|
|
54
55
|
"descLastUpdated": "Momento del último cambio de los datos de seguimiento - no de la última consulta.",
|
|
55
56
|
"descActiveCount": "Número de paquetes que aún no se han entregado.",
|
|
56
57
|
"descTodayCount": "Número de paquetes que se esperan hoy.",
|
package/admin/i18n/fr.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Créneau de livraison prévu ; un créneau sur plusieurs jours porte la date de chaque côté.",
|
|
52
52
|
"descDeliveryEstimate": "Livraison prévue en toutes lettres - aujourd'hui, demain ou dans quelques jours.",
|
|
53
53
|
"descLastEvent": "Dernier événement de suivi communiqué par le transporteur, avec sa date.",
|
|
54
|
+
"descLastLocation": "Où le transporteur a scanné le colis pour la dernière fois - ce n'est pas une position en direct.",
|
|
54
55
|
"descLastUpdated": "Moment du dernier changement des données de suivi - pas de la dernière interrogation.",
|
|
55
56
|
"descActiveCount": "Nombre de colis qui ne sont pas encore livrés.",
|
|
56
57
|
"descTodayCount": "Nombre de colis attendus aujourd'hui.",
|
package/admin/i18n/it.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Finestra di consegna prevista; una finestra su più giorni riporta la data su entrambi i lati.",
|
|
52
52
|
"descDeliveryEstimate": "Consegna prevista a parole - oggi, domani o fra alcuni giorni.",
|
|
53
53
|
"descLastEvent": "Evento di tracciamento più recente comunicato dal corriere, con la data.",
|
|
54
|
+
"descLastLocation": "Dove il corriere ha registrato il pacco l'ultima volta - non è la posizione attuale.",
|
|
54
55
|
"descLastUpdated": "Momento dell'ultima variazione dei dati di tracciamento - non dell'ultima interrogazione.",
|
|
55
56
|
"descActiveCount": "Numero di pacchi non ancora consegnati.",
|
|
56
57
|
"descTodayCount": "Numero di pacchi attesi oggi.",
|
package/admin/i18n/nl.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Verwacht bezorgvenster; een venster over meerdere dagen toont aan beide kanten de datum.",
|
|
52
52
|
"descDeliveryEstimate": "Verwachte bezorging in woorden - vandaag, morgen of over een aantal dagen.",
|
|
53
53
|
"descLastEvent": "Meest recente zendingsmelding van de vervoerder, met datum.",
|
|
54
|
+
"descLastLocation": "Waar de vervoerder het pakket het laatst scande - geen live positie.",
|
|
54
55
|
"descLastUpdated": "Moment waarop de zendinggegevens voor het laatst zijn gewijzigd - niet de laatste opvraging.",
|
|
55
56
|
"descActiveCount": "Aantal pakketten dat nog niet is bezorgd.",
|
|
56
57
|
"descTodayCount": "Aantal pakketten dat vandaag wordt verwacht.",
|
package/admin/i18n/pl.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Przewidywane okno dostawy; okno obejmujące kilka dni zawiera datę po obu stronach.",
|
|
52
52
|
"descDeliveryEstimate": "Przewidywana dostawa słownie - dziś, jutro lub za kilka dni.",
|
|
53
53
|
"descLastEvent": "Najnowsze zdarzenie śledzenia zgłoszone przez przewoźnika, wraz z datą.",
|
|
54
|
+
"descLastLocation": "Gdzie przewoźnik ostatnio zeskanował przesyłkę - to nie jest pozycja na żywo.",
|
|
54
55
|
"descLastUpdated": "Moment ostatniej zmiany danych śledzenia - nie ostatniego odpytania.",
|
|
55
56
|
"descActiveCount": "Liczba przesyłek, które nie zostały jeszcze doręczone.",
|
|
56
57
|
"descTodayCount": "Liczba przesyłek oczekiwanych dzisiaj.",
|
package/admin/i18n/pt.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Janela de entrega prevista; uma janela que abrange vários dias mostra a data de ambos os lados.",
|
|
52
52
|
"descDeliveryEstimate": "Entrega prevista por extenso - hoje, amanhã ou dentro de alguns dias.",
|
|
53
53
|
"descLastEvent": "Evento de rastreio mais recente comunicado pela transportadora, com a data.",
|
|
54
|
+
"descLastLocation": "Onde a transportadora leu a encomenda pela última vez - não é a posição atual.",
|
|
54
55
|
"descLastUpdated": "Momento da última alteração dos dados de rastreio - não da última consulta.",
|
|
55
56
|
"descActiveCount": "Número de encomendas que ainda não foram entregues.",
|
|
56
57
|
"descTodayCount": "Número de encomendas previstas para hoje.",
|
package/admin/i18n/ru.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Ожидаемое временное окно доставки; окно, охватывающее несколько дней, содержит дату с обеих сторон.",
|
|
52
52
|
"descDeliveryEstimate": "Ожидаемая доставка словами - сегодня, завтра или через несколько дней.",
|
|
53
53
|
"descLastEvent": "Последнее событие отслеживания от перевозчика с датой.",
|
|
54
|
+
"descLastLocation": "Где перевозчик в последний раз отсканировал посылку - это не текущее местоположение.",
|
|
54
55
|
"descLastUpdated": "Момент последнего изменения данных отслеживания - не время последнего опроса.",
|
|
55
56
|
"descActiveCount": "Количество посылок, которые ещё не доставлены.",
|
|
56
57
|
"descTodayCount": "Количество посылок, ожидаемых сегодня.",
|
package/admin/i18n/uk.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "Очікуване вікно доставки; вікно, що охоплює кілька днів, містить дату з обох боків.",
|
|
52
52
|
"descDeliveryEstimate": "Очікувана доставка словами - сьогодні, завтра або за кілька днів.",
|
|
53
53
|
"descLastEvent": "Найновіша подія відстеження від перевізника, разом із датою.",
|
|
54
|
+
"descLastLocation": "Де перевізник востаннє відсканував посилку - це не поточне місцезнаходження.",
|
|
54
55
|
"descLastUpdated": "Момент останньої зміни даних відстеження - не останнього опитування.",
|
|
55
56
|
"descActiveCount": "Кількість посилок, які ще не доставлено.",
|
|
56
57
|
"descTodayCount": "Кількість посилок, що очікуються сьогодні.",
|
package/admin/i18n/zh-cn.json
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"descDeliveryWindow": "预计派送时间段;跨越多天的时间段会在两侧显示日期。",
|
|
52
52
|
"descDeliveryEstimate": "以文字表示的预计派送时间——今天、明天或若干天后。",
|
|
53
53
|
"descLastEvent": "承运商报告的最新跟踪事件及其日期。",
|
|
54
|
+
"descLastLocation": "承运商最后一次扫描包裹的地点 - 不是实时位置。",
|
|
54
55
|
"descLastUpdated": "跟踪数据最后一次发生变化的时间——不是最后一次轮询的时间。",
|
|
55
56
|
"descActiveCount": "尚未派送的包裹数量。",
|
|
56
57
|
"descTodayCount": "预计今天送达的包裹数量。",
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var delivery_view_exports = {};
|
|
20
|
+
__export(delivery_view_exports, {
|
|
21
|
+
TRACKABLE_STATUSES: () => TRACKABLE_STATUSES,
|
|
22
|
+
calculateCombinedWindow: () => calculateCombinedWindow,
|
|
23
|
+
calculateDeliveryEstimate: () => calculateDeliveryEstimate,
|
|
24
|
+
calculateDeliveryWindow: () => calculateDeliveryWindow,
|
|
25
|
+
computeDiffDays: () => computeDiffDays,
|
|
26
|
+
extractLastLocation: () => extractLastLocation,
|
|
27
|
+
formatLastEvent: () => formatLastEvent,
|
|
28
|
+
formatWindow: () => formatWindow,
|
|
29
|
+
isToday: () => isToday,
|
|
30
|
+
parseExpectedToMs: () => parseExpectedToMs,
|
|
31
|
+
windowBoundsMs: () => windowBoundsMs
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(delivery_view_exports);
|
|
34
|
+
var import_coerce = require("./coerce");
|
|
35
|
+
var import_i18n = require("./i18n");
|
|
36
|
+
const TRACKABLE_STATUSES = /* @__PURE__ */ new Set([2, 4, 8]);
|
|
37
|
+
const MONTH_NAMES = {
|
|
38
|
+
january: 0,
|
|
39
|
+
february: 1,
|
|
40
|
+
march: 2,
|
|
41
|
+
april: 3,
|
|
42
|
+
may: 4,
|
|
43
|
+
june: 5,
|
|
44
|
+
july: 6,
|
|
45
|
+
august: 7,
|
|
46
|
+
september: 8,
|
|
47
|
+
october: 9,
|
|
48
|
+
november: 10,
|
|
49
|
+
december: 11
|
|
50
|
+
};
|
|
51
|
+
const ISO_LIKE_RE = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/;
|
|
52
|
+
const MONTH_NAME_RE = /^([A-Za-z]+)\s+(\d{1,2}),\s*(\d{4})(?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)?$/;
|
|
53
|
+
function matchDateParts(raw) {
|
|
54
|
+
const iso = ISO_LIKE_RE.exec(raw);
|
|
55
|
+
if (iso) {
|
|
56
|
+
return {
|
|
57
|
+
year: Number(iso[1]),
|
|
58
|
+
month: Number(iso[2]) - 1,
|
|
59
|
+
day: Number(iso[3]),
|
|
60
|
+
hour: iso[4] !== void 0 ? Number(iso[4]) : 0,
|
|
61
|
+
minute: iso[5] !== void 0 ? Number(iso[5]) : 0,
|
|
62
|
+
second: iso[6] !== void 0 ? Number(iso[6]) : 0,
|
|
63
|
+
hasClock: iso[4] !== void 0
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const named = MONTH_NAME_RE.exec(raw);
|
|
67
|
+
if (named) {
|
|
68
|
+
const month = MONTH_NAMES[named[1].toLowerCase()];
|
|
69
|
+
if (month === void 0) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
year: Number(named[3]),
|
|
74
|
+
month,
|
|
75
|
+
day: Number(named[2]),
|
|
76
|
+
hour: named[4] !== void 0 ? Number(named[4]) : 0,
|
|
77
|
+
minute: named[5] !== void 0 ? Number(named[5]) : 0,
|
|
78
|
+
second: named[6] !== void 0 ? Number(named[6]) : 0,
|
|
79
|
+
hasClock: named[4] !== void 0
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function parseExpectedToMs(value, log) {
|
|
85
|
+
if (typeof value !== "string") {
|
|
86
|
+
if (value !== void 0 && value !== null) {
|
|
87
|
+
log == null ? void 0 : log.debug(`expected-date drift: not a string (got ${typeof value})`);
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const raw = value.trim();
|
|
92
|
+
if (raw.length === 0) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const parts = matchDateParts(raw);
|
|
96
|
+
if (!parts) {
|
|
97
|
+
log == null ? void 0 : log.debug(`expected-date drift: unsupported format '${raw}' \u2014 no window/estimate for this package`);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
if (parts.month < 0 || parts.month > 11 || parts.day < 1 || parts.day > 31) {
|
|
101
|
+
log == null ? void 0 : log.debug(`expected-date drift: month/day out of range in '${raw}'`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if (parts.hour > 23 || parts.minute > 59 || parts.second > 59) {
|
|
105
|
+
log == null ? void 0 : log.debug(`expected-date drift: time out of range in '${raw}'`);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const date = new Date(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second);
|
|
109
|
+
if (Number.isNaN(date.getTime()) || date.getMonth() !== parts.month || date.getDate() !== parts.day) {
|
|
110
|
+
log == null ? void 0 : log.debug(`expected-date drift: '${raw}' is not a real calendar date`);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const hasTime = parts.hasClock && !(parts.hour === 0 && parts.minute === 0 && parts.second === 0);
|
|
114
|
+
return { ms: date.getTime(), hasTime };
|
|
115
|
+
}
|
|
116
|
+
function windowBoundsMs(delivery, statusCode, log) {
|
|
117
|
+
var _a, _b;
|
|
118
|
+
if (!TRACKABLE_STATUSES.has(statusCode)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const toMs = (timestamp) => {
|
|
122
|
+
const ts = (0, import_coerce.coerceFiniteNumber)(timestamp);
|
|
123
|
+
if (ts === null || ts <= 0) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const ms = ts * 1e3;
|
|
127
|
+
return Number.isNaN(new Date(ms).getTime()) ? null : ms;
|
|
128
|
+
};
|
|
129
|
+
const dateMs = (value) => {
|
|
130
|
+
const parsed = parseExpectedToMs(value, log);
|
|
131
|
+
return parsed && parsed.hasTime ? parsed.ms : null;
|
|
132
|
+
};
|
|
133
|
+
const start = (_a = toMs(delivery.timestamp_expected)) != null ? _a : dateMs(delivery.date_expected);
|
|
134
|
+
if (start === null) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const end = (_b = toMs(delivery.timestamp_expected_end)) != null ? _b : dateMs(delivery.date_expected_end);
|
|
138
|
+
return { start, end };
|
|
139
|
+
}
|
|
140
|
+
function formatHHMM(ms) {
|
|
141
|
+
const d = new Date(ms);
|
|
142
|
+
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
|
|
143
|
+
}
|
|
144
|
+
function formatDateHHMM(ms) {
|
|
145
|
+
const d = new Date(ms);
|
|
146
|
+
const mm = (d.getMonth() + 1).toString().padStart(2, "0");
|
|
147
|
+
const dd = d.getDate().toString().padStart(2, "0");
|
|
148
|
+
return `${mm}-${dd} ${formatHHMM(ms)}`;
|
|
149
|
+
}
|
|
150
|
+
function sameLocalDay(aMs, bMs) {
|
|
151
|
+
const a = new Date(aMs);
|
|
152
|
+
const b = new Date(bMs);
|
|
153
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
154
|
+
}
|
|
155
|
+
function formatWindow(startMs, endMs) {
|
|
156
|
+
if (endMs === null || endMs <= startMs) {
|
|
157
|
+
return formatHHMM(startMs);
|
|
158
|
+
}
|
|
159
|
+
return sameLocalDay(startMs, endMs) ? `${formatHHMM(startMs)} - ${formatHHMM(endMs)}` : `${formatDateHHMM(startMs)} - ${formatDateHHMM(endMs)}`;
|
|
160
|
+
}
|
|
161
|
+
function calculateDeliveryWindow(delivery, statusCode, log) {
|
|
162
|
+
const bounds = windowBoundsMs(delivery, statusCode, log);
|
|
163
|
+
if (!bounds) {
|
|
164
|
+
return "";
|
|
165
|
+
}
|
|
166
|
+
return formatWindow(bounds.start, bounds.end);
|
|
167
|
+
}
|
|
168
|
+
function computeDiffDays(delivery, statusCode, log) {
|
|
169
|
+
if (!TRACKABLE_STATUSES.has(statusCode)) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
let expectedDate = null;
|
|
173
|
+
const ts = (0, import_coerce.coerceFiniteNumber)(delivery.timestamp_expected);
|
|
174
|
+
if (ts !== null && ts > 0) {
|
|
175
|
+
expectedDate = new Date(ts * 1e3);
|
|
176
|
+
} else {
|
|
177
|
+
const parsed = parseExpectedToMs(delivery.date_expected, log);
|
|
178
|
+
expectedDate = parsed ? new Date(parsed.ms) : null;
|
|
179
|
+
}
|
|
180
|
+
if (!expectedDate || Number.isNaN(expectedDate.getTime())) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const now = /* @__PURE__ */ new Date();
|
|
184
|
+
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
185
|
+
const expectedStart = new Date(expectedDate.getFullYear(), expectedDate.getMonth(), expectedDate.getDate());
|
|
186
|
+
return Math.round((expectedStart.getTime() - todayStart.getTime()) / (1e3 * 60 * 60 * 24));
|
|
187
|
+
}
|
|
188
|
+
function calculateDeliveryEstimate(delivery, statusCode, log) {
|
|
189
|
+
const diffDays = computeDiffDays(delivery, statusCode, log);
|
|
190
|
+
if (diffDays === null) {
|
|
191
|
+
return "";
|
|
192
|
+
}
|
|
193
|
+
if (diffDays < 0) {
|
|
194
|
+
return (0, import_i18n.tText)("estimateOverdue");
|
|
195
|
+
}
|
|
196
|
+
if (diffDays === 0) {
|
|
197
|
+
return (0, import_i18n.tText)("estimateToday");
|
|
198
|
+
}
|
|
199
|
+
if (diffDays === 1) {
|
|
200
|
+
return (0, import_i18n.tText)("estimateTomorrow");
|
|
201
|
+
}
|
|
202
|
+
return (0, import_i18n.tText)("estimateDays", diffDays);
|
|
203
|
+
}
|
|
204
|
+
function isToday(delivery, statusCode, log) {
|
|
205
|
+
return computeDiffDays(delivery, statusCode, log) === 0;
|
|
206
|
+
}
|
|
207
|
+
function calculateCombinedWindow(todayDeliveries, log) {
|
|
208
|
+
var _a;
|
|
209
|
+
const bounds = todayDeliveries.map((e) => windowBoundsMs(e.delivery, e.statusCode, log)).filter((b) => b !== null);
|
|
210
|
+
if (bounds.length === 0) {
|
|
211
|
+
return "";
|
|
212
|
+
}
|
|
213
|
+
const minStart = bounds.reduce((m, b) => b.start < m ? b.start : m, bounds[0].start);
|
|
214
|
+
const maxEnd = bounds.reduce((m, b) => {
|
|
215
|
+
var _a2;
|
|
216
|
+
const e = (_a2 = b.end) != null ? _a2 : b.start;
|
|
217
|
+
return e > m ? e : m;
|
|
218
|
+
}, (_a = bounds[0].end) != null ? _a : bounds[0].start);
|
|
219
|
+
return formatWindow(minStart, maxEnd);
|
|
220
|
+
}
|
|
221
|
+
function getLatestEvent(delivery) {
|
|
222
|
+
if (!Array.isArray(delivery.events) || delivery.events.length === 0) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const latest = delivery.events[0];
|
|
226
|
+
if (!latest || typeof latest !== "object") {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return latest;
|
|
230
|
+
}
|
|
231
|
+
function formatLastEvent(delivery) {
|
|
232
|
+
const latest = getLatestEvent(delivery);
|
|
233
|
+
if (!latest) {
|
|
234
|
+
return "";
|
|
235
|
+
}
|
|
236
|
+
const parts = [];
|
|
237
|
+
if (typeof latest.event === "string" && latest.event.length > 0) {
|
|
238
|
+
parts.push(latest.event);
|
|
239
|
+
}
|
|
240
|
+
if (typeof latest.date === "string" && latest.date.length > 0) {
|
|
241
|
+
parts.push(latest.date);
|
|
242
|
+
}
|
|
243
|
+
return parts.join(" - ");
|
|
244
|
+
}
|
|
245
|
+
function extractLastLocation(delivery) {
|
|
246
|
+
const latest = getLatestEvent(delivery);
|
|
247
|
+
if (!latest) {
|
|
248
|
+
return "";
|
|
249
|
+
}
|
|
250
|
+
return typeof latest.location === "string" ? latest.location : "";
|
|
251
|
+
}
|
|
252
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
253
|
+
0 && (module.exports = {
|
|
254
|
+
TRACKABLE_STATUSES,
|
|
255
|
+
calculateCombinedWindow,
|
|
256
|
+
calculateDeliveryEstimate,
|
|
257
|
+
calculateDeliveryWindow,
|
|
258
|
+
computeDiffDays,
|
|
259
|
+
extractLastLocation,
|
|
260
|
+
formatLastEvent,
|
|
261
|
+
formatWindow,
|
|
262
|
+
isToday,
|
|
263
|
+
parseExpectedToMs,
|
|
264
|
+
windowBoundsMs
|
|
265
|
+
});
|
|
266
|
+
//# sourceMappingURL=delivery-view.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/delivery-view.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Everything that turns a raw parcel.app delivery into the strings the datapoints show:\n * the expected-delivery window, the human-readable estimate and the latest tracking event.\n *\n * Split out of `StateManager` in v0.11.2 (audit A1). The class had two reasons to change \u2014\n * \"parcel.app reports a new date format\" and \"ioBroker writes objects differently\" \u2014 and only the\n * second one has anything to do with the broker. Nothing here touches the adapter or the object\n * DB; the only outside contact is the optional drift logger and the i18n lookup for the estimate\n * wording.\n */\nimport { coerceFiniteNumber } from \"./coerce\";\nimport { tText } from \"./i18n\";\nimport type { ParcelDelivery, ParcelEvent } from \"./types\";\n\n/** Status codes that have an expected delivery date/time: 2=In Transit, 4=Out for Delivery, 8=Info Received */\nexport const TRACKABLE_STATUSES = new Set([2, 4, 8]);\n\n/**\n * Optional trace sink. Every parse that gives up says why \u2014 the adapter logs API drift everywhere\n * else (`parseStatus`, carrier lookup, response shape), and this was the one place that stayed\n * silent. An empty `deliveryWindow` with no log line is exactly what made the v0.8.0 window bug\n * hard to find (audit B2).\n */\nexport interface DriftLogger {\n /** Adapter debug log. One line per value that could not be parsed. */\n debug(message: string): void;\n}\n\n/**\n * Full English month names, lower-cased \u2192 0-based month index. Only complete names are accepted:\n * an abbreviation would invite the ambiguity this parser deliberately refuses elsewhere.\n */\nconst MONTH_NAMES: Record<string, number> = {\n january: 0,\n february: 1,\n march: 2,\n april: 3,\n may: 4,\n june: 5,\n july: 6,\n august: 7,\n september: 8,\n october: 9,\n november: 10,\n december: 11,\n};\n\n/** `YYYY-MM-DD`, optionally followed by `HH:MM[:SS]` after a space or `T`. The documented default. */\nconst ISO_LIKE_RE = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[ T](\\d{2}):(\\d{2})(?::(\\d{2}))?)?$/;\n/** `Month D, YYYY`, optionally followed by `H:MM[:SS]`. Unambiguous \u2014 the month is spelled out. */\nconst MONTH_NAME_RE = /^([A-Za-z]+)\\s+(\\d{1,2}),\\s*(\\d{4})(?:\\s+(\\d{1,2}):(\\d{2})(?::(\\d{2}))?)?$/;\n\n/** Date/time components pulled out of a raw string before they are range-checked. */\ninterface DateParts {\n year: number;\n /** 0-based, like `Date`. */\n month: number;\n day: number;\n hour: number;\n minute: number;\n second: number;\n /** Whether the string carried a clock at all (midnight still counts as \"no time of day\"). */\n hasClock: boolean;\n}\n\n/**\n * Pull the calendar components out of a raw string, without judging their ranges.\n *\n * Two formats are accepted, both unambiguous. The dotted (`dd.MM.yyyy` vs `MM.dd.yyyy`) and\n * weekday-only forms other clients guess at are deliberately NOT parsed \u2014 a wrong date is worse\n * than none. They now leave a drift line instead of vanishing silently.\n *\n * @param raw Trimmed date/time string from the API\n * @returns the components, or null when neither format matches\n */\nfunction matchDateParts(raw: string): DateParts | null {\n const iso = ISO_LIKE_RE.exec(raw);\n if (iso) {\n return {\n year: Number(iso[1]),\n month: Number(iso[2]) - 1,\n day: Number(iso[3]),\n hour: iso[4] !== undefined ? Number(iso[4]) : 0,\n minute: iso[5] !== undefined ? Number(iso[5]) : 0,\n second: iso[6] !== undefined ? Number(iso[6]) : 0,\n hasClock: iso[4] !== undefined,\n };\n }\n const named = MONTH_NAME_RE.exec(raw);\n if (named) {\n const month = MONTH_NAMES[named[1].toLowerCase()];\n if (month === undefined) {\n return null;\n }\n return {\n year: Number(named[3]),\n month,\n day: Number(named[2]),\n hour: named[4] !== undefined ? Number(named[4]) : 0,\n minute: named[5] !== undefined ? Number(named[5]) : 0,\n second: named[6] !== undefined ? Number(named[6]) : 0,\n hasClock: named[4] !== undefined,\n };\n }\n return null;\n}\n\n/**\n * Parse a parcel.app expected-date string to LOCAL epoch-millis.\n *\n * The API delivers `date_expected`/`date_expected_end` \"without specific timezone information\";\n * the components are applied to the local calendar so the value lands on the intended local\n * day/time (`new Date(\"YYYY-MM-DD\")` would be UTC midnight). `hasTime` is false for a bare date or\n * a midnight time (a day, not an hour-window).\n *\n * @param value Raw date/time string from the API\n * @param log Optional trace sink \u2014 every rejected value leaves one line\n * @returns epoch millis plus whether a real time of day was given, or null when unparseable\n */\nexport function parseExpectedToMs(value: unknown, log?: DriftLogger): { ms: number; hasTime: boolean } | null {\n if (typeof value !== \"string\") {\n // undefined is the normal \"carrier reports nothing\" case and must stay quiet; anything else\n // present but wrong-typed is drift worth seeing.\n if (value !== undefined && value !== null) {\n log?.debug(`expected-date drift: not a string (got ${typeof value})`);\n }\n return null;\n }\n const raw = value.trim();\n if (raw.length === 0) {\n return null;\n }\n const parts = matchDateParts(raw);\n if (!parts) {\n log?.debug(`expected-date drift: unsupported format '${raw}' \u2014 no window/estimate for this package`);\n return null;\n }\n // Range-validate the components. The patterns only check digit COUNT, not value range, and\n // `new Date(2026, 12, 40, 25, \u2026)` silently ROLLS OVER to a wrong date (getTime() is NOT NaN).\n if (parts.month < 0 || parts.month > 11 || parts.day < 1 || parts.day > 31) {\n log?.debug(`expected-date drift: month/day out of range in '${raw}'`);\n return null;\n }\n if (parts.hour > 23 || parts.minute > 59 || parts.second > 59) {\n log?.debug(`expected-date drift: time out of range in '${raw}'`);\n return null;\n }\n const date = new Date(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second);\n // Catch day-of-month overflow the range check misses (Feb 30, Apr 31, \u2026): a real date\n // round-trips the month and day it was built from.\n if (Number.isNaN(date.getTime()) || date.getMonth() !== parts.month || date.getDate() !== parts.day) {\n log?.debug(`expected-date drift: '${raw}' is not a real calendar date`);\n return null;\n }\n const hasTime = parts.hasClock && !(parts.hour === 0 && parts.minute === 0 && parts.second === 0);\n return { ms: date.getTime(), hasTime };\n}\n\n/**\n * Resolve a delivery's expected window to epoch-millis bounds. Returns null for a non-trackable\n * status or when there is no usable start time.\n *\n * Prefers the Unix timestamp fields; for carriers that report the window only as a date/time\n * string it falls back to those \u2014 but only when the string carries a real time of day (a bare date\n * or midnight is a day, not an hour-window). Carrier-agnostic.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n * @param log Optional trace sink for unparseable dates\n * @returns window bounds in epoch millis, or null\n */\nexport function windowBoundsMs(\n delivery: ParcelDelivery,\n statusCode: number,\n log?: DriftLogger,\n): { start: number; end: number | null } | null {\n if (!TRACKABLE_STATUSES.has(statusCode)) {\n return null;\n }\n const toMs = (timestamp: unknown): number | null => {\n const ts = coerceFiniteNumber(timestamp);\n if (ts === null || ts <= 0) {\n return null;\n }\n const ms = ts * 1000;\n return Number.isNaN(new Date(ms).getTime()) ? null : ms;\n };\n const dateMs = (value: unknown): number | null => {\n const parsed = parseExpectedToMs(value, log);\n return parsed && parsed.hasTime ? parsed.ms : null;\n };\n const start = toMs(delivery.timestamp_expected) ?? dateMs(delivery.date_expected);\n if (start === null) {\n return null;\n }\n const end = toMs(delivery.timestamp_expected_end) ?? dateMs(delivery.date_expected_end);\n return { start, end };\n}\n\n/**\n * Format epoch-millis as local HH:MM.\n *\n * @param ms Epoch milliseconds\n * @returns `HH:MM`\n */\nfunction formatHHMM(ms: number): string {\n const d = new Date(ms);\n return `${d.getHours().toString().padStart(2, \"0\")}:${d.getMinutes().toString().padStart(2, \"0\")}`;\n}\n\n/**\n * Local \"MM-DD HH:MM\" \u2014 used when a window spans more than one calendar day.\n *\n * @param ms Epoch milliseconds\n * @returns `MM-DD HH:MM`\n */\nfunction formatDateHHMM(ms: number): string {\n const d = new Date(ms);\n const mm = (d.getMonth() + 1).toString().padStart(2, \"0\");\n const dd = d.getDate().toString().padStart(2, \"0\");\n return `${mm}-${dd} ${formatHHMM(ms)}`;\n}\n\n/**\n * Whether two epoch-millis fall on the same LOCAL calendar day.\n *\n * @param aMs First epoch milliseconds\n * @param bMs Second epoch milliseconds\n * @returns true when both are the same local day\n */\nfunction sameLocalDay(aMs: number, bMs: number): boolean {\n const a = new Date(aMs);\n const b = new Date(bMs);\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\n/**\n * Format a start\u2192end window as a local string. A real end (> start) on the SAME day renders\n * \"HH:MM - HH:MM\"; an end on a LATER day carries the date on both sides (\"12-06 14:30 - 12-08\n * 18:30\") so a multi-day window is not shown as if it were same-day. No end, or an end <= start\n * (reversed/equal), renders just the start.\n *\n * @param startMs Window start (epoch ms)\n * @param endMs Window end (epoch ms) or null\n * @returns the formatted window\n */\nexport function formatWindow(startMs: number, endMs: number | null): string {\n if (endMs === null || endMs <= startMs) {\n return formatHHMM(startMs);\n }\n return sameLocalDay(startMs, endMs)\n ? `${formatHHMM(startMs)} - ${formatHHMM(endMs)}`\n : `${formatDateHHMM(startMs)} - ${formatDateHHMM(endMs)}`;\n}\n\n/**\n * Calculate a delivery time-window string from the resolved expected bounds.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n * @param log Optional trace sink for unparseable dates\n * @returns the window string, or \"\" when there is none\n */\nexport function calculateDeliveryWindow(delivery: ParcelDelivery, statusCode: number, log?: DriftLogger): string {\n const bounds = windowBoundsMs(delivery, statusCode, log);\n if (!bounds) {\n return \"\";\n }\n return formatWindow(bounds.start, bounds.end);\n}\n\n/**\n * Days from today to the expected delivery date. Returns null when the delivery has no usable\n * expected date or is in a non-trackable status.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n * @param log Optional trace sink for unparseable dates\n * @returns whole days from today, or null\n */\nexport function computeDiffDays(delivery: ParcelDelivery, statusCode: number, log?: DriftLogger): number | null {\n if (!TRACKABLE_STATUSES.has(statusCode)) {\n return null;\n }\n\n let expectedDate: Date | null = null;\n const ts = coerceFiniteNumber(delivery.timestamp_expected);\n if (ts !== null && ts > 0) {\n expectedDate = new Date(ts * 1000);\n } else {\n // Shares the window's date parser (one source of format-truth). Only the calendar day matters\n // here, so the time-of-day flag is ignored; the local-component parse keeps the day\n // timezone-stable.\n const parsed = parseExpectedToMs(delivery.date_expected, log);\n expectedDate = parsed ? new Date(parsed.ms) : null;\n }\n\n if (!expectedDate || Number.isNaN(expectedDate.getTime())) {\n return null;\n }\n\n const now = new Date();\n const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const expectedStart = new Date(expectedDate.getFullYear(), expectedDate.getMonth(), expectedDate.getDate());\n return Math.round((expectedStart.getTime() - todayStart.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Calculate the human-readable delivery estimate in the system language.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n * @param log Optional trace sink for unparseable dates\n * @returns the estimate wording, or \"\" when there is no usable date\n */\nexport function calculateDeliveryEstimate(delivery: ParcelDelivery, statusCode: number, log?: DriftLogger): string {\n const diffDays = computeDiffDays(delivery, statusCode, log);\n if (diffDays === null) {\n return \"\";\n }\n if (diffDays < 0) {\n return tText(\"estimateOverdue\");\n }\n if (diffDays === 0) {\n return tText(\"estimateToday\");\n }\n if (diffDays === 1) {\n return tText(\"estimateTomorrow\");\n }\n return tText(\"estimateDays\", diffDays);\n}\n\n/**\n * Whether the delivery is expected today. Language-agnostic, so the summary filter works in every\n * language.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n * @param log Optional trace sink for unparseable dates\n * @returns true when the expected date is today\n */\nexport function isToday(delivery: ParcelDelivery, statusCode: number, log?: DriftLogger): boolean {\n return computeDiffDays(delivery, statusCode, log) === 0;\n}\n\n/** A delivery together with its already-parsed status code \u2014 avoids re-parsing at every call site. */\nexport interface StatusedDelivery {\n /** The delivery data */\n delivery: ParcelDelivery;\n /** Pre-parsed status code */\n statusCode: number;\n}\n\n/**\n * Combined delivery window for today's packages: earliest start to latest end across all windows.\n * Computed from the raw millis (not the formatted strings) so the latest end always wins \u2014 fixes\n * the earlier bug where the end of the latest-*starting* window was used instead of the maximum\n * end.\n *\n * @param todayDeliveries Deliveries expected today, with their parsed status codes\n * @param log Optional trace sink for unparseable dates\n * @returns the combined window string, or \"\" when no package reports one\n */\nexport function calculateCombinedWindow(todayDeliveries: StatusedDelivery[], log?: DriftLogger): string {\n const bounds = todayDeliveries\n .map(e => windowBoundsMs(e.delivery, e.statusCode, log))\n .filter((b): b is { start: number; end: number | null } => b !== null);\n\n if (bounds.length === 0) {\n return \"\";\n }\n\n // L3: fold instead of Math.min/max(...spread). The bounds array is capped by the 1 MiB response\n // limit, but a spread over a large array can still hit V8's argument-count limit (RangeError);\n // reduce is O(n) and unbounded-safe \u2014 consistent with beszel's computeMaxTemp hardening.\n const minStart = bounds.reduce((m, b) => (b.start < m ? b.start : m), bounds[0].start);\n const maxEnd = bounds.reduce((m, b) => {\n const e = b.end ?? b.start;\n return e > m ? e : m;\n }, bounds[0].end ?? bounds[0].start);\n return formatWindow(minStart, maxEnd);\n}\n\n/**\n * Newest tracking event of a delivery \u2014 `events[0]`, confirmed across several parcel.app clients.\n *\n * @param delivery The delivery data\n * @returns the newest event, or null when there is none\n */\nfunction getLatestEvent(delivery: ParcelDelivery): ParcelEvent | null {\n if (!Array.isArray(delivery.events) || delivery.events.length === 0) {\n return null;\n }\n const latest = delivery.events[0];\n if (!latest || typeof latest !== \"object\") {\n return null;\n }\n return latest;\n}\n\n/**\n * The newest tracking event as \"<event> - <date>\", dropping whichever half the carrier omitted.\n *\n * @param delivery The delivery data\n * @returns the formatted event, or \"\" when there is none\n */\nexport function formatLastEvent(delivery: ParcelDelivery): string {\n const latest = getLatestEvent(delivery);\n if (!latest) {\n return \"\";\n }\n const parts: string[] = [];\n if (typeof latest.event === \"string\" && latest.event.length > 0) {\n parts.push(latest.event);\n }\n if (typeof latest.date === \"string\" && latest.date.length > 0) {\n parts.push(latest.date);\n }\n return parts.join(\" - \");\n}\n\n/**\n * Location of the newest tracking event.\n *\n * @param delivery The delivery data\n * @returns the location, or \"\" when the carrier reports none\n */\nexport function extractLastLocation(delivery: ParcelDelivery): string {\n const latest = getLatestEvent(delivery);\n if (!latest) {\n return \"\";\n }\n return typeof latest.location === \"string\" ? latest.location : \"\";\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["_a"]
|
|
7
|
+
}
|