Compare commits

..

8 Commits

Author SHA1 Message Date
Pascal Bourque
d9d6fc2861 Merge branch 'main' into 180-rapid-succession-of-aws_error_mqtt_unexpected_hangup-errors 2025-11-28 09:19:03 -05:00
Pascal Bourque
1b680b693f Example: process devices serially instead of in parallel 2025-11-28 08:43:23 -05:00
Pascal Bourque
181c9238da Merge branch 'main' into 180-rapid-succession-of-aws_error_mqtt_unexpected_hangup-errors 2025-11-23 10:35:14 -05:00
Pascal Bourque
6c24d59760 Added option to enable AWS CRT debug logging 2025-11-23 10:29:41 -05:00
Pascal Bourque
f7c3dc07b3 Recreate MQTT client on interrupt when credentials have expired 2025-11-23 09:53:45 -05:00
Pascal Bourque
2a2a843534 Increased ping timeout 2025-11-16 10:53:35 -05:00
Pascal Bourque
2d49a4ddb9 Hash username in MQTT client ID generation
Replaces the plain username in the MQTT client ID with a SHA-1 hash for improved privacy and to avoid exposing usernames in client identifiers.
2025-11-09 10:57:45 -05:00
Pascal Bourque
5fce04543a Improve MQTT clientId handling and interrupt recovery
Adds process PID to MQTT clientId to reduce collision risk and enhances logging with clientId context. Implements a guard against re-entrant MQTT resets during interrupt storms, clears connection promise before disconnect, and ensures device subscriptions are re-established after recovery. Also adds handling for new error type and improves error reporting.
2025-11-09 10:52:31 -05:00
5 changed files with 44 additions and 15 deletions

View File

@@ -20,6 +20,7 @@ const rootLogger = pino({
/** Main entry point of the example application. */
async function main() {
let session: MysaSession | undefined;
try {
rootLogger.info('Loading session...');
const sessionJson = await readFile('session.json', 'utf8');
@@ -27,7 +28,11 @@ async function main() {
} catch {
rootLogger.info('No valid session file found.');
}
const client = new MysaApiClient(session, { logger: rootLogger.child({ module: 'mysa-js-sdk' }) });
const client = new MysaApiClient(session, {
logger: rootLogger.child({ module: 'mysa-js-sdk' }),
isAwsCrtDebugLoggingEnabled: process.env.AWS_CRT_DEBUG_LOGGING === '1'
});
client.emitter.on('sessionChanged', async (newSession) => {
if (newSession) {
@@ -96,14 +101,12 @@ async function main() {
});
}
await Promise.all(
Object.entries(devices.DevicesObj).map(async ([deviceId, device]) => {
const serial = await client.getDeviceSerialNumber(deviceId);
rootLogger.info(`Serial number for device '${deviceId}' (${device.Name ?? 'Unknown'}): ${serial}`);
for (const [deviceId, device] of Object.entries(devices.DevicesObj)) {
const serial = await client.getDeviceSerialNumber(deviceId);
rootLogger.info(`Serial number for device '${deviceId}' (${device.Name ?? 'Unknown'}): ${serial}`);
await client.startRealtimeUpdates(deviceId);
})
);
await client.startRealtimeUpdates(deviceId);
}
}
main().catch((error) => {

23
package-lock.json generated
View File

@@ -14,7 +14,7 @@
"amazon-cognito-identity-js": "6.3.16",
"aws-iot-device-sdk-v2": "1.23.1",
"dayjs": "1.11.19",
"lodash": "4.17.23",
"lodash": "4.17.21",
"nanoid": "5.1.6"
},
"devDependencies": {
@@ -2547,6 +2547,7 @@
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/auth-token": "^6.0.0",
"@octokit/graphql": "^9.0.3",
@@ -4064,6 +4065,7 @@
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/types": "8.46.2",
@@ -4272,6 +4274,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4462,6 +4465,7 @@
"integrity": "sha512-LPVCX4If8nAy1OERzmFx/pVr4g6olBqbz30SoLTkVJu5m5WH5GkhbIKnucQ1e5ZbpfNh+nrsFM2dolXO8ow4PQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@aws-sdk/util-utf8-browser": "^3.259.0",
"@httptoolkit/websocket-stream": "^6.0.1",
@@ -5885,6 +5889,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
@@ -5948,6 +5953,7 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -7461,9 +7467,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash-es": {
@@ -7570,6 +7576,7 @@
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"marked": "bin/marked.js"
},
@@ -10796,6 +10803,7 @@
"dev": true,
"inBundle": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -11643,6 +11651,7 @@
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -12145,6 +12154,7 @@
"integrity": "sha512-6qGjWccl5yoyugHt3jTgztJ9Y0JVzyH8/Voc/D8PlLat9pwxQYXz7W1Dpnq5h0/G5GCYGUaDSlYcyk3AMh5A6g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/error": "^4.0.0",
@@ -13106,6 +13116,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -13267,6 +13278,7 @@
"integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
@@ -13396,6 +13408,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -13763,6 +13776,7 @@
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.0.0"
},
@@ -13810,6 +13824,7 @@
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"dev": true,
"license": "ISC",
"peer": true,
"bin": {
"yaml": "bin.mjs"
},

View File

@@ -55,7 +55,7 @@
"amazon-cognito-identity-js": "6.3.16",
"aws-iot-device-sdk-v2": "1.23.1",
"dayjs": "1.11.19",
"lodash": "4.17.23",
"lodash": "4.17.21",
"nanoid": "5.1.6"
},
"devDependencies": {

View File

@@ -18,7 +18,7 @@ import {
CognitoUserPool,
CognitoUserSession
} from 'amazon-cognito-identity-js';
import { iot, mqtt } from 'aws-iot-device-sdk-v2';
import { io, iot, mqtt } from 'aws-iot-device-sdk-v2';
import { hash } from 'crypto';
import dayjs, { Dayjs } from 'dayjs';
import duration from 'dayjs/plugin/duration.js';
@@ -152,6 +152,10 @@ export class MysaApiClient {
this._logger = options?.logger || new VoidLogger();
this._fetcher = options?.fetcher || fetch;
if (options?.isAwsCrtDebugLoggingEnabled) {
io.enable_logging(io.LogLevel.DEBUG);
}
if (session) {
this._cognitoUser = new CognitoUser({
Username: session.username,
@@ -741,7 +745,7 @@ export class MysaApiClient {
.with_client_id(this._mqttClientId)
.with_clean_session(false)
.with_keep_alive_seconds(30)
.with_ping_timeout_ms(3000)
.with_ping_timeout_ms(10000)
.with_protocol_operation_timeout_ms(60000)
.with_reconnect_min_sec(1)
.with_reconnect_max_sec(30);

View File

@@ -15,4 +15,11 @@ export interface MysaApiClientOptions {
* @defaultValue The global `fetch` function.
*/
fetcher?: typeof fetch;
/**
* Whether to enable debug logging for AWS CRT.
*
* @defaultValue `false`
*/
isAwsCrtDebugLoggingEnabled?: boolean;
}