iobroker.parcelapp 0.10.2 → 0.10.3

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
@@ -140,6 +140,10 @@ sendTo("parcelapp.0", "addDelivery", {
140
140
  Placeholder for the next version (at the beginning of the line):
141
141
  ### **WORK IN PROGRESS**
142
142
  -->
143
+ ### 0.10.3 (2026-08-27)
144
+
145
+ - 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.
146
+
143
147
  ### 0.10.2 (2026-08-22)
144
148
 
145
149
  - Changed: Internal cleanup. No user-facing changes.
@@ -164,11 +168,6 @@ sendTo("parcelapp.0", "addDelivery", {
164
168
  - Fixed: tracked packages could disappear from the object tree after a temporary update error or an unexpected API response — a package is now kept until parcel.app actually stops returning it.
165
169
  - Changed: multi-day delivery windows now show the date on each side (e.g. `12-06 14:30 - 12-08 18:30`) instead of looking same-day; out-of-range or reversed dates no longer produce a misleading window.
166
170
 
167
- ### 0.8.0 (2026-06-19)
168
-
169
- - The delivery window is now also shown for carriers that report it only as a date/time range, not just when the API provides a Unix timestamp.
170
- - When adding a delivery via script, you can now set an optional tracking language and request a push confirmation.
171
-
172
171
  [Older changelogs can be found there](CHANGELOG_OLD.md)
173
172
 
174
173
  ## Support
package/build/main.js CHANGED
@@ -116,8 +116,43 @@ class ParcelappAdapter extends utils.Adapter {
116
116
  this.on("unload", this.onUnload.bind(this));
117
117
  this.on("message", this.onMessage.bind(this));
118
118
  }
119
+ /**
120
+ * Switch off `supportedMessages.stopInstance` on this instance's own object.
121
+ *
122
+ * The entry was dropped from the manifest, which only helps a FRESH install: an upgrade
123
+ * merges the manifest into the existing instance object and never removes a key, so the old
124
+ * `true` survives in the database — and that is what the host reads. With it the host kills
125
+ * the process one second after asking it to stop, `onUnload` never runs, and the
126
+ * `info.connection=false` write on the way out never reaches the database.
127
+ *
128
+ * Only written when it is actually still on: every instance-object change restarts the
129
+ * instance, so doing it unconditionally would be a restart loop.
130
+ *
131
+ * @returns true when the correction was written and the restart is coming — the caller has
132
+ * to stop right there instead of polling in a process that is going down.
133
+ */
134
+ async clearStopInstanceFlag() {
135
+ var _a;
136
+ const id = `system.adapter.${this.namespace}`;
137
+ try {
138
+ const obj = await this.getForeignObjectAsync(id);
139
+ const supported = (_a = obj == null ? void 0 : obj.common) == null ? void 0 : _a.supportedMessages;
140
+ if (!(supported == null ? void 0 : supported.stopInstance)) {
141
+ return false;
142
+ }
143
+ this.log.info("Correcting a leftover setting from an earlier version \u2014 this instance restarts once");
144
+ await this.extendForeignObjectAsync(id, { common: { supportedMessages: { stopInstance: false } } });
145
+ return true;
146
+ } catch (err) {
147
+ this.log.debug(`Could not check the instance object ${id}: ${(0, import_coerce.errText)(err)}`);
148
+ return false;
149
+ }
150
+ }
119
151
  async onReady() {
120
152
  try {
153
+ if (await this.clearStopInstanceFlag()) {
154
+ return;
155
+ }
121
156
  await import_adapter_core.I18n.init((0, import_node_path.join)(this.adapterDir, "admin"), this);
122
157
  this.log.debug(`onReady: starting (autoRemoveDelivered=${this.config.autoRemoveDelivered})`);
123
158
  await this.setState("info.connection", { val: false, ack: true });
@@ -173,13 +208,13 @@ class ParcelappAdapter extends utils.Adapter {
173
208
  }
174
209
  this.testClients.clear();
175
210
  void this.setState("info.connection", { val: false, ack: true }).catch(() => {
176
- });
211
+ }).finally(callback);
212
+ return;
177
213
  } catch (err) {
178
214
  try {
179
215
  this.log.debug(`onUnload error (ignored): ${(0, import_coerce.errText)(err)}`);
180
216
  } catch {
181
217
  }
182
- } finally {
183
218
  callback();
184
219
  }
185
220
  }
package/build/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { I18n } from \"@iobroker/adapter-core\";\nimport { join } from \"node:path\";\nimport { coerceClampedInt, errText, isTrueish, oneLine } from \"./lib/coerce\";\nimport { ParcelClient, RETRY_AFTER_DEFAULT_SEC, RETRY_AFTER_MAX_SEC } from \"./lib/parcel-client\";\nimport { StateManager } from \"./lib/state-manager\";\nimport { DELIVERED_STATUS_CODE } from \"./lib/types\";\nimport type { AddDeliveryRequest } from \"./lib/types\";\n\nconst MIN_POLL_INTERVAL = 5;\nconst MAX_POLL_INTERVAL = 60;\nconst DEFAULT_POLL_INTERVAL = 10;\n// Minimum 60s between polls. Also the natural pacing after addDelivery: a\n// typical single add still polls immediately (the last poll is >60s ago),\n// while a burst of adds collapses to at most one extra GET per minute.\nconst MIN_POLL_GAP_MS = 60_000;\n/** v0.4.2 (M6): minimum length for an apiKey value to even be considered valid. */\nconst MIN_API_KEY_LENGTH = 10;\n// v0.9.0 (S5): cap addDelivery field lengths. A sendTo caller is local, but a\n// runaway script must not push a multi-MB POST body to parcel.app. Identifiers\n// are short; this is generous for a description.\nconst MAX_ADD_FIELD_LEN = 512;\n// v0.9.0 (S3): cap the per-poll broker fan-out. Updates run in batches of this\n// size instead of all deliveries at once, so an abnormally large API response\n// can't flood the broker with thousands of concurrent writes.\nconst UPDATE_BATCH_SIZE = 25;\n// v0.9.0 (S4): client-side throttle on addDelivery POSTs. parcel.app enforces\n// ~20 POST/day server-side; this caps a runaway/buggy script's burst so it can't\n// hammer the API. The window is generous enough never to block a real batch-add\n// (the daily server limit is the real cap).\nconst MAX_ADDS_PER_WINDOW = 20;\nconst ADD_WINDOW_MS = 60_000;\n\n/**\n * Node transport-level error codes treated as (transient) NETWORK problems:\n * DNS, connection refused/reset, unreachable \u2014 plus EPIPE/ECONNABORTED/EPROTO\n * (v0.10.0, I9), which belong to the same warn+keep-retrying class.\n */\nconst NETWORK_ERROR_CODES = new Set([\n \"ENOTFOUND\",\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"ENETUNREACH\",\n \"EHOSTUNREACH\",\n \"EAI_AGAIN\",\n \"EPIPE\",\n \"ECONNABORTED\",\n \"EPROTO\",\n]);\n\n/**\n * v0.10.0 (L12): the seam contracts name exactly the members main uses \u2014\n * structural Picks let the orchestration tests inject plain object fakes\n * without unsafe double-casts, and the compiler documents what a fake must\n * provide.\n */\ntype ClientLike = Pick<\n ParcelClient,\n \"getDeliveries\" | \"getCarrierName\" | \"addDelivery\" | \"testConnection\" | \"cancelAll\"\n>;\ntype StateManagerLike = Pick<\n StateManager,\n \"resetPollState\" | \"packageId\" | \"parseStatus\" | \"updateDelivery\" | \"updateSummary\" | \"cleanupDeliveries\"\n>;\n\n/**\n * ioBroker adapter for parcel.app package tracking. Exported so the\n * orchestration unit tests can drive its lifecycle/poll handlers directly.\n */\nexport class ParcelappAdapter extends utils.Adapter {\n private client: ClientLike | null = null;\n private stateManager: StateManagerLike | null = null;\n /**\n * Factories for the HTTP client + state manager \u2014 default to the real\n * constructors. Test seams (fleet pattern): unit tests replace these with\n * fakes to exercise the poll orchestration (throttle/rate-limit interplay,\n * error routing, failure dedup) without real network.\n *\n * @param apiKey parcel.app API key\n */\n private makeClient: (apiKey: string) => ClientLike = apiKey =>\n new ParcelClient(apiKey, { debug: (m: string) => this.log.debug(m) });\n private makeStateManager: () => StateManagerLike = () => new StateManager(this);\n private pollTimer: ioBroker.Interval | undefined = undefined;\n private isPolling = false;\n private lastPollTime = 0;\n private rateLimitedUntil = 0;\n private lastErrorCode = \"\";\n /**\n * v0.10.0 (L2): set in onUnload. onReady checks it after its awaits so a\n * stop during the first poll can no longer arm the interval or log\n * \"started\" after the unload already ran; batch failures during shutdown\n * degrade to debug.\n */\n private unloaded = false;\n /**\n * Package ids (not raw tracking numbers) whose last updateDelivery failed.\n * Keyed like the states so the dedup survives a sanitize collision or a\n * missing tracking number; pruned each poll against the visible pkgIds.\n */\n private failedDeliveries = new Set<string>();\n /** Timestamps of recent addDelivery POSTs \u2014 the S4 throttle window. */\n private addTimestamps: number[] = [];\n /**\n * L2: true while a checkConnection test GET is in flight. A test hits the same\n * 20/hour GET budget as polling; this guards against a concurrent second test\n * (double-click / admin re-render) stacking a redundant GET. A sequential\n * re-test after the current one settles runs normally.\n */\n private testConnectionInFlight = false;\n /**\n * v0.4.4: short-lived test-clients spawned from `checkConnection` admin\n * messages. The prod-`this.client` is what `onUnload` cancels, so these\n * need their own registry to be reachable at shutdown. Without this, an\n * admin clicking \"Test Connection\" right before adapter-stop could keep\n * the process alive past js-controller's 4-second kill deadline.\n */\n private testClients = new Set<ClientLike>();\n\n /** @param options Adapter options */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"parcelapp\",\n });\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n this.on(\"message\", this.onMessage.bind(this));\n }\n\n private async onReady(): Promise<void> {\n try {\n // I18n.init resolves system.config.language itself (adapter-core reads\n // the foreign object internally; unknown languages fall back to English)\n // \u2014 the former separate getForeignObject round-trip and the local\n // resolveLanguage step are gone with the STATUS_LABELS table (L20).\n await I18n.init(join(this.adapterDir, \"admin\"), this);\n this.log.debug(`onReady: starting (autoRemoveDelivered=${this.config.autoRemoveDelivered})`);\n\n await this.setState(\"info.connection\", { val: false, ack: true });\n\n const { apiKey } = this.config;\n if (!apiKey || apiKey.trim().length < MIN_API_KEY_LENGTH) {\n this.log.error(\"No valid API key configured \u2014 please enter your parcel.app API key in the adapter settings\");\n return;\n }\n\n this.client = this.makeClient(apiKey.trim());\n this.stateManager = this.makeStateManager();\n\n try {\n await this.cleanupObsoleteStates();\n } catch (err) {\n // C8: a cleanup failure must not abort startup. Without this guard the\n // outer catch would skip arming the poll interval below, and the adapter\n // would never poll until a manual restart. Degrade to a warning.\n this.log.warn(`cleanupObsoleteStates failed (continuing): ${errText(err)}`);\n }\n\n await this.poll();\n\n // v0.10.0 (L2): a stop during the awaits above must not arm a timer or\n // log a start line after onUnload already ran.\n if (this.unloaded) {\n return;\n }\n\n const interval = ParcelappAdapter.coercePollInterval(this.config.pollInterval);\n this.log.debug(`pollInterval: raw=${JSON.stringify(this.config.pollInterval)} resolved=${interval}min`);\n const intervalMs = interval * 60 * 1000;\n this.pollTimer = this.setInterval(() => {\n void this.poll().catch(err => this.log.error(`Scheduled poll failed: ${errText(err)}`));\n }, intervalMs);\n\n this.log.info(`Parcel tracking started \u2014 polling every ${interval} minutes`);\n } catch (err: unknown) {\n this.log.error(`onReady failed: ${errText(err)}`);\n // v0.10.0 (L4): a transient startup failure (i18n files, DB hiccup) used\n // to leave a green-looking zombie \u2014 no client, no timer, no retry until\n // a manual restart. Terminate instead: js-controller restarts the\n // instance, which self-heals the transient case; its restart-loop guard\n // backstops a persistent one.\n if (!this.unloaded) {\n this.terminate(\"startup failed \u2014 requesting restart\", utils.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);\n }\n }\n }\n\n /**\n * v0.4.2 (M5+X5): delegate to the shared `coerceClampedInt` helper.\n *\n * @param raw Raw `pollInterval` from admin config (number or numeric string).\n */\n private static coercePollInterval(raw: unknown): number {\n return coerceClampedInt(raw, MIN_POLL_INTERVAL, MAX_POLL_INTERVAL, DEFAULT_POLL_INTERVAL);\n }\n\n private onUnload(callback: () => void): void {\n this.unloaded = true;\n try {\n if (this.pollTimer) {\n this.clearInterval(this.pollTimer);\n this.pollTimer = undefined;\n }\n // v0.4.2 (M11+P1): cancel every in-flight HTTPS request so a slow\n // parcel.app endpoint doesn't keep the adapter alive past\n // js-controller's 4-second kill deadline. cancelAll also marks the\n // client terminal (v0.10.0, L3) \u2014 requests started after this point\n // are refused instead of opening fresh connections.\n this.client?.cancelAll();\n // v0.4.4: also abort any short-lived test-client (from checkConnection)\n // whose HTTPS-request might still be inflight at shutdown \u2014 the prod\n // `this.client.cancelAll()` only touches the production-client.\n for (const tc of this.testClients) {\n tc.cancelAll();\n }\n this.testClients.clear();\n // v0.4.2 (M10): explicit `.catch(() => {})` on the fire-and-forget so\n // a broker-already-down doesn't leak as an unhandled rejection.\n void this.setState(\"info.connection\", { val: false, ack: true }).catch(() => {\n /* broker is shutting down \u2014 ignore */\n });\n } catch (err) {\n // v0.4.3 (G4): replace silent `// ignore` with a trace so shutdown\n // errors leave a debug breadcrumb. Broker-already-down errors here\n // are expected \u2014 debug-level keeps them out of the user log.\n try {\n this.log.debug(`onUnload error (ignored): ${errText(err)}`);\n } catch {\n /* logger already gone \u2014 nothing left to report to */\n }\n } finally {\n // v0.10.0 (I7): the callback is the contract with js-controller \u2014\n // structurally guaranteed exactly once, even if the catch-path throws.\n callback();\n }\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n try {\n // v0.4.3 (F1): entry log BEFORE the early-return \u2014 broadcast messages\n // without callback wouldn't be visible otherwise. Inside the try since\n // v0.10.0 (I6) so the handler body is gap-free top-level guarded.\n this.log.debug(\n `onMessage: command='${oneLine(String(obj?.command ?? \"\"))}' from='${obj?.from}' has-callback=${!!obj?.callback}`,\n );\n if (!obj?.command || !obj.callback) {\n return;\n }\n\n switch (obj.command) {\n case \"checkConnection\":\n await this.handleCheckConnection(obj);\n break;\n case \"addDelivery\":\n await this.handleAddDelivery(obj);\n break;\n default:\n // v0.4.3 (F6): trace unknown command before sendTo.\n this.log.debug(`onMessage: unknown command '${oneLine(String(obj.command))}'`);\n this.sendTo(obj.from, obj.command, { error: \"Unknown command\" }, obj.callback);\n }\n } catch (err) {\n // v0.4.3 (F7): trace catch so the debug log shows what failed. The\n // reply itself is guarded too (v0.10.0, I6) \u2014 a synchronous sendTo\n // throw must not escape the message handler. checkConnection replies\n // use the admin {error} envelope (H1), scripts keep the documented\n // {success, error_message} shape.\n try {\n this.log.debug(`onMessage: '${oneLine(String(obj?.command ?? \"\"))}' failed: ${errText(err)}`);\n if (obj?.callback) {\n const reply =\n obj.command === \"checkConnection\"\n ? { error: errText(err) }\n : { success: false, error_message: errText(err) };\n this.sendTo(obj.from, obj.command, reply, obj.callback);\n }\n } catch {\n /* reply channel gone \u2014 nothing left to do */\n }\n }\n }\n\n /**\n * Admin \"Test Connection\" button (H1). The jsonConfig sendTo component reads\n * ONLY `response.error` / `response.result` \u2014 never success/message \u2014 so the\n * internal `{success, message}` result is mapped to that contract here.\n * Before this, a FAILED test showed a false-positive \"Ok\" in the admin\n * (fleet fix; beszel's message-router is the model).\n *\n * @param obj The sendTo message (validated: command + callback present)\n */\n private async handleCheckConnection(obj: ioBroker.Message): Promise<void> {\n const msg = obj.message as { apiKey?: string };\n const key = msg?.apiKey?.trim() || \"\";\n if (!key || key.length < MIN_API_KEY_LENGTH) {\n // v0.4.3 (F2): trace the reject before sendTo.\n this.log.debug(\"checkConnection: apiKey too short\");\n this.sendTo(obj.from, obj.command, { error: \"API key is too short\" }, obj.callback);\n return;\n }\n // L2: a Test-Connection GET counts against the same 20/hour budget as\n // polling. Guard against a concurrent second test (double-click / admin\n // re-render) so stacked clicks can't each burn a GET (which could later trip\n // the poll's rate-limit cooldown). Set BEFORE the await so the check is\n // synchronous against a still-in-flight first test.\n if (this.testConnectionInFlight) {\n this.log.debug(\"checkConnection: a test is already running\");\n this.sendTo(obj.from, obj.command, { error: \"A connection test is already running \u2014 please wait\" }, obj.callback);\n return;\n }\n this.testConnectionInFlight = true;\n // v0.4.3: same debug-logger as the prod client so checkConnection\n // failures get the same HTTPS-layer trace (via the makeClient seam).\n const testClient = this.makeClient(key);\n // v0.4.4: register test-client so onUnload can abort its inflight\n // HTTPS-request \u2014 the adapter's `this.client.cancelAll()` only\n // touches the prod-client, not these short-lived test-clients.\n this.testClients.add(testClient);\n try {\n const result = await testClient.testConnection();\n // v0.4.3 (F3): trace checkConnection result.\n this.log.debug(`checkConnection: result=${result.success ? \"ok\" : \"fail\"} (${result.message})`);\n this.sendTo(\n obj.from,\n obj.command,\n result.success ? { result: result.message } : { error: result.message },\n obj.callback,\n );\n } finally {\n this.testClients.delete(testClient);\n this.testConnectionInFlight = false;\n }\n }\n\n /**\n * Reply an addDelivery failure to the sendTo caller. This is the documented\n * script API envelope (`{success: false, error_message}`) \u2014 unchanged for\n * backward compatibility; only the admin checkConnection uses {error}.\n *\n * @param obj The sendTo message being answered\n * @param message Human-readable failure reason\n */\n private replyAddError(obj: ioBroker.Message, message: string): void {\n this.sendTo(obj.from, obj.command, { success: false, error_message: message }, obj.callback);\n }\n\n /**\n * Script-facing addDelivery command: validate the message shape, cap field\n * lengths, throttle bursts, forward to the API and trigger a poll on\n * success. Extracted from the onMessage switch (M9) \u2014 one command, one\n * method, one change reason.\n *\n * @param obj The sendTo message (validated: command + callback present)\n */\n private async handleAddDelivery(obj: ioBroker.Message): Promise<void> {\n if (!this.client) {\n // v0.4.3 (F4): trace addDelivery-before-init.\n this.log.debug(\"addDelivery: adapter not initialized\");\n this.replyAddError(obj, \"Adapter not initialized\");\n return;\n }\n // v0.7.2: obj.message is `unknown`-shaped \u2014 a script calling\n // sendTo(\"parcelapp\", \"addDelivery\", null) used to surface as an\n // ugly TypeError through the catch instead of a clear validation\n // message. Coerce to a plain object and validate required fields.\n const raw = obj.message;\n const msg = raw !== null && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n if (\n typeof msg.tracking_number !== \"string\" ||\n msg.tracking_number.length === 0 ||\n typeof msg.carrier_code !== \"string\" ||\n msg.carrier_code.length === 0 ||\n typeof msg.description !== \"string\" ||\n msg.description.length === 0\n ) {\n this.log.debug(\"addDelivery: missing tracking_number/carrier_code/description in message\");\n this.replyAddError(obj, \"tracking_number, carrier_code and description are required\");\n return;\n }\n // v0.9.0 (S5): cap field lengths so a runaway local script can't push\n // a multi-MB POST body to parcel.app. Checked after the required-field\n // guard above so the two validation messages stay distinct.\n if (\n msg.tracking_number.length > MAX_ADD_FIELD_LEN ||\n msg.carrier_code.length > MAX_ADD_FIELD_LEN ||\n msg.description.length > MAX_ADD_FIELD_LEN ||\n (typeof msg.language === \"string\" && msg.language.length > MAX_ADD_FIELD_LEN)\n ) {\n this.log.debug(\"addDelivery: a field exceeds the maximum length\");\n this.replyAddError(obj, `each field must be at most ${MAX_ADD_FIELD_LEN} characters`);\n return;\n }\n // Pass the optional API fields through when the caller supplies them\n // (language: ISO 639-1 two-letter code; send_push_confirmation: push\n // notification once the delivery is added).\n const request: AddDeliveryRequest = {\n tracking_number: msg.tracking_number,\n carrier_code: msg.carrier_code,\n description: msg.description,\n };\n if (typeof msg.language === \"string\" && msg.language.length > 0) {\n request.language = msg.language;\n }\n if (typeof msg.send_push_confirmation === \"boolean\") {\n request.send_push_confirmation = msg.send_push_confirmation;\n }\n // v0.9.0 (S4): throttle addDelivery POSTs. parcel.app caps ~20/day\n // server-side; this stops a runaway/buggy script from hammering the API\n // with a burst. Record the attempt before the await so concurrent\n // callers count too.\n const nowMs = Date.now();\n this.addTimestamps = this.addTimestamps.filter(t => nowMs - t < ADD_WINDOW_MS);\n if (this.addTimestamps.length >= MAX_ADDS_PER_WINDOW) {\n this.log.warn(`addDelivery throttled: more than ${MAX_ADDS_PER_WINDOW} requests within ${ADD_WINDOW_MS / 1000}s`);\n this.replyAddError(obj, `too many addDelivery requests; max ${MAX_ADDS_PER_WINDOW} per ${ADD_WINDOW_MS / 1000}s`);\n return;\n }\n this.addTimestamps.push(nowMs);\n const addResult = await this.client.addDelivery(request);\n // v0.4.3 (F5): trace addDelivery result with the (flattened) tracking number.\n // v0.10.0 (L9): the drift-guarded isTrueish \u2014 getDeliveries hardens the same\n // API flag; a drifted `success: \"false\"` string must not read as truthy.\n const added = isTrueish(addResult.success);\n this.log.debug(`addDelivery: '${oneLine(request.tracking_number)}' result=${added ? \"ok\" : \"fail\"}`);\n this.sendTo(obj.from, obj.command, addResult, obj.callback);\n if (added) {\n // v0.10.0 (L5): plain poll, no force \u2014 a single add still polls right\n // away (the last poll is usually >60s back), but an add-burst can no\n // longer stack force-GETs past the 20/h API budget. Nothing is lost:\n // the server caches the list ~45-90 min, a fresh package rarely shows\n // tracking data immediately anyway.\n void this.poll().catch(err => this.log.error(`Poll after addDelivery failed: ${errText(err)}`));\n }\n }\n\n private async cleanupObsoleteStates(): Promise<void> {\n // One getObject per adapter start, forever \u2014 deliberately kept (I3/ARCH-24):\n // a one-shot migration marker would cost more mechanics than this read.\n // Drop the list entirely at the next major once 0.1.x installs are gone.\n const obsoleteStates = [\n \"summary.json\", // removed in 0.2.0\n ];\n for (const stateId of obsoleteStates) {\n const obj = await this.getObjectAsync(stateId);\n if (obj) {\n await this.delObjectAsync(stateId);\n this.log.debug(`Removed obsolete state: ${stateId}`);\n }\n }\n }\n\n /**\n * Classify an error for deduplication and log-level decisions.\n *\n * v0.10.0 (M1): the client codes every failure it raises (TIMEOUT,\n * PARSE_ERROR, ABORTED, RATE_LIMITED, \u2026) \u2014 a present machine code always\n * wins. The message-substring sniff only remains for code-less foreign\n * errors, so an API error_message merely CONTAINING \"timeout\" can no longer\n * be misclassified.\n *\n * @param error The error to classify\n */\n private classifyError(error: Error & { code?: string }): string {\n if (error.code) {\n if (NETWORK_ERROR_CODES.has(error.code)) {\n return \"NETWORK\";\n }\n if (error.code === \"ETIMEDOUT\") {\n return \"TIMEOUT\";\n }\n return error.code;\n }\n if (error.message.includes(\"timeout\")) {\n return \"TIMEOUT\";\n }\n return \"UNKNOWN\";\n }\n\n private async poll(): Promise<void> {\n if (this.isPolling || !this.client || !this.stateManager) {\n // v0.10.0 (M4): make the re-entry/uninitialized skip visible like the\n // rate-limit/throttle skips below \u2014 this used to be the one silent spot\n // where \"the adapter does nothing\" left no trace.\n this.log.debug(\"Skipping poll \u2014 already running or not initialized\");\n return;\n }\n // v0.10.0 (L14): local bindings instead of non-null assertions in the\n // closures below \u2014 provable narrowing the compiler checks, robust against\n // a future refactor nulling the fields mid-poll.\n const client = this.client;\n const stateManager = this.stateManager;\n\n const now = Date.now();\n // v0.4.3 (B1): poll-entry anchor \u2014 visible after the re-entry guard but\n // before the rate-limit/throttle skips. Shows mode + current error state\n // so the debug log gives context for whatever follows.\n const autoRemoveMode = this.config.autoRemoveDelivered !== false;\n this.log.debug(`poll: starting (autoRemove=${autoRemoveMode}, lastErrorCode='${this.lastErrorCode}')`);\n\n // Skip if rate limited\n if (now < this.rateLimitedUntil) {\n const waitMin = Math.ceil((this.rateLimitedUntil - now) / 60_000);\n this.log.debug(`Skipping poll \u2014 rate limited for ${waitMin} more minute(s)`);\n return;\n }\n\n // Throttle: minimum gap between polls (also paces the poll after a\n // successful addDelivery \u2014 see handleAddDelivery).\n if (now - this.lastPollTime < MIN_POLL_GAP_MS) {\n this.log.debug(\"Skipping poll \u2014 too soon after last poll\");\n return;\n }\n\n this.isPolling = true;\n this.lastPollTime = now;\n try {\n // When keeping delivered packages, use \"recent\" to get them from API\n const deliveries = await client.getDeliveries(autoRemoveMode ? \"active\" : \"recent\");\n\n // Reset error state on success\n this.rateLimitedUntil = 0;\n if (this.lastErrorCode) {\n this.log.info(\"Connection restored\");\n this.lastErrorCode = \"\";\n }\n await this.setStateChangedAsync(\"info.connection\", { val: true, ack: true });\n\n // Split into active (non-delivered) and visible (what gets states)\n const activeDeliveries = deliveries.filter(d => stateManager.parseStatus(d) !== DELIVERED_STATUS_CODE);\n const visibleDeliveries = autoRemoveMode ? activeDeliveries : deliveries;\n\n // v0.4.2 (S3): reset the per-poll collision tracker, then compute every\n // package id in a deterministic sequential pre-pass (stable array order)\n // BEFORE the parallel updates \u2014 collision-suffixing is then deterministic\n // and packageId runs exactly once per delivery instead of twice.\n stateManager.resetPollState();\n const pkgIds = visibleDeliveries.map(d => stateManager.packageId(d));\n\n // v0.4.2 (M4): per-delivery updates run in parallel, each wrapped in\n // try/catch so one bad delivery doesn't poison the others.\n // v0.9.0 (S3): process the updates in bounded batches instead of one\n // broker fan-out for ALL deliveries at once. The keep-set is still every\n // visible pkgId (computed above), so this only caps concurrency \u2014 it never\n // drops a package. A normal poll (a handful of packages) is a single batch.\n if (visibleDeliveries.length > UPDATE_BATCH_SIZE) {\n this.log.debug(`Updating ${visibleDeliveries.length} deliveries in batches of ${UPDATE_BATCH_SIZE}`);\n }\n for (let start = 0; start < visibleDeliveries.length; start += UPDATE_BATCH_SIZE) {\n const batch = visibleDeliveries.slice(start, start + UPDATE_BATCH_SIZE);\n await Promise.all(\n batch.map(async (delivery, offset) => {\n const pkgId = pkgIds[start + offset];\n // Pre-sanitize the externally-sourced strings once for logging. The\n // fields are optional (the API can drop them), so default to \"\" before\n // oneLine flattens them onto a single log line.\n const tracking = oneLine(delivery.tracking_number ?? \"\");\n const carrier = oneLine(delivery.carrier_code ?? \"\");\n try {\n // v0.4.3 (C1): per-delivery entry. ~10 packages \u00D7 144 polls/day\n // = ~1440 debug lines/day \u2014 acceptable at debug-level. Line stays\n // short (tracking + carrier + status only, no full delivery JSON).\n // v0.10.0 (M6): status_code is typed number|string (drift), so it\n // is flattened like the other externally-sourced fields.\n this.log.debug(\n `updateDelivery: '${tracking}' carrier=${carrier} status=${oneLine(String(delivery.status_code))}`,\n );\n const carrierName = await client.getCarrierName(delivery.carrier_code);\n await stateManager.updateDelivery(delivery, carrierName, pkgId);\n this.failedDeliveries.delete(pkgId);\n } catch (err) {\n const msg = errText(err);\n if (this.failedDeliveries.has(pkgId)) {\n this.log.debug(`Failed to update '${tracking}': ${msg}`);\n } else if (this.unloaded) {\n // v0.10.0 (L2): broker teardown mid-batch is expected noise\n // during shutdown, not a per-package warning.\n this.log.debug(`Failed to update '${tracking}' during shutdown: ${msg}`);\n } else {\n this.log.warn(`Failed to update '${tracking}': ${msg}`);\n this.failedDeliveries.add(pkgId);\n }\n }\n }),\n );\n }\n\n // v0.9.0 (C1): keep-set = EVERY package the API still returns this poll\n // (pkgIds), NOT only the writes that just succeeded. A transient\n // updateDelivery failure leaves a package's states stale, but it must not\n // drop the package from cleanup \u2014 that would delete a still-present\n // package's states (and any user-set device name) at green info.connection.\n // v0.10.0 (M2): broker-side failures in cleanup/summary are NOT API\n // failures \u2014 they must neither flip info.connection to false (the GET\n // above just succeeded) nor run through the API error classification.\n try {\n await stateManager.cleanupDeliveries(pkgIds);\n // Update summary (always uses active/non-delivered)\n await stateManager.updateSummary(activeDeliveries);\n } catch (err) {\n this.log.warn(`State maintenance failed (API connection is fine, retrying next poll): ${errText(err)}`);\n }\n\n // Keep failedDeliveries bounded: drop entries for package ids no longer\n // present, so packages that vanish from the API don't linger forever.\n const seenPkgIds = new Set(pkgIds);\n for (const id of [...this.failedDeliveries]) {\n if (!seenPkgIds.has(id)) {\n this.failedDeliveries.delete(id);\n }\n }\n\n this.log.debug(`Polled ${visibleDeliveries.length} deliveries (${activeDeliveries.length} active)`);\n } catch (err) {\n await this.handlePollError(err as Error & { code?: string; retryAfterSeconds?: number });\n } finally {\n this.isPolling = false;\n }\n }\n\n /**\n * Classify + route a poll failure: log level, dedup, cooldown and the\n * info.connection=false write. Extracted from poll()'s catch (M9) so the\n * happy path reads as a plain sequence and the error policy sits next to\n * classifyError. Dispatches on the CLASSIFIED code only (L6) \u2014 one source\n * of truth for the error class.\n *\n * @param error The poll failure (usually an ApiError from the client)\n */\n private async handlePollError(error: Error & { code?: string; retryAfterSeconds?: number }): Promise<void> {\n const errorCode = this.classifyError(error);\n const isRepeat = errorCode === this.lastErrorCode;\n this.lastErrorCode = errorCode;\n\n switch (errorCode) {\n case \"ABORTED\":\n // v0.10.0 (M1): expected during shutdown \u2014 cancelAll aborts the\n // in-flight GET. A deliberate stop must not paint a red error line.\n this.log.debug(`Poll aborted: ${error.message}`);\n break;\n case \"RATE_LIMITED\": {\n // v0.4.2 (M9): clamp Retry-After into [60s, 24h] (shared constants\n // with the client parser, L7). A bogus 0/negative/fractional value\n // must neither wipe the cooldown nor set it for milliseconds.\n const rawCooldown = error.retryAfterSeconds ?? 0;\n const cooldownSec =\n Number.isFinite(rawCooldown) && rawCooldown > 0\n ? Math.min(RETRY_AFTER_MAX_SEC, Math.max(60, Math.floor(rawCooldown)))\n : RETRY_AFTER_DEFAULT_SEC;\n this.rateLimitedUntil = Date.now() + cooldownSec * 1000;\n // v0.10.0 (M3): warn once \u2014 a persistent 429 repeats at debug.\n const line = `Rate limit hit \u2014 pausing API requests for ${Math.ceil(cooldownSec / 60)} minute(s)`;\n if (isRepeat) {\n this.log.debug(line);\n } else {\n this.log.warn(line);\n }\n break;\n }\n case \"FORBIDDEN\": {\n // v0.4.2 (P3): 403 is a permission issue (e.g. Premium subscription\n // expired). Reauth wouldn't help \u2014 surface a clear hint.\n // v0.10.0 (M3): once at error level, repeats at debug \u2014 not 144\n // identical error lines per day for one unchanged account problem.\n const line =\n \"parcel.app returned 403 Forbidden \u2014 your account may not have an active Premium subscription, or the API key was revoked. Check your account on parcelapp.net.\";\n if (isRepeat) {\n this.log.debug(line);\n } else {\n this.log.error(line);\n }\n break;\n }\n case \"INVALID_API_KEY\": {\n // v0.10.0 (M3): first occurrence at error (the user must fix the\n // config; info.connection goes red too) \u2014 repeats at debug.\n const line = \"Invalid API key \u2014 please check your parcel.app API key\";\n if (isRepeat) {\n this.log.debug(line);\n } else {\n this.log.error(line);\n }\n break;\n }\n case \"NETWORK\":\n if (isRepeat) {\n this.log.debug(`Poll failed (ongoing): ${error.message}`);\n } else {\n this.log.warn(\"Cannot reach parcel.app API \u2014 will keep retrying\");\n }\n break;\n case \"TIMEOUT\":\n if (isRepeat) {\n this.log.debug(`Poll failed (ongoing): ${error.message}`);\n } else {\n this.log.warn(\"API request timeout \u2014 will retry next cycle\");\n }\n break;\n default:\n if (isRepeat) {\n // Same error as last time \u2014 don't spam the log\n this.log.debug(`Poll failed (ongoing): ${error.message}`);\n } else {\n this.log.error(`Poll failed: ${error.message}`);\n }\n }\n\n // C2: setStateChangedAsync avoids redundant `false` writes on sustained\n // failure. The `.catch` keeps poll() from rejecting when the broker is\n // already down, so the fire-and-forget callers never see an unhandled\n // rejection (no global process handler needed).\n await this.setStateChangedAsync(\"info.connection\", { val: false, ack: true }).catch(() => {\n /* broker shutting down \u2014 ignore */\n });\n }\n}\n\n// Process entry point. Not reachable from the unit tests (they import the class\n// instead of running the file as a process), but NOT untested: `npm run\n// test:integration` boots the adapter through exactly this branch inside a real\n// js-controller, which is what catches module-scope startup crashes.\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new ParcelappAdapter(options);\n} else {\n (() => new ParcelappAdapter())();\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { I18n } from \"@iobroker/adapter-core\";\nimport { join } from \"node:path\";\nimport { coerceClampedInt, errText, isTrueish, oneLine } from \"./lib/coerce\";\nimport { ParcelClient, RETRY_AFTER_DEFAULT_SEC, RETRY_AFTER_MAX_SEC } from \"./lib/parcel-client\";\nimport { StateManager } from \"./lib/state-manager\";\nimport { DELIVERED_STATUS_CODE } from \"./lib/types\";\nimport type { AddDeliveryRequest } from \"./lib/types\";\n\nconst MIN_POLL_INTERVAL = 5;\nconst MAX_POLL_INTERVAL = 60;\nconst DEFAULT_POLL_INTERVAL = 10;\n// Minimum 60s between polls. Also the natural pacing after addDelivery: a\n// typical single add still polls immediately (the last poll is >60s ago),\n// while a burst of adds collapses to at most one extra GET per minute.\nconst MIN_POLL_GAP_MS = 60_000;\n/** v0.4.2 (M6): minimum length for an apiKey value to even be considered valid. */\nconst MIN_API_KEY_LENGTH = 10;\n// v0.9.0 (S5): cap addDelivery field lengths. A sendTo caller is local, but a\n// runaway script must not push a multi-MB POST body to parcel.app. Identifiers\n// are short; this is generous for a description.\nconst MAX_ADD_FIELD_LEN = 512;\n// v0.9.0 (S3): cap the per-poll broker fan-out. Updates run in batches of this\n// size instead of all deliveries at once, so an abnormally large API response\n// can't flood the broker with thousands of concurrent writes.\nconst UPDATE_BATCH_SIZE = 25;\n// v0.9.0 (S4): client-side throttle on addDelivery POSTs. parcel.app enforces\n// ~20 POST/day server-side; this caps a runaway/buggy script's burst so it can't\n// hammer the API. The window is generous enough never to block a real batch-add\n// (the daily server limit is the real cap).\nconst MAX_ADDS_PER_WINDOW = 20;\nconst ADD_WINDOW_MS = 60_000;\n\n/**\n * Node transport-level error codes treated as (transient) NETWORK problems:\n * DNS, connection refused/reset, unreachable \u2014 plus EPIPE/ECONNABORTED/EPROTO\n * (v0.10.0, I9), which belong to the same warn+keep-retrying class.\n */\nconst NETWORK_ERROR_CODES = new Set([\n \"ENOTFOUND\",\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"ENETUNREACH\",\n \"EHOSTUNREACH\",\n \"EAI_AGAIN\",\n \"EPIPE\",\n \"ECONNABORTED\",\n \"EPROTO\",\n]);\n\n/**\n * v0.10.0 (L12): the seam contracts name exactly the members main uses \u2014\n * structural Picks let the orchestration tests inject plain object fakes\n * without unsafe double-casts, and the compiler documents what a fake must\n * provide.\n */\ntype ClientLike = Pick<\n ParcelClient,\n \"getDeliveries\" | \"getCarrierName\" | \"addDelivery\" | \"testConnection\" | \"cancelAll\"\n>;\ntype StateManagerLike = Pick<\n StateManager,\n \"resetPollState\" | \"packageId\" | \"parseStatus\" | \"updateDelivery\" | \"updateSummary\" | \"cleanupDeliveries\"\n>;\n\n/**\n * ioBroker adapter for parcel.app package tracking. Exported so the\n * orchestration unit tests can drive its lifecycle/poll handlers directly.\n */\nexport class ParcelappAdapter extends utils.Adapter {\n private client: ClientLike | null = null;\n private stateManager: StateManagerLike | null = null;\n /**\n * Factories for the HTTP client + state manager \u2014 default to the real\n * constructors. Test seams (fleet pattern): unit tests replace these with\n * fakes to exercise the poll orchestration (throttle/rate-limit interplay,\n * error routing, failure dedup) without real network.\n *\n * @param apiKey parcel.app API key\n */\n private makeClient: (apiKey: string) => ClientLike = apiKey =>\n new ParcelClient(apiKey, { debug: (m: string) => this.log.debug(m) });\n private makeStateManager: () => StateManagerLike = () => new StateManager(this);\n private pollTimer: ioBroker.Interval | undefined = undefined;\n private isPolling = false;\n private lastPollTime = 0;\n private rateLimitedUntil = 0;\n private lastErrorCode = \"\";\n /**\n * v0.10.0 (L2): set in onUnload. onReady checks it after its awaits so a\n * stop during the first poll can no longer arm the interval or log\n * \"started\" after the unload already ran; batch failures during shutdown\n * degrade to debug.\n */\n private unloaded = false;\n /**\n * Package ids (not raw tracking numbers) whose last updateDelivery failed.\n * Keyed like the states so the dedup survives a sanitize collision or a\n * missing tracking number; pruned each poll against the visible pkgIds.\n */\n private failedDeliveries = new Set<string>();\n /** Timestamps of recent addDelivery POSTs \u2014 the S4 throttle window. */\n private addTimestamps: number[] = [];\n /**\n * L2: true while a checkConnection test GET is in flight. A test hits the same\n * 20/hour GET budget as polling; this guards against a concurrent second test\n * (double-click / admin re-render) stacking a redundant GET. A sequential\n * re-test after the current one settles runs normally.\n */\n private testConnectionInFlight = false;\n /**\n * v0.4.4: short-lived test-clients spawned from `checkConnection` admin\n * messages. The prod-`this.client` is what `onUnload` cancels, so these\n * need their own registry to be reachable at shutdown. Without this, an\n * admin clicking \"Test Connection\" right before adapter-stop could keep\n * the process alive past js-controller's 4-second kill deadline.\n */\n private testClients = new Set<ClientLike>();\n\n /** @param options Adapter options */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"parcelapp\",\n });\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n this.on(\"message\", this.onMessage.bind(this));\n }\n\n /**\n * Switch off `supportedMessages.stopInstance` on this instance's own object.\n *\n * The entry was dropped from the manifest, which only helps a FRESH install: an upgrade\n * merges the manifest into the existing instance object and never removes a key, so the old\n * `true` survives in the database \u2014 and that is what the host reads. With it the host kills\n * the process one second after asking it to stop, `onUnload` never runs, and the\n * `info.connection=false` write on the way out never reaches the database.\n *\n * Only written when it is actually still on: every instance-object change restarts the\n * instance, so doing it unconditionally would be a restart loop.\n *\n * @returns true when the correction was written and the restart is coming \u2014 the caller has\n * to stop right there instead of polling in a process that is going down.\n */\n private async clearStopInstanceFlag(): Promise<boolean> {\n const id = `system.adapter.${this.namespace}`;\n try {\n const obj = await this.getForeignObjectAsync(id);\n const supported = obj?.common?.supportedMessages as { stopInstance?: unknown } | undefined;\n if (!supported?.stopInstance) {\n return false;\n }\n this.log.info(\"Correcting a leftover setting from an earlier version \u2014 this instance restarts once\");\n await this.extendForeignObjectAsync(id, { common: { supportedMessages: { stopInstance: false } } });\n return true;\n } catch (err) {\n // Objects DB unreachable \u2014 not worth failing the start over; the next start retries.\n this.log.debug(`Could not check the instance object ${id}: ${errText(err)}`);\n return false;\n }\n }\n\n private async onReady(): Promise<void> {\n try {\n // First: without this the whole shutdown path stays dead on an updated install.\n // A correction means the host is restarting us \u2014 no point starting a poll cycle.\n if (await this.clearStopInstanceFlag()) {\n return;\n }\n // I18n.init resolves system.config.language itself (adapter-core reads\n // the foreign object internally; unknown languages fall back to English)\n // \u2014 the former separate getForeignObject round-trip and the local\n // resolveLanguage step are gone with the STATUS_LABELS table (L20).\n await I18n.init(join(this.adapterDir, \"admin\"), this);\n this.log.debug(`onReady: starting (autoRemoveDelivered=${this.config.autoRemoveDelivered})`);\n\n await this.setState(\"info.connection\", { val: false, ack: true });\n\n const { apiKey } = this.config;\n if (!apiKey || apiKey.trim().length < MIN_API_KEY_LENGTH) {\n this.log.error(\"No valid API key configured \u2014 please enter your parcel.app API key in the adapter settings\");\n return;\n }\n\n this.client = this.makeClient(apiKey.trim());\n this.stateManager = this.makeStateManager();\n\n try {\n await this.cleanupObsoleteStates();\n } catch (err) {\n // C8: a cleanup failure must not abort startup. Without this guard the\n // outer catch would skip arming the poll interval below, and the adapter\n // would never poll until a manual restart. Degrade to a warning.\n this.log.warn(`cleanupObsoleteStates failed (continuing): ${errText(err)}`);\n }\n\n await this.poll();\n\n // v0.10.0 (L2): a stop during the awaits above must not arm a timer or\n // log a start line after onUnload already ran.\n if (this.unloaded) {\n return;\n }\n\n const interval = ParcelappAdapter.coercePollInterval(this.config.pollInterval);\n this.log.debug(`pollInterval: raw=${JSON.stringify(this.config.pollInterval)} resolved=${interval}min`);\n const intervalMs = interval * 60 * 1000;\n this.pollTimer = this.setInterval(() => {\n void this.poll().catch(err => this.log.error(`Scheduled poll failed: ${errText(err)}`));\n }, intervalMs);\n\n this.log.info(`Parcel tracking started \u2014 polling every ${interval} minutes`);\n } catch (err: unknown) {\n this.log.error(`onReady failed: ${errText(err)}`);\n // v0.10.0 (L4): a transient startup failure (i18n files, DB hiccup) used\n // to leave a green-looking zombie \u2014 no client, no timer, no retry until\n // a manual restart. Terminate instead: js-controller restarts the\n // instance, which self-heals the transient case; its restart-loop guard\n // backstops a persistent one.\n if (!this.unloaded) {\n this.terminate(\"startup failed \u2014 requesting restart\", utils.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);\n }\n }\n }\n\n /**\n * v0.4.2 (M5+X5): delegate to the shared `coerceClampedInt` helper.\n *\n * @param raw Raw `pollInterval` from admin config (number or numeric string).\n */\n private static coercePollInterval(raw: unknown): number {\n return coerceClampedInt(raw, MIN_POLL_INTERVAL, MAX_POLL_INTERVAL, DEFAULT_POLL_INTERVAL);\n }\n\n private onUnload(callback: () => void): void {\n this.unloaded = true;\n try {\n if (this.pollTimer) {\n this.clearInterval(this.pollTimer);\n this.pollTimer = undefined;\n }\n // v0.4.2 (M11+P1): cancel every in-flight HTTPS request so a slow\n // parcel.app endpoint doesn't keep the adapter alive past\n // js-controller's 4-second kill deadline. cancelAll also marks the\n // client terminal (v0.10.0, L3) \u2014 requests started after this point\n // are refused instead of opening fresh connections.\n this.client?.cancelAll();\n // v0.4.4: also abort any short-lived test-client (from checkConnection)\n // whose HTTPS-request might still be inflight at shutdown \u2014 the prod\n // `this.client.cancelAll()` only touches the production-client.\n for (const tc of this.testClients) {\n tc.cancelAll();\n }\n this.testClients.clear();\n // v0.10.3: report done only once the disconnect write landed. Calling back\n // first means the host tears the process down while the write is still in\n // flight and the instance keeps claiming a live connection. No own deadline\n // needed \u2014 the host already has one (`common.stopTimeout`), and\n // `this.setTimeout` refuses during shutdown anyway.\n void this.setState(\"info.connection\", { val: false, ack: true })\n .catch(() => {\n /* broker is shutting down \u2014 ignore */\n })\n .finally(callback);\n return;\n } catch (err) {\n // v0.4.3 (G4): replace silent `// ignore` with a trace so shutdown\n // errors leave a debug breadcrumb. Broker-already-down errors here\n // are expected \u2014 debug-level keeps them out of the user log.\n try {\n this.log.debug(`onUnload error (ignored): ${errText(err)}`);\n } catch {\n /* logger already gone \u2014 nothing left to report to */\n }\n // v0.10.0 (I7): the callback is the contract with js-controller \u2014 exactly\n // once. v0.10.3: the happy path returns above with the callback chained to\n // the disconnect write, so this catch is the only other exit.\n callback();\n }\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n try {\n // v0.4.3 (F1): entry log BEFORE the early-return \u2014 broadcast messages\n // without callback wouldn't be visible otherwise. Inside the try since\n // v0.10.0 (I6) so the handler body is gap-free top-level guarded.\n this.log.debug(\n `onMessage: command='${oneLine(String(obj?.command ?? \"\"))}' from='${obj?.from}' has-callback=${!!obj?.callback}`,\n );\n if (!obj?.command || !obj.callback) {\n return;\n }\n\n switch (obj.command) {\n case \"checkConnection\":\n await this.handleCheckConnection(obj);\n break;\n case \"addDelivery\":\n await this.handleAddDelivery(obj);\n break;\n default:\n // v0.4.3 (F6): trace unknown command before sendTo.\n this.log.debug(`onMessage: unknown command '${oneLine(String(obj.command))}'`);\n this.sendTo(obj.from, obj.command, { error: \"Unknown command\" }, obj.callback);\n }\n } catch (err) {\n // v0.4.3 (F7): trace catch so the debug log shows what failed. The\n // reply itself is guarded too (v0.10.0, I6) \u2014 a synchronous sendTo\n // throw must not escape the message handler. checkConnection replies\n // use the admin {error} envelope (H1), scripts keep the documented\n // {success, error_message} shape.\n try {\n this.log.debug(`onMessage: '${oneLine(String(obj?.command ?? \"\"))}' failed: ${errText(err)}`);\n if (obj?.callback) {\n const reply =\n obj.command === \"checkConnection\"\n ? { error: errText(err) }\n : { success: false, error_message: errText(err) };\n this.sendTo(obj.from, obj.command, reply, obj.callback);\n }\n } catch {\n /* reply channel gone \u2014 nothing left to do */\n }\n }\n }\n\n /**\n * Admin \"Test Connection\" button (H1). The jsonConfig sendTo component reads\n * ONLY `response.error` / `response.result` \u2014 never success/message \u2014 so the\n * internal `{success, message}` result is mapped to that contract here.\n * Before this, a FAILED test showed a false-positive \"Ok\" in the admin\n * (fleet fix; beszel's message-router is the model).\n *\n * @param obj The sendTo message (validated: command + callback present)\n */\n private async handleCheckConnection(obj: ioBroker.Message): Promise<void> {\n const msg = obj.message as { apiKey?: string };\n const key = msg?.apiKey?.trim() || \"\";\n if (!key || key.length < MIN_API_KEY_LENGTH) {\n // v0.4.3 (F2): trace the reject before sendTo.\n this.log.debug(\"checkConnection: apiKey too short\");\n this.sendTo(obj.from, obj.command, { error: \"API key is too short\" }, obj.callback);\n return;\n }\n // L2: a Test-Connection GET counts against the same 20/hour budget as\n // polling. Guard against a concurrent second test (double-click / admin\n // re-render) so stacked clicks can't each burn a GET (which could later trip\n // the poll's rate-limit cooldown). Set BEFORE the await so the check is\n // synchronous against a still-in-flight first test.\n if (this.testConnectionInFlight) {\n this.log.debug(\"checkConnection: a test is already running\");\n this.sendTo(obj.from, obj.command, { error: \"A connection test is already running \u2014 please wait\" }, obj.callback);\n return;\n }\n this.testConnectionInFlight = true;\n // v0.4.3: same debug-logger as the prod client so checkConnection\n // failures get the same HTTPS-layer trace (via the makeClient seam).\n const testClient = this.makeClient(key);\n // v0.4.4: register test-client so onUnload can abort its inflight\n // HTTPS-request \u2014 the adapter's `this.client.cancelAll()` only\n // touches the prod-client, not these short-lived test-clients.\n this.testClients.add(testClient);\n try {\n const result = await testClient.testConnection();\n // v0.4.3 (F3): trace checkConnection result.\n this.log.debug(`checkConnection: result=${result.success ? \"ok\" : \"fail\"} (${result.message})`);\n this.sendTo(\n obj.from,\n obj.command,\n result.success ? { result: result.message } : { error: result.message },\n obj.callback,\n );\n } finally {\n this.testClients.delete(testClient);\n this.testConnectionInFlight = false;\n }\n }\n\n /**\n * Reply an addDelivery failure to the sendTo caller. This is the documented\n * script API envelope (`{success: false, error_message}`) \u2014 unchanged for\n * backward compatibility; only the admin checkConnection uses {error}.\n *\n * @param obj The sendTo message being answered\n * @param message Human-readable failure reason\n */\n private replyAddError(obj: ioBroker.Message, message: string): void {\n this.sendTo(obj.from, obj.command, { success: false, error_message: message }, obj.callback);\n }\n\n /**\n * Script-facing addDelivery command: validate the message shape, cap field\n * lengths, throttle bursts, forward to the API and trigger a poll on\n * success. Extracted from the onMessage switch (M9) \u2014 one command, one\n * method, one change reason.\n *\n * @param obj The sendTo message (validated: command + callback present)\n */\n private async handleAddDelivery(obj: ioBroker.Message): Promise<void> {\n if (!this.client) {\n // v0.4.3 (F4): trace addDelivery-before-init.\n this.log.debug(\"addDelivery: adapter not initialized\");\n this.replyAddError(obj, \"Adapter not initialized\");\n return;\n }\n // v0.7.2: obj.message is `unknown`-shaped \u2014 a script calling\n // sendTo(\"parcelapp\", \"addDelivery\", null) used to surface as an\n // ugly TypeError through the catch instead of a clear validation\n // message. Coerce to a plain object and validate required fields.\n const raw = obj.message;\n const msg = raw !== null && typeof raw === \"object\" && !Array.isArray(raw) ? (raw as Record<string, unknown>) : {};\n if (\n typeof msg.tracking_number !== \"string\" ||\n msg.tracking_number.length === 0 ||\n typeof msg.carrier_code !== \"string\" ||\n msg.carrier_code.length === 0 ||\n typeof msg.description !== \"string\" ||\n msg.description.length === 0\n ) {\n this.log.debug(\"addDelivery: missing tracking_number/carrier_code/description in message\");\n this.replyAddError(obj, \"tracking_number, carrier_code and description are required\");\n return;\n }\n // v0.9.0 (S5): cap field lengths so a runaway local script can't push\n // a multi-MB POST body to parcel.app. Checked after the required-field\n // guard above so the two validation messages stay distinct.\n if (\n msg.tracking_number.length > MAX_ADD_FIELD_LEN ||\n msg.carrier_code.length > MAX_ADD_FIELD_LEN ||\n msg.description.length > MAX_ADD_FIELD_LEN ||\n (typeof msg.language === \"string\" && msg.language.length > MAX_ADD_FIELD_LEN)\n ) {\n this.log.debug(\"addDelivery: a field exceeds the maximum length\");\n this.replyAddError(obj, `each field must be at most ${MAX_ADD_FIELD_LEN} characters`);\n return;\n }\n // Pass the optional API fields through when the caller supplies them\n // (language: ISO 639-1 two-letter code; send_push_confirmation: push\n // notification once the delivery is added).\n const request: AddDeliveryRequest = {\n tracking_number: msg.tracking_number,\n carrier_code: msg.carrier_code,\n description: msg.description,\n };\n if (typeof msg.language === \"string\" && msg.language.length > 0) {\n request.language = msg.language;\n }\n if (typeof msg.send_push_confirmation === \"boolean\") {\n request.send_push_confirmation = msg.send_push_confirmation;\n }\n // v0.9.0 (S4): throttle addDelivery POSTs. parcel.app caps ~20/day\n // server-side; this stops a runaway/buggy script from hammering the API\n // with a burst. Record the attempt before the await so concurrent\n // callers count too.\n const nowMs = Date.now();\n this.addTimestamps = this.addTimestamps.filter(t => nowMs - t < ADD_WINDOW_MS);\n if (this.addTimestamps.length >= MAX_ADDS_PER_WINDOW) {\n this.log.warn(`addDelivery throttled: more than ${MAX_ADDS_PER_WINDOW} requests within ${ADD_WINDOW_MS / 1000}s`);\n this.replyAddError(obj, `too many addDelivery requests; max ${MAX_ADDS_PER_WINDOW} per ${ADD_WINDOW_MS / 1000}s`);\n return;\n }\n this.addTimestamps.push(nowMs);\n const addResult = await this.client.addDelivery(request);\n // v0.4.3 (F5): trace addDelivery result with the (flattened) tracking number.\n // v0.10.0 (L9): the drift-guarded isTrueish \u2014 getDeliveries hardens the same\n // API flag; a drifted `success: \"false\"` string must not read as truthy.\n const added = isTrueish(addResult.success);\n this.log.debug(`addDelivery: '${oneLine(request.tracking_number)}' result=${added ? \"ok\" : \"fail\"}`);\n this.sendTo(obj.from, obj.command, addResult, obj.callback);\n if (added) {\n // v0.10.0 (L5): plain poll, no force \u2014 a single add still polls right\n // away (the last poll is usually >60s back), but an add-burst can no\n // longer stack force-GETs past the 20/h API budget. Nothing is lost:\n // the server caches the list ~45-90 min, a fresh package rarely shows\n // tracking data immediately anyway.\n void this.poll().catch(err => this.log.error(`Poll after addDelivery failed: ${errText(err)}`));\n }\n }\n\n private async cleanupObsoleteStates(): Promise<void> {\n // One getObject per adapter start, forever \u2014 deliberately kept (I3/ARCH-24):\n // a one-shot migration marker would cost more mechanics than this read.\n // Drop the list entirely at the next major once 0.1.x installs are gone.\n const obsoleteStates = [\n \"summary.json\", // removed in 0.2.0\n ];\n for (const stateId of obsoleteStates) {\n const obj = await this.getObjectAsync(stateId);\n if (obj) {\n await this.delObjectAsync(stateId);\n this.log.debug(`Removed obsolete state: ${stateId}`);\n }\n }\n }\n\n /**\n * Classify an error for deduplication and log-level decisions.\n *\n * v0.10.0 (M1): the client codes every failure it raises (TIMEOUT,\n * PARSE_ERROR, ABORTED, RATE_LIMITED, \u2026) \u2014 a present machine code always\n * wins. The message-substring sniff only remains for code-less foreign\n * errors, so an API error_message merely CONTAINING \"timeout\" can no longer\n * be misclassified.\n *\n * @param error The error to classify\n */\n private classifyError(error: Error & { code?: string }): string {\n if (error.code) {\n if (NETWORK_ERROR_CODES.has(error.code)) {\n return \"NETWORK\";\n }\n if (error.code === \"ETIMEDOUT\") {\n return \"TIMEOUT\";\n }\n return error.code;\n }\n if (error.message.includes(\"timeout\")) {\n return \"TIMEOUT\";\n }\n return \"UNKNOWN\";\n }\n\n private async poll(): Promise<void> {\n if (this.isPolling || !this.client || !this.stateManager) {\n // v0.10.0 (M4): make the re-entry/uninitialized skip visible like the\n // rate-limit/throttle skips below \u2014 this used to be the one silent spot\n // where \"the adapter does nothing\" left no trace.\n this.log.debug(\"Skipping poll \u2014 already running or not initialized\");\n return;\n }\n // v0.10.0 (L14): local bindings instead of non-null assertions in the\n // closures below \u2014 provable narrowing the compiler checks, robust against\n // a future refactor nulling the fields mid-poll.\n const client = this.client;\n const stateManager = this.stateManager;\n\n const now = Date.now();\n // v0.4.3 (B1): poll-entry anchor \u2014 visible after the re-entry guard but\n // before the rate-limit/throttle skips. Shows mode + current error state\n // so the debug log gives context for whatever follows.\n const autoRemoveMode = this.config.autoRemoveDelivered !== false;\n this.log.debug(`poll: starting (autoRemove=${autoRemoveMode}, lastErrorCode='${this.lastErrorCode}')`);\n\n // Skip if rate limited\n if (now < this.rateLimitedUntil) {\n const waitMin = Math.ceil((this.rateLimitedUntil - now) / 60_000);\n this.log.debug(`Skipping poll \u2014 rate limited for ${waitMin} more minute(s)`);\n return;\n }\n\n // Throttle: minimum gap between polls (also paces the poll after a\n // successful addDelivery \u2014 see handleAddDelivery).\n if (now - this.lastPollTime < MIN_POLL_GAP_MS) {\n this.log.debug(\"Skipping poll \u2014 too soon after last poll\");\n return;\n }\n\n this.isPolling = true;\n this.lastPollTime = now;\n try {\n // When keeping delivered packages, use \"recent\" to get them from API\n const deliveries = await client.getDeliveries(autoRemoveMode ? \"active\" : \"recent\");\n\n // Reset error state on success\n this.rateLimitedUntil = 0;\n if (this.lastErrorCode) {\n this.log.info(\"Connection restored\");\n this.lastErrorCode = \"\";\n }\n await this.setStateChangedAsync(\"info.connection\", { val: true, ack: true });\n\n // Split into active (non-delivered) and visible (what gets states)\n const activeDeliveries = deliveries.filter(d => stateManager.parseStatus(d) !== DELIVERED_STATUS_CODE);\n const visibleDeliveries = autoRemoveMode ? activeDeliveries : deliveries;\n\n // v0.4.2 (S3): reset the per-poll collision tracker, then compute every\n // package id in a deterministic sequential pre-pass (stable array order)\n // BEFORE the parallel updates \u2014 collision-suffixing is then deterministic\n // and packageId runs exactly once per delivery instead of twice.\n stateManager.resetPollState();\n const pkgIds = visibleDeliveries.map(d => stateManager.packageId(d));\n\n // v0.4.2 (M4): per-delivery updates run in parallel, each wrapped in\n // try/catch so one bad delivery doesn't poison the others.\n // v0.9.0 (S3): process the updates in bounded batches instead of one\n // broker fan-out for ALL deliveries at once. The keep-set is still every\n // visible pkgId (computed above), so this only caps concurrency \u2014 it never\n // drops a package. A normal poll (a handful of packages) is a single batch.\n if (visibleDeliveries.length > UPDATE_BATCH_SIZE) {\n this.log.debug(`Updating ${visibleDeliveries.length} deliveries in batches of ${UPDATE_BATCH_SIZE}`);\n }\n for (let start = 0; start < visibleDeliveries.length; start += UPDATE_BATCH_SIZE) {\n const batch = visibleDeliveries.slice(start, start + UPDATE_BATCH_SIZE);\n await Promise.all(\n batch.map(async (delivery, offset) => {\n const pkgId = pkgIds[start + offset];\n // Pre-sanitize the externally-sourced strings once for logging. The\n // fields are optional (the API can drop them), so default to \"\" before\n // oneLine flattens them onto a single log line.\n const tracking = oneLine(delivery.tracking_number ?? \"\");\n const carrier = oneLine(delivery.carrier_code ?? \"\");\n try {\n // v0.4.3 (C1): per-delivery entry. ~10 packages \u00D7 144 polls/day\n // = ~1440 debug lines/day \u2014 acceptable at debug-level. Line stays\n // short (tracking + carrier + status only, no full delivery JSON).\n // v0.10.0 (M6): status_code is typed number|string (drift), so it\n // is flattened like the other externally-sourced fields.\n this.log.debug(\n `updateDelivery: '${tracking}' carrier=${carrier} status=${oneLine(String(delivery.status_code))}`,\n );\n const carrierName = await client.getCarrierName(delivery.carrier_code);\n await stateManager.updateDelivery(delivery, carrierName, pkgId);\n this.failedDeliveries.delete(pkgId);\n } catch (err) {\n const msg = errText(err);\n if (this.failedDeliveries.has(pkgId)) {\n this.log.debug(`Failed to update '${tracking}': ${msg}`);\n } else if (this.unloaded) {\n // v0.10.0 (L2): broker teardown mid-batch is expected noise\n // during shutdown, not a per-package warning.\n this.log.debug(`Failed to update '${tracking}' during shutdown: ${msg}`);\n } else {\n this.log.warn(`Failed to update '${tracking}': ${msg}`);\n this.failedDeliveries.add(pkgId);\n }\n }\n }),\n );\n }\n\n // v0.9.0 (C1): keep-set = EVERY package the API still returns this poll\n // (pkgIds), NOT only the writes that just succeeded. A transient\n // updateDelivery failure leaves a package's states stale, but it must not\n // drop the package from cleanup \u2014 that would delete a still-present\n // package's states (and any user-set device name) at green info.connection.\n // v0.10.0 (M2): broker-side failures in cleanup/summary are NOT API\n // failures \u2014 they must neither flip info.connection to false (the GET\n // above just succeeded) nor run through the API error classification.\n try {\n await stateManager.cleanupDeliveries(pkgIds);\n // Update summary (always uses active/non-delivered)\n await stateManager.updateSummary(activeDeliveries);\n } catch (err) {\n this.log.warn(`State maintenance failed (API connection is fine, retrying next poll): ${errText(err)}`);\n }\n\n // Keep failedDeliveries bounded: drop entries for package ids no longer\n // present, so packages that vanish from the API don't linger forever.\n const seenPkgIds = new Set(pkgIds);\n for (const id of [...this.failedDeliveries]) {\n if (!seenPkgIds.has(id)) {\n this.failedDeliveries.delete(id);\n }\n }\n\n this.log.debug(`Polled ${visibleDeliveries.length} deliveries (${activeDeliveries.length} active)`);\n } catch (err) {\n await this.handlePollError(err as Error & { code?: string; retryAfterSeconds?: number });\n } finally {\n this.isPolling = false;\n }\n }\n\n /**\n * Classify + route a poll failure: log level, dedup, cooldown and the\n * info.connection=false write. Extracted from poll()'s catch (M9) so the\n * happy path reads as a plain sequence and the error policy sits next to\n * classifyError. Dispatches on the CLASSIFIED code only (L6) \u2014 one source\n * of truth for the error class.\n *\n * @param error The poll failure (usually an ApiError from the client)\n */\n private async handlePollError(error: Error & { code?: string; retryAfterSeconds?: number }): Promise<void> {\n const errorCode = this.classifyError(error);\n const isRepeat = errorCode === this.lastErrorCode;\n this.lastErrorCode = errorCode;\n\n switch (errorCode) {\n case \"ABORTED\":\n // v0.10.0 (M1): expected during shutdown \u2014 cancelAll aborts the\n // in-flight GET. A deliberate stop must not paint a red error line.\n this.log.debug(`Poll aborted: ${error.message}`);\n break;\n case \"RATE_LIMITED\": {\n // v0.4.2 (M9): clamp Retry-After into [60s, 24h] (shared constants\n // with the client parser, L7). A bogus 0/negative/fractional value\n // must neither wipe the cooldown nor set it for milliseconds.\n const rawCooldown = error.retryAfterSeconds ?? 0;\n const cooldownSec =\n Number.isFinite(rawCooldown) && rawCooldown > 0\n ? Math.min(RETRY_AFTER_MAX_SEC, Math.max(60, Math.floor(rawCooldown)))\n : RETRY_AFTER_DEFAULT_SEC;\n this.rateLimitedUntil = Date.now() + cooldownSec * 1000;\n // v0.10.0 (M3): warn once \u2014 a persistent 429 repeats at debug.\n const line = `Rate limit hit \u2014 pausing API requests for ${Math.ceil(cooldownSec / 60)} minute(s)`;\n if (isRepeat) {\n this.log.debug(line);\n } else {\n this.log.warn(line);\n }\n break;\n }\n case \"FORBIDDEN\": {\n // v0.4.2 (P3): 403 is a permission issue (e.g. Premium subscription\n // expired). Reauth wouldn't help \u2014 surface a clear hint.\n // v0.10.0 (M3): once at error level, repeats at debug \u2014 not 144\n // identical error lines per day for one unchanged account problem.\n const line =\n \"parcel.app returned 403 Forbidden \u2014 your account may not have an active Premium subscription, or the API key was revoked. Check your account on parcelapp.net.\";\n if (isRepeat) {\n this.log.debug(line);\n } else {\n this.log.error(line);\n }\n break;\n }\n case \"INVALID_API_KEY\": {\n // v0.10.0 (M3): first occurrence at error (the user must fix the\n // config; info.connection goes red too) \u2014 repeats at debug.\n const line = \"Invalid API key \u2014 please check your parcel.app API key\";\n if (isRepeat) {\n this.log.debug(line);\n } else {\n this.log.error(line);\n }\n break;\n }\n case \"NETWORK\":\n if (isRepeat) {\n this.log.debug(`Poll failed (ongoing): ${error.message}`);\n } else {\n this.log.warn(\"Cannot reach parcel.app API \u2014 will keep retrying\");\n }\n break;\n case \"TIMEOUT\":\n if (isRepeat) {\n this.log.debug(`Poll failed (ongoing): ${error.message}`);\n } else {\n this.log.warn(\"API request timeout \u2014 will retry next cycle\");\n }\n break;\n default:\n if (isRepeat) {\n // Same error as last time \u2014 don't spam the log\n this.log.debug(`Poll failed (ongoing): ${error.message}`);\n } else {\n this.log.error(`Poll failed: ${error.message}`);\n }\n }\n\n // C2: setStateChangedAsync avoids redundant `false` writes on sustained\n // failure. The `.catch` keeps poll() from rejecting when the broker is\n // already down, so the fire-and-forget callers never see an unhandled\n // rejection (no global process handler needed).\n await this.setStateChangedAsync(\"info.connection\", { val: false, ack: true }).catch(() => {\n /* broker shutting down \u2014 ignore */\n });\n }\n}\n\n// Process entry point. Not reachable from the unit tests (they import the class\n// instead of running the file as a process), but NOT untested: `npm run\n// test:integration` boots the adapter through exactly this branch inside a real\n// js-controller, which is what catches module-scope startup crashes.\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new ParcelappAdapter(options);\n} else {\n (() => new ParcelappAdapter())();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "parcelapp",
4
- "version": "0.10.2",
4
+ "version": "0.10.3",
5
5
  "news": {
6
+ "0.10.3": {
7
+ "en": "Stopping or restarting the adapter was cut short — the stopped instance kept claiming a live connection to parcel.app instead of showing as disconnected.",
8
+ "de": "Beenden oder Neustart des Adapters brach vorzeitig ab — die gestoppte Instanz meldete weiterhin eine bestehende Verbindung zu parcel.app statt getrennt zu sein.",
9
+ "ru": "Остановка или перезапуск адаптера обрывались — остановленный экземпляр продолжал сообщать о действующем соединении с parcel.app вместо разрыва.",
10
+ "pt": "Parar ou reiniciar o adaptador era interrompido — a instância parada continuava a indicar uma ligação ativa ao parcel.app em vez de aparecer desligada.",
11
+ "nl": "Stoppen of herstarten van de adapter werd afgebroken — de gestopte instantie meldde nog steeds een actieve verbinding met parcel.app in plaats van verbroken.",
12
+ "fr": "L'arrêt ou le redémarrage de l'adaptateur était interrompu — l'instance arrêtée indiquait toujours une connexion active à parcel.app au lieu d'être déconnectée.",
13
+ "it": "L'arresto o il riavvio dell'adattatore veniva interrotto — l'istanza fermata continuava a segnalare una connessione attiva a parcel.app invece di risultare disconnessa.",
14
+ "es": "Detener o reiniciar el adaptador se interrumpía — la instancia detenida seguía indicando una conexión activa con parcel.app en lugar de aparecer desconectada.",
15
+ "pl": "Zatrzymanie lub restart adaptera był przerywany — zatrzymana instancja nadal zgłaszała aktywne połączenie z parcel.app zamiast rozłączenia.",
16
+ "uk": "Зупинка або перезапуск адаптера обривалися — зупинений екземпляр далі повідомляв про активне з'єднання з parcel.app замість роз'єднання.",
17
+ "zh-cn": "停止或重启适配器时过程被中断——已停止的实例仍显示与 parcel.app 保持连接,而不是显示为已断开。"
18
+ },
6
19
  "0.10.2": {
7
20
  "en": "Internal cleanup. No user-facing changes.",
8
21
  "de": "Interne Aufräumarbeiten. Keine Änderungen für Nutzer.",
@@ -80,19 +93,6 @@
80
93
  "pl": "Spokojniejsze aktualizacje: znacznik ostatniej aktualizacji paczki zmienia się tylko przy realnej zmianie danych, wpisy urządzeń nie są przepisywane przy każdym odpytaniu.",
81
94
  "uk": "Спокійніші оновлення: мітка останнього оновлення посилки змінюється лише за реальної зміни даних, записи пристроїв не перезаписуються щоопитування.",
82
95
  "zh-cn": "状态更新更安静:包裹的最后更新时间戳仅在跟踪数据真正变化时才更新,设备条目不再每次轮询都被重写。"
83
- },
84
- "0.7.1": {
85
- "en": "Fixed a timezone edge case in delivery estimates: when the API reports only a calendar date, the estimate could be off by a day in time zones west of UTC — now stable.",
86
- "de": "Zeitzonen-Randfall bei Lieferschätzungen behoben: Wenn die API nur ein Kalenderdatum liefert, konnte die Schätzung in Zeitzonen westlich von UTC einen Tag danebenliegen — jetzt stabil.",
87
- "ru": "Исправлен пограничный случай с часовым поясом в оценках доставки: если API возвращает только дату, оценка могла отличаться на день в зонах западнее UTC — теперь стабильно.",
88
- "pt": "Corrigido um caso-limite de fuso horário nas estimativas de entrega: quando a API fornece apenas uma data, a estimativa podia falhar por um dia em fusos a oeste de UTC — agora estável.",
89
- "nl": "Tijdzone-randgeval in bezorgschattingen opgelost: als de API alleen een kalenderdatum geeft, kon de schatting in tijdzones ten westen van UTC een dag afwijken — nu stabiel.",
90
- "fr": "Correction d'un cas limite de fuseau horaire dans les estimations de livraison : quand l'API ne fournit qu'une date, l'estimation pouvait décaler d'un jour à l'ouest d'UTC — désormais stable.",
91
- "it": "Corretto un caso limite di fuso orario nelle stime di consegna: quando l'API fornisce solo una data, la stima poteva sbagliare di un giorno nei fusi a ovest di UTC — ora stabile.",
92
- "es": "Corregido un caso límite de zona horaria en las estimaciones de entrega: cuando la API solo da una fecha, la estimación podía fallar por un día en zonas al oeste de UTC — ahora estable.",
93
- "pl": "Naprawiono przypadek brzegowy strefy czasowej w szacowanym czasie dostawy: gdy API podaje tylko datę, szacunek mógł różnić się o dzień w strefach na zachód od UTC — teraz stabilny.",
94
- "uk": "Виправлено граничний випадок часового поясу в оцінках доставки: коли API повертає лише дату, оцінка могла відрізнятися на день у зонах на захід від UTC — тепер стабільно.",
95
- "zh-cn": "修复了配送预计时间的时区边界问题:当 API 仅提供日期时,在 UTC 以西时区的预计可能相差一天,现在已稳定。"
96
96
  }
97
97
  },
98
98
  "plugins": {
@@ -157,9 +157,6 @@
157
157
  "adminUI": {
158
158
  "config": "json"
159
159
  },
160
- "supportedMessages": {
161
- "stopInstance": true
162
- },
163
160
  "dependencies": [
164
161
  {
165
162
  "js-controller": ">=7.2.2"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.parcelapp",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "ioBroker adapter for the parcel.app API",
5
5
  "author": {
6
6
  "name": "krobi",
@@ -43,6 +43,7 @@
43
43
  "@types/iobroker": "npm:@iobroker/types@^7.2.2",
44
44
  "@types/node": "^22.19.17",
45
45
  "@vitest/coverage-v8": "^4.1.11",
46
+ "iobroker-adapter-checks": "^0.3.0",
46
47
  "rimraf": "^6.1.3",
47
48
  "typescript": "~6.0.3",
48
49
  "vitest": "^4.1.11"
@@ -75,5 +76,12 @@
75
76
  "bugs": {
76
77
  "url": "https://github.com/krobipd/ioBroker.parcelapp/issues"
77
78
  },
78
- "main": "build/main.js"
79
+ "main": "build/main.js",
80
+ "overrides": {
81
+ "mocha": {
82
+ "diff": "^8.0.3",
83
+ "serialize-javascript": "^7.0.5"
84
+ },
85
+ "esbuild": ">=0.25.0"
86
+ }
79
87
  }