iobroker.parcelapp 0.4.8 → 0.4.9

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
@@ -123,6 +123,10 @@ The delivery is added to your parcel.app account and immediately appears in ioBr
123
123
  Placeholder for the next version (at the beginning of the line):
124
124
  ### **WORK IN PROGRESS**
125
125
  -->
126
+ ### 0.4.9 (2026-05-21)
127
+
128
+ - Use community-standard event handler pattern
129
+
126
130
  ### 0.4.8 (2026-05-19)
127
131
 
128
132
  - Code quality enforced with standard formatting.
@@ -139,10 +143,6 @@ The delivery is added to your parcel.app account and immediately appears in ioBr
139
143
 
140
144
  - Fixed adapter icon not displaying in some admin environments (Content Security Policy).
141
145
 
142
- ### 0.4.4 (2026-05-13)
143
-
144
- - Adapter shuts down cleanly even if the "Test Connection" button was still running — the test request is now aborted at unload along with regular polling.
145
-
146
146
  Older entries are in [CHANGELOG_OLD.md](CHANGELOG_OLD.md).
147
147
 
148
148
  ## Support
package/build/main.js CHANGED
@@ -57,13 +57,9 @@ class ParcelappAdapter extends utils.Adapter {
57
57
  ...options,
58
58
  name: "parcelapp"
59
59
  });
60
- this.on("ready", () => {
61
- this.onReady().catch((err) => this.log.error(`onReady failed: ${(0, import_coerce.errText)(err)}`));
62
- });
60
+ this.on("ready", this.onReady.bind(this));
63
61
  this.on("unload", this.onUnload.bind(this));
