mysa-js-sdk 2.1.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # mysa-js-sdk
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#199](https://github.com/bourquep/mysa2mqtt/pull/199) [`b384d79`](https://github.com/bourquep/mysa2mqtt/commit/b384d7950d757bc85af7580eaa26435190b47364) Thanks [@bourquep](https://github.com/bourquep)! - The client now takes Mysa account credentials instead of a session object, and re-authenticates on its own when its refresh token expires or is revoked, so long-running processes no longer die after about a month. `new MysaApiClient(session, options)` becomes `new MysaApiClient({ username, password }, options)`, `login()` no longer takes arguments and is optional (the client authenticates on demand), and the `MysaSession` type, the `session` and `isAuthenticated` properties, and the `sessionChanged` event are gone — there is no longer any session state to persist.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#201](https://github.com/bourquep/mysa2mqtt/pull/201) [`10ee91c`](https://github.com/bourquep/mysa2mqtt/commit/10ee91c1981b77ef1e9f76abd3d24ba6d9a19d77) Thanks [@bourquep](https://github.com/bourquep)! - A Mysa login rejected for a bad password now reports that a `$` in it is expanded by shells and by Docker Compose (in both `environment:` entries and default-format `env_file:` files, where it must be written `$$`, but not in an `env_file:` declared with `format: raw`), and that an unquoted `#` truncates a `.env` value, instead of surfacing a bare `Incorrect username or password.` stack trace. An unrecognized account gets username-specific guidance instead, since none of the password escaping rules apply to it. Transport and Cognito service failures keep propagating without that guidance, since `UnauthenticatedError` now carries the underlying failure as its `cause`. Debug logging also reports the length of the password that was actually received -- never the password or the account it belongs to -- so a mangled value can be spotted at a glance.
12
+
13
+ - [#190](https://github.com/bourquep/mysa2mqtt/pull/190) [`7169263`](https://github.com/bourquep/mysa2mqtt/commit/7169263fcb4b2aeb51c7eae4c112972c3e2fdb08) Thanks [@vavallee](https://github.com/vavallee)! - Payload parse failures no longer write to console.error, which bypassed the configurable SDK logger and could leak payload contents. The error is rethrown with the original failure attached as cause and logged by the message handler through the SDK logger.
14
+
15
+ - [#191](https://github.com/bourquep/mysa2mqtt/pull/191) [`527ef25`](https://github.com/bourquep/mysa2mqtt/commit/527ef25886aad766a2fd71fc92002d8de126b364) Thanks [@vavallee](https://github.com/vavallee)! - setDeviceState now throws a descriptive UnknownDeviceError when the device id does not match any device on the account, instead of failing with a raw TypeError on an undefined dereference.
16
+
17
+ - [#200](https://github.com/bourquep/mysa2mqtt/pull/200) [`61dc2a2`](https://github.com/bourquep/mysa2mqtt/commit/61dc2a2b395397e9e5245098bfd31b49b501fd7d) Thanks [@bourquep](https://github.com/bourquep)! - V2 thermostats can now report power. These devices have no current sensor and only report the duty cycle of their heating relay, which is why their **Current power** sensor has always been unavailable. Set the new `--heater-watts` option (`M2M_HEATER_WATTS`) to the rated wattage of the heaters each thermostat controls — for example `M2M_HEATER_WATTS="Kitchen=1500,<device-id>=750"`, matching devices by name or id — and power is estimated as `duty cycle × rated wattage`. V1 thermostats measure their own current and continue to work with no configuration.
18
+
19
+ The power sensor is now only created for devices that can actually report a value. If you have AC devices, or V2 thermostats for which you have not configured a wattage, their **Current power** entity is removed from Home Assistant on startup; it only ever showed as unavailable.
20
+
21
+ `mqtt2ha` gains a `Discoverable.removeConfig()` method, which clears an entity's retained discovery topic so Home Assistant drops the entity. This is what makes the removal above take effect: because `writeConfig()` retains its payload, an entity published by an earlier run persists until its topic is explicitly cleared.
22
+
3
23
  ## 2.1.2
4
24
 
5
25
  ### Patch Changes
package/README.md CHANGED
@@ -66,17 +66,20 @@ The Mysa SDK provides a simple interface to interact with Mysa smart thermostats
66
66
  ```typescript
67
67
  import { MysaApiClient } from 'mysa-js-sdk';
68
68
 
69
- const client = new MysaApiClient();
70
-
71
- // Login with email and password
72
- await client.login('your-email@example.com', 'your-password');
69
+ // The client holds your credentials and authenticates on demand
70
+ const client = new MysaApiClient({
71
+ username: 'your-email@example.com',
72
+ password: 'your-password'
73
+ });
73
74
 
74
- // Check if authenticated
75
- if (client.isAuthenticated) {
76
- console.log('Successfully authenticated!');
77
- }
75
+ // Optional: log in eagerly to fail fast on invalid credentials
76
+ await client.login();
78
77
  ```
79
78
 
79
+ The client manages its session on its own: it refreshes the session when it needs to, and logs back in automatically
80
+ when the refresh token has itself expired or been revoked. There is nothing to persist, and no need to re-create the
81
+ client after a long run.
82
+
80
83
  ### Retrieving Thermostat Data
81
84
 
82
85
  Once authenticated, you can access your thermostat data:
@@ -139,10 +142,9 @@ The SDK provides specific error types to handle API errors:
139
142
  ```typescript
140
143
  import { MysaApiClient, MysaApiError, UnauthenticatedError } from 'mysa-js-sdk';
141
144
 
142
- const client = new MysaApiClient();
145
+ const client = new MysaApiClient({ username: 'user@example.com', password: 'password' });
143
146
 
144
147
  try {
145
- await client.login('user@example.com', 'password');
146
148
  const devices = await client.getDevices();
147
149
  } catch (error) {
148
150
  if (error instanceof UnauthenticatedError) {
@@ -170,31 +172,13 @@ const logger = pino({
170
172
  });
171
173
 
172
174
  // Configure client with options
173
- const client = new MysaApiClient(undefined, {
174
- logger: logger,
175
- fetcher: fetch // Custom fetch implementation if needed
176
- });
177
-
178
- // Or restore from a saved session
179
- const savedSession = {
180
- username: 'user@example.com',
181
- idToken: 'eyJ...',
182
- accessToken: 'eyJ...',
183
- refreshToken: 'abc123...'
184
- };
185
-
186
- const clientWithSession = new MysaApiClient(savedSession, { logger });
187
-
188
- // Listen for session changes to persist them
189
- client.emitter.on('sessionChanged', (newSession) => {
190
- if (newSession) {
191
- // Save session to storage (file, database, etc.)
192
- localStorage.setItem('mysaSession', JSON.stringify(newSession));
193
- } else {
194
- // Session expired or logged out
195
- localStorage.removeItem('mysaSession');
175
+ const client = new MysaApiClient(
176
+ { username: 'user@example.com', password: 'password' },
177
+ {
178
+ logger: logger,
179
+ fetcher: fetch // Custom fetch implementation if needed
196
180
  }
197
- });
181
+ );
198
182
  ```
199
183
 
200
184
  ### Reference documentation
package/dist/index.cjs CHANGED
@@ -59,20 +59,31 @@ __export(index_exports, {
59
59
  MysaApiError: () => MysaApiError,
60
60
  OutMessageType: () => OutMessageType,
61
61
  UnauthenticatedError: () => UnauthenticatedError,
62
+ UnknownDeviceError: () => UnknownDeviceError,
62
63
  VoidLogger: () => VoidLogger
63
64
  });
64
65
  module.exports = __toCommonJS(index_exports);
65
66
 
66
67
  // src/api/Errors.ts
67
68
  var UnauthenticatedError = class extends Error {
69
+ /**
70
+ * The underlying failure that prevented authentication.
71
+ *
72
+ * Callers need it to tell a credential rejection apart from a transport or service failure, since both surface as
73
+ * this error. Declared explicitly rather than passed to `super` so the SDK keeps compiling against its current lib,
74
+ * which predates the two-argument `Error` constructor.
75
+ */
76
+ cause;
68
77
  /**
69
78
  * Creates a new UnauthenticatedError instance.
70
79
  *
71
80
  * @param message - The error message
81
+ * @param cause - The underlying failure that prevented authentication, if any.
72
82
  */
73
- constructor(message) {
83
+ constructor(message, cause) {
74
84
  super(message);
75
85
  this.name = "UnauthenticatedError";
86
+ this.cause = cause;
76
87
  }
77
88
  };
78
89
  var MysaApiError = class extends Error {
@@ -94,6 +105,19 @@ var MysaApiError = class extends Error {
94
105
  this.statusText = apiResponse.statusText;
95
106
  }
96
107
  };
108
+ var UnknownDeviceError = class extends Error {
109
+ /**
110
+ * Creates a new UnknownDeviceError instance.
111
+ *
112
+ * @param deviceId - The device id that could not be resolved
113
+ */
114
+ constructor(deviceId) {
115
+ super(`Unknown device id '${deviceId}': no such device on this account.`);
116
+ this.deviceId = deviceId;
117
+ this.name = "UnknownDeviceError";
118
+ }
119
+ deviceId;
120
+ };
97
121
  var MqttPublishError = class extends Error {
98
122
  /**
99
123
  * Creates a new MqttPublishError instance.
@@ -191,8 +215,9 @@ function parseMqttPayload(payload) {
191
215
  const jsonString = decoder.decode(payload);
192
216
  return JSON.parse(jsonString);
193
217
  } catch (error) {
194
- console.error("Error parsing MQTT payload:", error);
195
- throw new Error("Failed to parse MQTT payload");
218
+ const parseError = new Error("Failed to parse MQTT payload");
219
+ parseError.cause = error;
220
+ throw parseError;
196
221
  }
197
222
  }
198
223
  function serializeMqttPayload(payload) {
@@ -250,10 +275,14 @@ var MysaApiBaseUrl = "https://app-prod.mysa.cloud";
250
275
  var RealtimeKeepAliveInterval = import_dayjs.default.duration(5, "minutes");
251
276
  var PublishAckTimeout = import_dayjs.default.duration(30, "seconds");
252
277
  var MysaApiClient = class {
278
+ /** The credentials of the Mysa account this client authenticates as. */
279
+ _credentials;
253
280
  /** The current session object, if any. */
254
281
  _cognitoUserSession;
255
282
  /** The current user object, if any. */
256
283
  _cognitoUser;
284
+ /** The in-flight session acquisition, if any, so that concurrent callers share a single refresh or login. */
285
+ _freshSessionPromise;
257
286
  /** The logger instance used by the client. */
258
287
  _logger;
259
288
  /** The fetcher function used by the client. */
@@ -278,89 +307,62 @@ var MysaApiClient = class {
278
307
  * @see {@link MysaApiClientEventTypes} for the possible events and their payloads.
279
308
  */
280
309
  emitter = new EventEmitter();
281
- /**
282
- * Gets the persistable session object.
283
- *
284
- * @returns The current persistable session object, if any.
285
- */
286
- get session() {
287
- if (!this._cognitoUserSession || !this._cognitoUser) {
288
- return void 0;
289
- }
290
- return {
291
- username: this._cognitoUser.getUsername(),
292
- idToken: this._cognitoUserSession.getIdToken().getJwtToken(),
293
- accessToken: this._cognitoUserSession.getAccessToken().getJwtToken(),
294
- refreshToken: this._cognitoUserSession.getRefreshToken().getToken()
295
- };
296
- }
297
- /**
298
- * Returns whether the client currently has an active session.
299
- *
300
- * @returns True if the client has an active session, false otherwise.
301
- */
302
- get isAuthenticated() {
303
- return !!this.session;
304
- }
305
310
  /**
306
311
  * Constructs a new instance of the MysaApiClient.
307
312
  *
308
- * @param session - The persistable session object, if any.
313
+ * @param credentials - The credentials of the Mysa account to authenticate as.
309
314
  * @param options - The options for the client.
310
315
  */
311
- constructor(session, options) {
316
+ constructor(credentials, options) {
317
+ this._credentials = credentials;
312
318
  this._logger = (options == null ? void 0 : options.logger) || new VoidLogger();
313
319
  this._fetcher = (options == null ? void 0 : options.fetcher) || fetch;
314
- if (session) {
315
- this._cognitoUser = new import_amazon_cognito_identity_js.CognitoUser({
316
- Username: session.username,
317
- Pool: new import_amazon_cognito_identity_js.CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })
318
- });
319
- this._cognitoUserSession = new import_amazon_cognito_identity_js.CognitoUserSession({
320
- IdToken: new import_amazon_cognito_identity_js.CognitoIdToken({ IdToken: session.idToken }),
321
- AccessToken: new import_amazon_cognito_identity_js.CognitoAccessToken({ AccessToken: session.accessToken }),
322
- RefreshToken: new import_amazon_cognito_identity_js.CognitoRefreshToken({ RefreshToken: session.refreshToken })
323
- });
324
- }
325
320
  }
326
321
  /**
327
- * Logs in the user with the given email address and password.
322
+ * Ensures the client has a usable session, logging in with the credentials it was constructed with if needed.
328
323
  *
329
- * This method authenticates the user with Mysa's Cognito user pool and establishes a session that can be used for
330
- * subsequent API calls. Upon successful login, a 'sessionChanged' event is emitted.
324
+ * Calling this method is optional: the client authenticates on demand before its first API call, and re-authenticates
325
+ * on its own whenever its session can no longer be refreshed. Call it explicitly at startup to fail fast on invalid
326
+ * credentials instead of on the first API call. It is a no-op when the current session is still usable.
331
327
  *
332
328
  * @example
333
329
  *
334
330
  * ```typescript
335
331
  * try {
336
- * await client.login('user@example.com', 'password123');
332
+ * await client.login();
337
333
  * console.log('Login successful!');
338
334
  * } catch (error) {
339
335
  * console.error('Login failed:', error.message);
340
336
  * }
341
337
  * ```
342
338
  *
343
- * @param emailAddress - The email address of the user.
344
- * @param password - The password of the user.
339
+ * @throws {@link UnauthenticatedError} When authentication fails due to invalid credentials or network issues.
340
+ */
341
+ async login() {
342
+ await this._getFreshSession();
343
+ }
344
+ /**
345
+ * Authenticates with Mysa's Cognito user pool and replaces the current session, if any.
346
+ *
347
+ * @returns A promise that resolves to the newly established session.
345
348
  * @throws {@link Error} When authentication fails due to invalid credentials or network issues.
346
349
  */
347
- async login(emailAddress, password) {
350
+ _login() {
348
351
  this._cognitoUser = void 0;
349
352
  this._cognitoUserSession = void 0;
350
353
  this._mqttClientId = void 0;
351
354
  this._mqttInterrupts = [];
352
- this.emitter.emit("sessionChanged", this.session);
355
+ const { username, password } = this._credentials;
353
356
  return new Promise((resolve, reject) => {
354
357
  const user = new import_amazon_cognito_identity_js.CognitoUser({
355
- Username: emailAddress,
358
+ Username: username,
356
359
  Pool: new import_amazon_cognito_identity_js.CognitoUserPool({ UserPoolId: CognitoUserPoolId, ClientId: CognitoClientId })
357
360
  });
358
- user.authenticateUser(new import_amazon_cognito_identity_js.AuthenticationDetails({ Username: emailAddress, Password: password }), {
361
+ user.authenticateUser(new import_amazon_cognito_identity_js.AuthenticationDetails({ Username: username, Password: password }), {
359
362
  onSuccess: (session) => {
360
363
  this._cognitoUser = user;
361
364
  this._cognitoUserSession = session;
362
- this.emitter.emit("sessionChanged", this.session);
363
- resolve();
365
+ resolve(session);
364
366
  },
365
367
  onFailure: (err) => {
366
368
  reject(err);
@@ -385,7 +387,7 @@ var MysaApiClient = class {
385
387
  *
386
388
  * @returns A promise that resolves to the list of devices.
387
389
  * @throws {@link MysaApiError} When the API request fails.
388
- * @throws {@link UnauthenticatedError} When the user is not authenticated.
390
+ * @throws {@link UnauthenticatedError} When the client cannot authenticate with its credentials.
389
391
  */
390
392
  async getDevices() {
391
393
  this._logger.debug(`Fetching devices...`);
@@ -419,7 +421,7 @@ var MysaApiClient = class {
419
421
  *
420
422
  * @param deviceId - The ID of the device to get the serial number for.
421
423
  * @returns A promise that resolves to the serial number, or undefined if not found.
422
- * @throws {@link UnauthenticatedError} When the user is not authenticated.
424
+ * @throws {@link UnauthenticatedError} When the client cannot authenticate with its credentials.
423
425
  */
424
426
  async getDeviceSerialNumber(deviceId) {
425
427
  var _a;
@@ -457,7 +459,7 @@ var MysaApiClient = class {
457
459
  *
458
460
  * @returns A promise that resolves to the firmware information for all devices.
459
461
  * @throws {@link MysaApiError} When the API request fails.
460
- * @throws {@link UnauthenticatedError} When the user is not authenticated.
462
+ * @throws {@link UnauthenticatedError} When the client cannot authenticate with its credentials.
461
463
  */
462
464
  async getDeviceFirmwares() {
463
465
  this._logger.debug(`Fetching device firmwares...`);
@@ -477,7 +479,7 @@ var MysaApiClient = class {
477
479
  *
478
480
  * @returns A promise that resolves to the current state of all devices.
479
481
  * @throws {@link MysaApiError} When the API request fails.
480
- * @throws {@link UnauthenticatedError} When the user is not authenticated.
482
+ * @throws {@link UnauthenticatedError} When the client cannot authenticate with its credentials.
481
483
  */
482
484
  async getDeviceStates() {
483
485
  this._logger.debug(`Fetching device states...`);
@@ -497,7 +499,7 @@ var MysaApiClient = class {
497
499
  *
498
500
  * @returns A promise that resolves to the homes information.
499
501
  * @throws {@link MysaApiError} When the API request fails.
500
- * @throws {@link UnauthenticatedError} When the user is not authenticated.
502
+ * @throws {@link UnauthenticatedError} When the client cannot authenticate with its credentials.
501
503
  */
502
504
  async getHomes() {
503
505
  this._logger.debug(`Fetching homes...`);
@@ -539,16 +541,20 @@ var MysaApiClient = class {
539
541
  * @param mode - The operating mode to set (one of MysaDeviceMode values, or undefined to leave unchanged).
540
542
  * @param fanSpeed - The fan speed mode to set ('low', 'medium', 'high', 'max', 'auto', or undefined to leave
541
543
  * unchanged).
542
- * @throws {@link UnauthenticatedError} When the user is not authenticated.
544
+ * @throws {@link UnauthenticatedError} When the client cannot authenticate with its credentials.
545
+ * @throws {@link UnknownDeviceError} When the device id does not match any device on the account.
543
546
  * @throws {@link Error} When MQTT connection or command sending fails.
544
547
  */
545
548
  async setDeviceState(deviceId, setPoint, mode, fanSpeed) {
546
- var _a;
547
549
  this._logger.debug(`Setting device state for '${deviceId}'`);
548
550
  if (!this._cachedDevices) {
549
551
  this._cachedDevices = await this.getDevices();
550
552
  }
553
+ if (!Object.prototype.hasOwnProperty.call(this._cachedDevices.DevicesObj, deviceId)) {
554
+ throw new UnknownDeviceError(deviceId);
555
+ }
551
556
  const device = this._cachedDevices.DevicesObj[deviceId];
557
+ await this._getFreshSession();
552
558
  this._logger.debug(`Initializing MQTT connection...`);
553
559
  const mqttConnection = await this._getMqttConnection();
554
560
  const now = (0, import_dayjs.default)();
@@ -561,7 +567,7 @@ var MysaApiClient = class {
561
567
  time: now.unix(),
562
568
  ver: "1.0",
563
569
  src: {
564
- ref: ((_a = this.session) == null ? void 0 : _a.username) ?? "",
570
+ ref: this._cognitoUser.getUsername(),
565
571
  type: 100
566
572
  },
567
573
  dest: {
@@ -679,33 +685,58 @@ var MysaApiClient = class {
679
685
  * Ensures a valid, non-expired session is available.
680
686
  *
681
687
  * This method checks if the current session is valid and not expired. If the session is expired, it automatically
682
- * refreshes it using the refresh token.
688
+ * refreshes it using the refresh token. If there is no session yet, or if the refresh token has itself expired or
689
+ * been revoked, the client logs back in with its credentials.
690
+ *
691
+ * Concurrent callers share a single in-flight acquisition, so a burst of API calls never triggers more than one
692
+ * refresh or login.
683
693
  *
684
694
  * @returns A promise that resolves to a valid CognitoUserSession.
685
- * @throws {@link UnauthenticatedError} When no session exists or refresh fails.
695
+ * @throws {@link UnauthenticatedError} When neither refreshing nor logging back in succeeds.
686
696
  */
687
697
  async _getFreshSession() {
688
- if (!this._cognitoUser || !this._cognitoUserSession) {
689
- throw new UnauthenticatedError("An attempt was made to access a resource without a valid session.");
690
- }
691
- if (this._cognitoUserSession.isValid() && import_dayjs.default.unix(this._cognitoUserSession.getIdToken().getExpiration()).isAfter()) {
698
+ var _a;
699
+ if (this._cognitoUser && ((_a = this._cognitoUserSession) == null ? void 0 : _a.isValid()) && import_dayjs.default.unix(this._cognitoUserSession.getIdToken().getExpiration()).isAfter()) {
692
700
  this._logger.debug("Session is valid, no need to refresh");
693
- return Promise.resolve(this._cognitoUserSession);
701
+ return this._cognitoUserSession;
694
702
  }
695
- this._logger.debug("Session is not valid or expired, refreshing...");
696
- return new Promise((resolve, reject) => {
697
- this._cognitoUser.refreshSession(this._cognitoUserSession.getRefreshToken(), (error, session) => {
698
- if (error) {
699
- this._logger.error("Failed to refresh session:", error);
700
- reject(new UnauthenticatedError("Unable to refresh the authentication session."));
701
- } else {
702
- this._logger.debug("Session refreshed successfully");
703
- this._cognitoUserSession = session;
704
- this.emitter.emit("sessionChanged", this.session);
705
- resolve(session);
706
- }
707
- });
703
+ this._freshSessionPromise ??= this._acquireFreshSession().finally(() => {
704
+ this._freshSessionPromise = void 0;
708
705
  });
706
+ return this._freshSessionPromise;
707
+ }
708
+ /**
709
+ * Refreshes the current session, falling back to a full login when it cannot be refreshed.
710
+ *
711
+ * @returns A promise that resolves to a valid CognitoUserSession.
712
+ * @throws {@link UnauthenticatedError} When logging back in fails.
713
+ */
714
+ async _acquireFreshSession() {
715
+ if (this._cognitoUser && this._cognitoUserSession) {
716
+ this._logger.debug("Session is not valid or expired, refreshing...");
717
+ try {
718
+ return await new Promise((resolve, reject) => {
719
+ this._cognitoUser.refreshSession(this._cognitoUserSession.getRefreshToken(), (error, session) => {
720
+ if (error) {
721
+ reject(error);
722
+ } else {
723
+ this._logger.debug("Session refreshed successfully");
724
+ this._cognitoUserSession = session;
725
+ resolve(session);
726
+ }
727
+ });
728
+ });
729
+ } catch (error) {
730
+ this._logger.warn("Failed to refresh session, logging back in:", error);
731
+ }
732
+ }
733
+ try {
734
+ this._logger.info("Logging in...");
735
+ return await this._login();
736
+ } catch (error) {
737
+ this._logger.error("Failed to log in:", error);
738
+ throw new UnauthenticatedError("Unable to establish an authentication session.", error);
739
+ }
709
740
  }
710
741
  /**
711
742
  * Establishes and returns an MQTT connection for real-time communication.
@@ -847,7 +878,7 @@ var MysaApiClient = class {
847
878
  throw new Error("MQTT credentials are already expired.");
848
879
  }
849
880
  if (!this._mqttClientId) {
850
- const username = ((_a = this.session) == null ? void 0 : _a.username) ?? "anon";
881
+ const username = ((_a = this._cognitoUser) == null ? void 0 : _a.getUsername()) ?? "anon";
851
882
  const usernameHash = (0, import_crypto.hash)("sha1", username);
852
883
  this._mqttClientId = `mysa-js-sdk-${usernameHash}-${process.pid}-${getRandomClientId()}`;
853
884
  }
@@ -1060,6 +1091,7 @@ var MysaApiClient = class {
1060
1091
  MysaApiError,
1061
1092
  OutMessageType,
1062
1093
  UnauthenticatedError,
1094
+ UnknownDeviceError,
1063
1095
  VoidLogger
1064
1096
  });
1065
1097
  //# sourceMappingURL=index.cjs.map