64
- this.on("message", (obj) => {
65
- this.onMessage(obj).catch((err) => this.log.error(`onMessage failed: ${(0, import_coerce.errText)(err)}`));
66
- });
62
+ this.on("message", this.onMessage.bind(this));
67
63
  this.unhandledRejectionHandler = (reason) => {
68
64
  var _a;
69
65
  this.log.error(`Unhandled rejection: ${(0, import_coerce.errText)(reason)}`);
@@ -79,30 +75,34 @@ class ParcelappAdapter extends utils.Adapter {
79
75
  }
80
76
  async onReady() {
81
77
  var _a, _b;
82
- this.log.debug(
83
- `onReady: starting (pollInterval=${JSON.stringify(this.config.pollInterval)}, autoRemoveDelivered=${this.config.autoRemoveDelivered})`
84
- );
85
- const sysConfig = await this.getForeignObjectAsync("system.config");
86
- const language = (_b = (_a = sysConfig == null ? void 0 : sysConfig.common) == null ? void 0 : _a.language) != null ? _b : "";
87
- if (typeof language === "string" && language.length > 0) {
88
- this.systemLang = language;
89
- }
90
- this.log.debug(`system language: '${language}' \u2192 using '${this.systemLang}'`);
91
- await this.setStateAsync("info.connection", { val: false, ack: true });
92
- const { apiKey } = this.config;
93
- if (!apiKey || apiKey.trim().length < MIN_API_KEY_LENGTH) {
94
- this.log.error("No valid API key configured \u2014 please enter your parcel.app API key in the adapter settings");
95
- return;
78
+ try {
79
+ this.log.debug(
80
+ `onReady: starting (pollInterval=${JSON.stringify(this.config.pollInterval)}, autoRemoveDelivered=${this.config.autoRemoveDelivered})`
81
+ );
82
+ const sysConfig = await this.getForeignObjectAsync("system.config");
83
+ const language = (_b = (_a = sysConfig == null ? void 0 : sysConfig.common) == null ? void 0 : _a.language) != null ? _b : "";
84
+ if (typeof language === "string" && language.length > 0) {
85
+ this.systemLang = language;
86
+ }
87
+ this.log.debug(`system language: '${language}' \u2192 using '${this.systemLang}'`);
88
+ await this.setStateAsync("info.connection", { val: false, ack: true });
89
+ const { apiKey } = this.config;
90
+ if (!apiKey || apiKey.trim().length < MIN_API_KEY_LENGTH) {
91
+ this.log.error("No valid API key configured \u2014 please enter your parcel.app API key in the adapter settings");
92
+ return;
93
+ }
94
+ this.client = new import_parcel_client.ParcelClient(apiKey.trim(), { debug: (m) => this.log.debug(m) });
95
+ this.stateManager = new import_state_manager.StateManager(this, language);
96
+ await this.cleanupObsoleteStates();
97
+ await this.poll();
98
+ const interval = ParcelappAdapter.coercePollInterval(this.config.pollInterval);
99
+ this.log.debug(`pollInterval: raw=${JSON.stringify(this.config.pollInterval)} resolved=${interval}min`);
100
+ const intervalMs = interval * 60 * 1e3;
101
+ this.pollTimer = this.setInterval(() => void this.poll(), intervalMs);
102
+ this.log.info(`Parcel tracking started \u2014 polling every ${interval} minutes`);
103
+ } catch (err) {
104
+ this.log.error(`onReady failed: ${(0, import_coerce.errText)(err)}`);
96
105
  }
97
- this.client = new import_parcel_client.ParcelClient(apiKey.trim(), { debug: (m) => this.log.debug(m) });
98
- this.stateManager = new import_state_manager.StateManager(this, language);
99
- await this.cleanupObsoleteStates();
100
- await this.poll();
101
- const interval = ParcelappAdapter.coercePollInterval(this.config.pollInterval);
102
- this.log.debug(`pollInterval: raw=${JSON.stringify(this.config.pollInterval)} resolved=${interval}min`);
103
- const intervalMs = interval * 60 * 1e3;
104
- this.pollTimer = this.setInterval(() => void this.poll(), intervalMs);
105
- this.log.info(`Parcel tracking started \u2014 polling every ${interval} minutes`);
106
106
  }
107
107
  /**
108
108
  * v0.4.2 (M5+X5): delegate to the shared `coerceClampedInt` helper.
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 { coerceClampedInt, errText } from \"./lib/coerce\";\nimport { ParcelClient } from \"./lib/parcel-client\";\nimport { StateManager } from \"./lib/state-manager\";\n\nconst MIN_POLL_INTERVAL = 5;\nconst MAX_POLL_INTERVAL = 60;\nconst DEFAULT_POLL_INTERVAL = 10;\nconst MIN_POLL_GAP_MS = 60_000; // Minimum 60s between polls\n/** v0.4.2 (M6): minimum length for an apiKey value to even be considered valid. */\nconst MIN_API_KEY_LENGTH = 10;\n\n/** ioBroker adapter for parcel.app package tracking */\nclass ParcelappAdapter extends utils.Adapter {\n private client: ParcelClient | null = null;\n private stateManager: StateManager | null = null;\n private pollTimer: ioBroker.Interval | undefined = undefined;\n private isPolling = false;\n private lastPollTime = 0;\n private rateLimitedUntil = 0;\n private lastErrorCode = \"\";\n private failedDeliveries = new Set<string>();\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<ParcelClient>();\n private unhandledRejectionHandler: ((reason: unknown) => void) | null = null;\n private uncaughtExceptionHandler: ((err: Error) => void) | null = null;\n /** ioBroker system language \u2014 read once in `onReady` from `system.config`. EN fallback. */\n private systemLang: string = \"en\";\n\n /** @param options Adapter options */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"parcelapp\",\n });\n // Wrap async handlers with .catch() so a rejection can never become an\n // unhandled promise rejection (which would SIGKILL the adapter and trap\n // js-controller in a restart loop without any stack trace).\n this.on(\"ready\", () => {\n this.onReady().catch(err => this.log.error(`onReady failed: ${errText(err)}`));\n });\n this.on(\"unload\", this.onUnload.bind(this));\n this.on(\"message\", obj => {\n this.onMessage(obj).catch(err => this.log.error(`onMessage failed: ${errText(err)}`));\n });\n // Last-line-of-defence against unhandled rejections / sync throws from\n // fire-and-forget paths (e.g. `void this.poll()`). The per-handler\n // .catch() wrappers cover the documented async paths; this catches\n // anything that slips past during refactors.\n // v0.4.2 (M1): log + terminate(11) instead of leaving the process alive\n // in an undefined state. The per-handler wrappers cover expected paths;\n // anything reaching here is by definition unexpected.\n this.unhandledRejectionHandler = (reason: unknown) => {\n this.log.error(`Unhandled rejection: ${errText(reason)}`);\n this.terminate?.(11);\n };\n this.uncaughtExceptionHandler = (err: Error) => {\n this.log.error(`Uncaught exception: ${errText(err)}`);\n this.terminate?.(11);\n };\n process.on(\"unhandledRejection\", this.unhandledRejectionHandler);\n process.on(\"uncaughtException\", this.uncaughtExceptionHandler);\n }\n\n private async onReady(): Promise<void> {\n // v0.4.3 (G1): trace start of onReady so the debug log shows when the\n // adapter starts wiring up, what the config inputs look like.\n this.log.debug(\n `onReady: starting (pollInterval=${JSON.stringify(this.config.pollInterval)}, autoRemoveDelivered=${this.config.autoRemoveDelivered})`,\n );\n\n // Pick the system language up-front so all user-facing logs go out in the\n // user's language. StateManager also gets it for state-name localization.\n const sysConfig = await this.getForeignObjectAsync(\"system.config\");\n const language = (sysConfig?.common as { language?: string } | undefined)?.language ?? \"\";\n if (typeof language === \"string\" && language.length > 0) {\n this.systemLang = language;\n }\n // v0.4.3 (G2): trace the resolved system language. Empty/unknown values\n // fall back to \"en\" silently in older versions \u2014 now visible.\n this.log.debug(`system language: '${language}' \u2192 using '${this.systemLang}'`);\n\n await this.setStateAsync(\"info.connection\", { val: false, ack: true });\n\n // Validate config\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 // Initialize. v0.4.3: pass a debug-logger so the HTTPS layer can trace\n // request/response lifecycle. `loglevel=info` users see nothing changed.\n this.client = new ParcelClient(apiKey.trim(), { debug: (m: string) => this.log.debug(m) });\n this.stateManager = new StateManager(this, language);\n\n // Cleanup obsolete states\n await this.cleanupObsoleteStates();\n\n // Initial poll\n await this.poll();\n\n // v0.4.2 (M5): coerce explicitly. Admin can store `pollInterval` as a\n // string; `Math.min(60, \"10\")` happens to coerce, but `Math.max(5,\n // undefined)` returns NaN, and `setInterval(fn, NaN)` becomes\n // `setInterval(fn, 0)` \u2014 a tight loop that hammers the API.\n const interval = ParcelappAdapter.coercePollInterval(this.config.pollInterval);\n // v0.4.3 (G3): always-log the resolved interval. Detecting \"drift\" from\n // the raw value is fragile (`\"10\"` \u2192 `10` is not drift, just coercion);\n // an always-on log line keeps the resolution visible without false-flag\n // logic. See `Ressourcen/parcelapp/v0.4.3-debug-audit.md` advisor pt 3.\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(() => void this.poll(), intervalMs);\n\n this.log.info(`Parcel tracking started \u2014 polling every ${interval} minutes`);\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 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.\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 if (this.unhandledRejectionHandler) {\n process.off(\"unhandledRejection\", this.unhandledRejectionHandler);\n this.unhandledRejectionHandler = null;\n }\n if (this.uncaughtExceptionHandler) {\n process.off(\"uncaughtException\", this.uncaughtExceptionHandler);\n this.uncaughtExceptionHandler = null;\n }\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 this.log.debug(`onUnload error (ignored): ${errText(err)}`);\n }\n callback();\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n // v0.4.3 (F1): entry log BEFORE the early-return \u2014 broadcast messages\n // without callback wouldn't be visible otherwise.\n this.log.debug(`onMessage: command='${obj?.command}' from='${obj?.from}' has-callback=${!!obj?.callback}`);\n if (!obj?.command || !obj.callback) {\n return;\n }\n\n try {\n switch (obj.command) {\n case \"checkConnection\": {\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, { success: false, message: \"API key is too short\" }, obj.callback);\n return;\n }\n // v0.4.3: same debug-logger as the prod client so checkConnection\n // failures get the same HTTPS-layer trace.\n const testClient = new ParcelClient(key, { debug: (m: string) => this.log.debug(m) });\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(obj.from, obj.command, result, obj.callback);\n } finally {\n this.testClients.delete(testClient);\n }\n break;\n }\n case \"addDelivery\": {\n if (!this.client) {\n // v0.4.3 (F4): trace addDelivery-before-init.\n this.log.debug(\"addDelivery: adapter not initialized\");\n this.sendTo(\n obj.from,\n obj.command,\n { success: false, error_message: \"Adapter not initialized\" },\n obj.callback,\n );\n return;\n }\n const request = obj.message as {\n tracking_number: string;\n carrier_code: string;\n description: string;\n };\n const addResult = await this.client.addDelivery(request);\n // v0.4.3 (F5): trace addDelivery result with the tracking number.\n this.log.debug(`addDelivery: '${request?.tracking_number}' result=${addResult.success ? \"ok\" : \"fail\"}`);\n this.sendTo(obj.from, obj.command, addResult, obj.callback);\n if (addResult.success) {\n void this.poll();\n }\n break;\n }\n default:\n // v0.4.3 (F6): trace unknown command before sendTo.\n this.log.debug(`onMessage: unknown command '${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.\n // The sendTo back to the caller is preserved unchanged.\n this.log.debug(`onMessage: '${obj.command}' failed: ${errText(err)}`);\n this.sendTo(obj.from, obj.command, { success: false, error_message: errText(err) }, obj.callback);\n }\n }\n\n private async cleanupObsoleteStates(): Promise<void> {\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 * @param error The error to classify\n */\n private classifyError(error: Error & { code?: string }): string {\n if (error.code === \"RATE_LIMITED\") {\n return \"RATE_LIMITED\";\n }\n if (error.code === \"INVALID_API_KEY\") {\n return \"INVALID_API_KEY\";\n }\n // v0.4.2 (P3): 403 is a permission issue, distinct from invalid api-key.\n if (error.code === \"FORBIDDEN\") {\n return \"FORBIDDEN\";\n }\n // Network errors: DNS, connection refused, no internet\n if (\n error.code === \"ENOTFOUND\" ||\n error.code === \"ECONNREFUSED\" ||\n error.code === \"ECONNRESET\" ||\n error.code === \"ENETUNREACH\" ||\n error.code === \"EHOSTUNREACH\" ||\n error.code === \"EAI_AGAIN\"\n ) {\n return \"NETWORK\";\n }\n if (error.message.includes(\"timeout\") || error.code === \"ETIMEDOUT\") {\n return \"TIMEOUT\";\n }\n return error.code || \"UNKNOWN\";\n }\n\n private async poll(): Promise<void> {\n if (this.isPolling || !this.client || !this.stateManager) {\n return;\n }\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\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 this.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.setStateAsync(\"info.connection\", { val: true, ack: true });\n\n // Split into active (non-delivered) and visible (what gets states)\n const activeDeliveries = deliveries.filter(d => this.stateManager!.parseStatus(d) !== 0);\n const visibleDeliveries = autoRemoveMode ? activeDeliveries : deliveries;\n\n // v0.4.2 (S3): reset per-poll collision tracker so the bare-id wins\n // for the first occurrence in this poll (deterministic, back-compat).\n this.stateManager.resetPollState();\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 const idResults = await Promise.all(\n visibleDeliveries.map(async delivery => {\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 this.log.debug(\n `updateDelivery: '${delivery.tracking_number}' carrier=${delivery.carrier_code} status=${delivery.status_code}`,\n );\n const carrierName = await this.client!.getCarrierName(delivery.carrier_code);\n await this.stateManager!.updateDelivery(delivery, carrierName);\n this.failedDeliveries.delete(delivery.tracking_number);\n return this.stateManager!.packageId(delivery);\n } catch (err) {\n const msg = errText(err);\n if (this.failedDeliveries.has(delivery.tracking_number)) {\n this.log.debug(`Failed to update \"${delivery.tracking_number}\": ${msg}`);\n } else {\n this.log.warn(`Failed to update '${delivery.tracking_number}': ${msg}`);\n this.failedDeliveries.add(delivery.tracking_number);\n }\n return null;\n }\n }),\n );\n const activeIds = idResults.filter((id): id is string => id !== null);\n\n // Cleanup stale deliveries\n await this.stateManager.cleanupDeliveries(activeIds);\n\n // Update summary (always uses active/non-delivered)\n await this.stateManager.updateSummary(activeDeliveries);\n\n this.log.debug(`Polled ${visibleDeliveries.length} deliveries (${activeDeliveries.length} active)`);\n } catch (err) {\n const error = err as Error & {\n code?: string;\n retryAfterSeconds?: number;\n };\n\n // Classify the error\n const errorCode = this.classifyError(error);\n const isRepeat = errorCode === this.lastErrorCode;\n this.lastErrorCode = errorCode;\n\n if (error.code === \"RATE_LIMITED\") {\n // v0.4.2 (M9): clamp Retry-After value into [60s, 24h]. A bogus 0,\n // negative, or fractional value used to either wipe the cooldown\n // (set rateLimitedUntil to past) or set it for fractions of a\n // second \u2014 neither is the intended behavior.\n const rawCooldown = error.retryAfterSeconds ?? 0;\n const cooldownSec =\n Number.isFinite(rawCooldown) && rawCooldown > 0\n ? Math.min(24 * 3600, Math.max(60, Math.floor(rawCooldown)))\n : 5 * 60;\n this.rateLimitedUntil = Date.now() + cooldownSec * 1000;\n this.log.warn(`Rate limit hit \u2014 pausing API requests for ${Math.ceil(cooldownSec / 60)} minute(s)`);\n } else if (error.code === \"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 this.log.error(\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 );\n } else if (error.code === \"INVALID_API_KEY\") {\n // Always log \u2014 user must fix config\n this.log.error(\"Invalid API key \u2014 please check your parcel.app API key\");\n } else 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 if (errorCode === \"NETWORK\") {\n this.log.warn(\"Cannot reach parcel.app API \u2014 will keep retrying\");\n } else if (errorCode === \"TIMEOUT\") {\n this.log.warn(\"API request timeout \u2014 will retry next cycle\");\n } else {\n this.log.error(`Poll failed: ${error.message}`);\n }\n\n await this.setStateAsync(\"info.connection\", { val: false, ack: true });\n } finally {\n this.isPolling = false;\n }\n }\n}\n\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 { coerceClampedInt, errText } from \"./lib/coerce\";\nimport { ParcelClient } from \"./lib/parcel-client\";\nimport { StateManager } from \"./lib/state-manager\";\n\nconst MIN_POLL_INTERVAL = 5;\nconst MAX_POLL_INTERVAL = 60;\nconst DEFAULT_POLL_INTERVAL = 10;\nconst MIN_POLL_GAP_MS = 60_000; // Minimum 60s between polls\n/** v0.4.2 (M6): minimum length for an apiKey value to even be considered valid. */\nconst MIN_API_KEY_LENGTH = 10;\n\n/** ioBroker adapter for parcel.app package tracking */\nclass ParcelappAdapter extends utils.Adapter {\n private client: ParcelClient | null = null;\n private stateManager: StateManager | null = null;\n private pollTimer: ioBroker.Interval | undefined = undefined;\n private isPolling = false;\n private lastPollTime = 0;\n private rateLimitedUntil = 0;\n private lastErrorCode = \"\";\n private failedDeliveries = new Set<string>();\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<ParcelClient>();\n private unhandledRejectionHandler: ((reason: unknown) => void) | null = null;\n private uncaughtExceptionHandler: ((err: Error) => void) | null = null;\n /** ioBroker system language \u2014 read once in `onReady` from `system.config`. EN fallback. */\n private systemLang: string = \"en\";\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 // Safety net for fire-and-forget paths (e.g. `void this.poll()`).\n this.unhandledRejectionHandler = (reason: unknown) => {\n this.log.error(`Unhandled rejection: ${errText(reason)}`);\n this.terminate?.(11);\n };\n this.uncaughtExceptionHandler = (err: Error) => {\n this.log.error(`Uncaught exception: ${errText(err)}`);\n this.terminate?.(11);\n };\n process.on(\"unhandledRejection\", this.unhandledRejectionHandler);\n process.on(\"uncaughtException\", this.uncaughtExceptionHandler);\n }\n\n private async onReady(): Promise<void> {\n try {\n this.log.debug(\n `onReady: starting (pollInterval=${JSON.stringify(this.config.pollInterval)}, autoRemoveDelivered=${this.config.autoRemoveDelivered})`,\n );\n\n const sysConfig = await this.getForeignObjectAsync(\"system.config\");\n const language = (sysConfig?.common as { language?: string } | undefined)?.language ?? \"\";\n if (typeof language === \"string\" && language.length > 0) {\n this.systemLang = language;\n }\n this.log.debug(`system language: '${language}' \u2192 using '${this.systemLang}'`);\n\n await this.setStateAsync(\"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 = new ParcelClient(apiKey.trim(), { debug: (m: string) => this.log.debug(m) });\n this.stateManager = new StateManager(this, language);\n\n await this.cleanupObsoleteStates();\n\n await this.poll();\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(() => void this.poll(), 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 }\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 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.\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 if (this.unhandledRejectionHandler) {\n process.off(\"unhandledRejection\", this.unhandledRejectionHandler);\n this.unhandledRejectionHandler = null;\n }\n if (this.uncaughtExceptionHandler) {\n process.off(\"uncaughtException\", this.uncaughtExceptionHandler);\n this.uncaughtExceptionHandler = null;\n }\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 this.log.debug(`onUnload error (ignored): ${errText(err)}`);\n }\n callback();\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n // v0.4.3 (F1): entry log BEFORE the early-return \u2014 broadcast messages\n // without callback wouldn't be visible otherwise.\n this.log.debug(`onMessage: command='${obj?.command}' from='${obj?.from}' has-callback=${!!obj?.callback}`);\n if (!obj?.command || !obj.callback) {\n return;\n }\n\n try {\n switch (obj.command) {\n case \"checkConnection\": {\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, { success: false, message: \"API key is too short\" }, obj.callback);\n return;\n }\n // v0.4.3: same debug-logger as the prod client so checkConnection\n // failures get the same HTTPS-layer trace.\n const testClient = new ParcelClient(key, { debug: (m: string) => this.log.debug(m) });\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(obj.from, obj.command, result, obj.callback);\n } finally {\n this.testClients.delete(testClient);\n }\n break;\n }\n case \"addDelivery\": {\n if (!this.client) {\n // v0.4.3 (F4): trace addDelivery-before-init.\n this.log.debug(\"addDelivery: adapter not initialized\");\n this.sendTo(\n obj.from,\n obj.command,\n { success: false, error_message: \"Adapter not initialized\" },\n obj.callback,\n );\n return;\n }\n const request = obj.message as {\n tracking_number: string;\n carrier_code: string;\n description: string;\n };\n const addResult = await this.client.addDelivery(request);\n // v0.4.3 (F5): trace addDelivery result with the tracking number.\n this.log.debug(`addDelivery: '${request?.tracking_number}' result=${addResult.success ? \"ok\" : \"fail\"}`);\n this.sendTo(obj.from, obj.command, addResult, obj.callback);\n if (addResult.success) {\n void this.poll();\n }\n break;\n }\n default:\n // v0.4.3 (F6): trace unknown command before sendTo.\n this.log.debug(`onMessage: unknown command '${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.\n // The sendTo back to the caller is preserved unchanged.\n this.log.debug(`onMessage: '${obj.command}' failed: ${errText(err)}`);\n this.sendTo(obj.from, obj.command, { success: false, error_message: errText(err) }, obj.callback);\n }\n }\n\n private async cleanupObsoleteStates(): Promise<void> {\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 * @param error The error to classify\n */\n private classifyError(error: Error & { code?: string }): string {\n if (error.code === \"RATE_LIMITED\") {\n return \"RATE_LIMITED\";\n }\n if (error.code === \"INVALID_API_KEY\") {\n return \"INVALID_API_KEY\";\n }\n // v0.4.2 (P3): 403 is a permission issue, distinct from invalid api-key.\n if (error.code === \"FORBIDDEN\") {\n return \"FORBIDDEN\";\n }\n // Network errors: DNS, connection refused, no internet\n if (\n error.code === \"ENOTFOUND\" ||\n error.code === \"ECONNREFUSED\" ||\n error.code === \"ECONNRESET\" ||\n error.code === \"ENETUNREACH\" ||\n error.code === \"EHOSTUNREACH\" ||\n error.code === \"EAI_AGAIN\"\n ) {\n return \"NETWORK\";\n }\n if (error.message.includes(\"timeout\") || error.code === \"ETIMEDOUT\") {\n return \"TIMEOUT\";\n }\n return error.code || \"UNKNOWN\";\n }\n\n private async poll(): Promise<void> {\n if (this.isPolling || !this.client || !this.stateManager) {\n return;\n }\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\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 this.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.setStateAsync(\"info.connection\", { val: true, ack: true });\n\n // Split into active (non-delivered) and visible (what gets states)\n const activeDeliveries = deliveries.filter(d => this.stateManager!.parseStatus(d) !== 0);\n const visibleDeliveries = autoRemoveMode ? activeDeliveries : deliveries;\n\n // v0.4.2 (S3): reset per-poll collision tracker so the bare-id wins\n // for the first occurrence in this poll (deterministic, back-compat).\n this.stateManager.resetPollState();\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 const idResults = await Promise.all(\n visibleDeliveries.map(async delivery => {\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 this.log.debug(\n `updateDelivery: '${delivery.tracking_number}' carrier=${delivery.carrier_code} status=${delivery.status_code}`,\n );\n const carrierName = await this.client!.getCarrierName(delivery.carrier_code);\n await this.stateManager!.updateDelivery(delivery, carrierName);\n this.failedDeliveries.delete(delivery.tracking_number);\n return this.stateManager!.packageId(delivery);\n } catch (err) {\n const msg = errText(err);\n if (this.failedDeliveries.has(delivery.tracking_number)) {\n this.log.debug(`Failed to update \"${delivery.tracking_number}\": ${msg}`);\n } else {\n this.log.warn(`Failed to update '${delivery.tracking_number}': ${msg}`);\n this.failedDeliveries.add(delivery.tracking_number);\n }\n return null;\n }\n }),\n );\n const activeIds = idResults.filter((id): id is string => id !== null);\n\n // Cleanup stale deliveries\n await this.stateManager.cleanupDeliveries(activeIds);\n\n // Update summary (always uses active/non-delivered)\n await this.stateManager.updateSummary(activeDeliveries);\n\n this.log.debug(`Polled ${visibleDeliveries.length} deliveries (${activeDeliveries.length} active)`);\n } catch (err) {\n const error = err as Error & {\n code?: string;\n retryAfterSeconds?: number;\n };\n\n // Classify the error\n const errorCode = this.classifyError(error);\n const isRepeat = errorCode === this.lastErrorCode;\n this.lastErrorCode = errorCode;\n\n if (error.code === \"RATE_LIMITED\") {\n // v0.4.2 (M9): clamp Retry-After value into [60s, 24h]. A bogus 0,\n // negative, or fractional value used to either wipe the cooldown\n // (set rateLimitedUntil to past) or set it for fractions of a\n // second \u2014 neither is the intended behavior.\n const rawCooldown = error.retryAfterSeconds ?? 0;\n const cooldownSec =\n Number.isFinite(rawCooldown) && rawCooldown > 0\n ? Math.min(24 * 3600, Math.max(60, Math.floor(rawCooldown)))\n : 5 * 60;\n this.rateLimitedUntil = Date.now() + cooldownSec * 1000;\n this.log.warn(`Rate limit hit \u2014 pausing API requests for ${Math.ceil(cooldownSec / 60)} minute(s)`);\n } else if (error.code === \"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 this.log.error(\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 );\n } else if (error.code === \"INVALID_API_KEY\") {\n // Always log \u2014 user must fix config\n this.log.error(\"Invalid API key \u2014 please check your parcel.app API key\");\n } else 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 if (errorCode === \"NETWORK\") {\n this.log.warn(\"Cannot reach parcel.app API \u2014 will keep retrying\");\n } else if (errorCode === \"TIMEOUT\") {\n this.log.warn(\"API request timeout \u2014 will retry next cycle\");\n } else {\n this.log.error(`Poll failed: ${error.message}`);\n }\n\n await this.setStateAsync(\"info.connection\", { val: false, ack: true });\n } finally {\n this.isPolling = false;\n }\n }\n}\n\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.4.8",
4
+ "version": "0.4.9",
5
5
  "news": {
6
+ "0.4.9": {
7
+ "en": "Use community-standard event handler pattern",
8
+ "de": "Community-Standard Event-Handler-Pattern verwenden",
9
+ "ru": "Использование стандартного паттерна обработчика событий сообщества",
10
+ "pt": "Usar padrão de manipulador de eventos padrão da comunidade",
11
+ "nl": "Community-standaard event handler patroon gebruiken",
12
+ "fr": "Utiliser le modèle de gestionnaire d'événements standard de la communauté",
13
+ "it": "Utilizzare il pattern standard della community per i gestori degli eventi",
14
+ "es": "Usar el patrón de manejador de eventos estándar de la comunidad",
15
+ "pl": "Użyj standardowego wzorca obsługi zdarzeń społeczności",
16
+ "uk": "Використання стандартного шаблону обробника подій спільноти",
17
+ "zh-cn": "使用社区标准事件处理程序模式"
18
+ },
6
19
  "0.4.8": {
7
20
  "en": "Code quality enforced with standard formatting.",
8
21
  "de": "Codequalität mit standardisierter Formatierung durchgesetzt.",
@@ -80,19 +93,6 @@
80
93
  "pl": "Log debugowania śledzi ciche ścieżki: cykl HTTPS, lista przewoźników, aktualizacje paczek i wiadomości admin. Domyślny log bez zmian.",
81
94
  "uk": "Журнал зневадження відстежує тихі шляхи: цикл HTTPS, список перевізників, оновлення посилок та повідомлення адміна. Стандартний лог без змін.",
82
95
  "zh-cn": "调试日志跟踪以前静默的路径:HTTPS 请求生命周期、承运商列表、每个包裹更新以及管理消息处理。默认日志保持不变。"
83
- },
84
- "0.4.2": {
85
- "en": "Adapter shuts down cleanly when parcel.app is slow, 403 Forbidden gives a 'check Premium' hint instead of reauth-loop, two trackings with the same sanitized id no longer overwrite.",
86
- "de": "Adapter beendet sauber wenn parcel.app langsam ist, 403-Fehler liefert 'Premium prüfen'-Hinweis statt Reauth-Loop, und zwei Trackings mit identischer Sanitize-ID überschreiben sich nicht mehr.",
87
- "ru": "Адаптер корректно завершается при медленной parcel.app, 403 даёт подсказку 'проверьте Premium' вместо цикла переавторизации, два трекинга с одинаковым ID больше не перезаписываются.",
88
- "pt": "O adaptador encerra de forma limpa quando o parcel.app está lento, 403 mostra dica 'verificar Premium' em vez de re-autenticar em loop, dois rastreios com o mesmo ID já não se sobrescrevem.",
89
- "nl": "Adapter sluit netjes af als parcel.app traag is, 403 geeft 'Premium controleren'-hint in plaats van reauth-loop, en twee trackings met dezelfde id overschrijven elkaar niet meer.",
90
- "fr": "L'adaptateur s'arrête proprement quand parcel.app est lent, un 403 donne 'vérifier Premium' au lieu de boucler la réauth, et deux suivis avec le même id ne s'écrasent plus.",
91
- "it": "L'adapter si arresta correttamente quando parcel.app è lento, un 403 mostra 'controlla Premium' invece di un loop di reauth, due tracciamenti con lo stesso id non si sovrascrivono più.",
92
- "es": "El adaptador se cierra limpiamente cuando parcel.app va lento, 403 muestra 'verificar Premium' en vez de bucle de reauth, y dos seguimientos con el mismo id ya no se sobrescriben.",
93
- "pl": "Adapter zamyka się czysto, gdy parcel.app jest powolny, 403 daje wskazówkę 'sprawdź Premium' zamiast pętli reauth, dwa śledzenia z tym samym id już się nie nadpisują.",
94
- "uk": "Адаптер коректно вимикається при повільному parcel.app, 403 дає підказку 'перевірте Premium' замість циклу входу, два трекінги з однаковим id більше не перезаписуються.",
95
- "zh-cn": "当 parcel.app 响应缓慢时,适配器现在能干净退出(终止进行中的请求)。403 错误会提示检查 Premium 订阅,而不是反复重新认证。两个清洗后 ID 相同的快递不再互相覆盖。"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.parcelapp",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "description": "ioBroker adapter for the parcel.app API",
5
5
  "author": {
6
6
  "name": "krobi",