myio-js-library 0.1.52 → 0.1.53

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/dist/index.cjs CHANGED
@@ -576,12 +576,14 @@ __export(index_exports, {
576
576
  addNamespace: () => addNamespace,
577
577
  averageByDay: () => averageByDay,
578
578
  buildListItemsThingsboardByUniqueDatasource: () => buildListItemsThingsboardByUniqueDatasource,
579
+ buildMyioIngestionAuth: () => buildMyioIngestionAuth,
579
580
  buildWaterReportCSV: () => buildWaterReportCSV,
580
581
  buildWaterStoresCSV: () => buildWaterStoresCSV,
581
582
  calcDeltaPercent: () => calcDeltaPercent,
582
583
  classify: () => classify,
583
584
  classifyWaterLabel: () => classifyWaterLabel,
584
585
  classifyWaterLabels: () => classifyWaterLabels,
586
+ clearAllAuthCaches: () => clearAllAuthCaches,
585
587
  createDateRangePicker: () => createDateRangePicker2,
586
588
  createInputDateRangePickerInsideDIV: () => createInputDateRangePickerInsideDIV,
587
589
  decodePayload: () => decodePayload,
@@ -603,6 +605,7 @@ __export(index_exports, {
603
605
  formatTankHeadFromCm: () => formatTankHeadFromCm,
604
606
  formatWaterByGroup: () => formatWaterByGroup,
605
607
  formatWaterVolumeM3: () => formatWaterVolumeM3,
608
+ getAuthCacheStats: () => getAuthCacheStats,
606
609
  getAvailableContexts: () => getAvailableContexts,
607
610
  getDateRangeArray: () => getDateRangeArray,
608
611
  getSaoPauloISOString: () => getSaoPauloISOString,
@@ -1371,6 +1374,135 @@ function buildListItemsThingsboardByUniqueDatasource(datasources, data) {
1371
1374
  return items;
1372
1375
  }
1373
1376
 
1377
+ // src/thingsboard/auth/buildMyioIngestionAuth.ts
1378
+ var globalCache = /* @__PURE__ */ new Map();
1379
+ function generateCacheKey(config) {
1380
+ return `${config.dataApiHost}:${config.clientId}:${config.clientSecret}`;
1381
+ }
1382
+ function buildMyioIngestionAuth(config) {
1383
+ const {
1384
+ dataApiHost,
1385
+ clientId,
1386
+ clientSecret,
1387
+ renewSkewSeconds = 60,
1388
+ retryBaseMs = 500,
1389
+ retryMaxAttempts = 3
1390
+ } = config;
1391
+ if (!dataApiHost || !clientId || !clientSecret) {
1392
+ throw new Error("dataApiHost, clientId, and clientSecret are required");
1393
+ }
1394
+ const authUrl = new URL(`${dataApiHost}/api/v1/auth`);
1395
+ const cacheKey = generateCacheKey(config);
1396
+ if (!globalCache.has(cacheKey)) {
1397
+ globalCache.set(cacheKey, {
1398
+ token: null,
1399
+ expiresAt: 0,
1400
+ inFlight: null
1401
+ });
1402
+ }
1403
+ const cache = globalCache.get(cacheKey);
1404
+ function now() {
1405
+ return Date.now();
1406
+ }
1407
+ function aboutToExpire() {
1408
+ if (!cache.token) return true;
1409
+ const skewMs = renewSkewSeconds * 1e3;
1410
+ return now() >= cache.expiresAt - skewMs;
1411
+ }
1412
+ async function sleep(ms) {
1413
+ return new Promise((resolve) => setTimeout(resolve, ms));
1414
+ }
1415
+ async function requestNewToken() {
1416
+ const body = {
1417
+ client_id: clientId,
1418
+ client_secret: clientSecret
1419
+ };
1420
+ let attempt = 0;
1421
+ while (true) {
1422
+ try {
1423
+ const response = await fetch(authUrl, {
1424
+ method: "POST",
1425
+ headers: {
1426
+ "Content-Type": "application/json"
1427
+ },
1428
+ body: JSON.stringify(body)
1429
+ });
1430
+ if (!response.ok) {
1431
+ const text = await response.text().catch(() => "");
1432
+ throw new Error(
1433
+ `Auth failed: HTTP ${response.status} ${response.statusText} ${text}`
1434
+ );
1435
+ }
1436
+ const json = await response.json();
1437
+ if (!json || !json.access_token || !json.expires_in) {
1438
+ throw new Error("Auth response missing required fields (access_token, expires_in)");
1439
+ }
1440
+ cache.token = json.access_token;
1441
+ cache.expiresAt = now() + Number(json.expires_in) * 1e3;
1442
+ console.log(
1443
+ `[MyIOAuth] New token obtained for ${clientId}. Expires in ~${Math.round(Number(json.expires_in) / 60)} min`
1444
+ );
1445
+ return cache.token;
1446
+ } catch (error) {
1447
+ attempt++;
1448
+ console.warn(
1449
+ `[MyIOAuth] Error obtaining token (attempt ${attempt}/${retryMaxAttempts}):`,
1450
+ error instanceof Error ? error.message : error
1451
+ );
1452
+ if (attempt >= retryMaxAttempts) {
1453
+ throw error;
1454
+ }
1455
+ const backoff = retryBaseMs * Math.pow(2, attempt - 1);
1456
+ await sleep(backoff);
1457
+ }
1458
+ }
1459
+ }
1460
+ async function getToken() {
1461
+ if (cache.inFlight) {
1462
+ return cache.inFlight;
1463
+ }
1464
+ if (aboutToExpire()) {
1465
+ cache.inFlight = requestNewToken().finally(() => {
1466
+ cache.inFlight = null;
1467
+ });
1468
+ return cache.inFlight;
1469
+ }
1470
+ return cache.token;
1471
+ }
1472
+ function getExpiryInfo() {
1473
+ return {
1474
+ expiresAt: cache.expiresAt,
1475
+ expiresInSeconds: Math.max(0, Math.floor((cache.expiresAt - now()) / 1e3))
1476
+ };
1477
+ }
1478
+ function clearCache() {
1479
+ cache.token = null;
1480
+ cache.expiresAt = 0;
1481
+ cache.inFlight = null;
1482
+ }
1483
+ function isTokenValid() {
1484
+ if (!globalCache.has(cacheKey)) {
1485
+ return false;
1486
+ }
1487
+ return !aboutToExpire();
1488
+ }
1489
+ return {
1490
+ getToken,
1491
+ getExpiryInfo,
1492
+ clearCache,
1493
+ isTokenValid
1494
+ };
1495
+ }
1496
+ function clearAllAuthCaches() {
1497
+ globalCache.clear();
1498
+ }
1499
+ function getAuthCacheStats() {
1500
+ return {
1501
+ totalCaches: globalCache.size,
1502
+ cacheKeys: Array.from(globalCache.keys())
1503
+ };
1504
+ }
1505
+
1374
1506
  // src/utils/deviceType.js
1375
1507
  function normalize(str) {
1376
1508
  if (typeof str !== "string") return "";
@@ -10051,12 +10183,14 @@ async function openDemandModal(params) {
10051
10183
  addNamespace,
10052
10184
  averageByDay,
10053
10185
  buildListItemsThingsboardByUniqueDatasource,
10186
+ buildMyioIngestionAuth,
10054
10187
  buildWaterReportCSV,
10055
10188
  buildWaterStoresCSV,
10056
10189
  calcDeltaPercent,
10057
10190
  classify,
10058
10191
  classifyWaterLabel,
10059
10192
  classifyWaterLabels,
10193
+ clearAllAuthCaches,
10060
10194
  createDateRangePicker,
10061
10195
  createInputDateRangePickerInsideDIV,
10062
10196
  decodePayload,
@@ -10078,6 +10212,7 @@ async function openDemandModal(params) {
10078
10212
  formatTankHeadFromCm,
10079
10213
  formatWaterByGroup,
10080
10214
  formatWaterVolumeM3,
10215
+ getAuthCacheStats,
10081
10216
  getAvailableContexts,
10082
10217
  getDateRangeArray,
10083
10218
  getSaoPauloISOString,
package/dist/index.d.cts CHANGED
@@ -345,6 +345,69 @@ interface ThingsBoardItem {
345
345
  */
346
346
  declare function buildListItemsThingsboardByUniqueDatasource(datasources: any[], data: any[]): ThingsBoardItem[];
347
347
 
348
+ /**
349
+ * MyIO Ingestion Authentication Component
350
+ *
351
+ * Factory function that creates authentication instances with shared token caching
352
+ * based on credentials. Multiple instances with the same credentials will share
353
+ * the same token cache for efficiency.
354
+ */
355
+ /**
356
+ * Authentication configuration parameters
357
+ */
358
+ interface MyIOAuthConfig {
359
+ dataApiHost: string;
360
+ clientId: string;
361
+ clientSecret: string;
362
+ renewSkewSeconds?: number;
363
+ retryBaseMs?: number;
364
+ retryMaxAttempts?: number;
365
+ }
366
+ /**
367
+ * Authentication instance interface
368
+ */
369
+ interface MyIOAuthInstance {
370
+ getToken(): Promise<string>;
371
+ getExpiryInfo(): {
372
+ expiresAt: number;
373
+ expiresInSeconds: number;
374
+ };
375
+ clearCache(): void;
376
+ isTokenValid(): boolean;
377
+ }
378
+ /**
379
+ * Create a MyIO Ingestion Authentication instance
380
+ *
381
+ * Multiple instances with the same credentials will share the same token cache,
382
+ * which is efficient and prevents unnecessary API calls.
383
+ *
384
+ * @param config Authentication configuration
385
+ * @returns Authentication instance
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * const auth = buildMyioIngestionAuth({
390
+ * dataApiHost: 'https://api.data.apps.myio-bas.com',
391
+ * clientId: 'your-client-id',
392
+ * clientSecret: 'your-client-secret'
393
+ * });
394
+ *
395
+ * const token = await auth.getToken();
396
+ * ```
397
+ */
398
+ declare function buildMyioIngestionAuth(config: MyIOAuthConfig): MyIOAuthInstance;
399
+ /**
400
+ * Clear all cached tokens (useful for testing or logout scenarios)
401
+ */
402
+ declare function clearAllAuthCaches(): void;
403
+ /**
404
+ * Get cache statistics (useful for debugging)
405
+ */
406
+ declare function getAuthCacheStats(): {
407
+ totalCaches: number;
408
+ cacheKeys: string[];
409
+ };
410
+
348
411
  /**
349
412
  * Detects the device type based on the given name and context.
350
413
  * Uses the specified detection context to identify device types.
@@ -1124,4 +1187,4 @@ interface DemandModalInstance {
1124
1187
  */
1125
1188
  declare function openDemandModal(params: DemandModalParams): Promise<DemandModalInstance>;
1126
1189
 
1127
- export { type CreateDateRangePickerOptions, type CreateInputDateRangePickerInsideDIVParams, type DateRangeControl, type DateRangeInputController, type DateRangeResult, type DemandModalInstance, type DemandModalParams, type DemandModalPdfConfig, type DemandModalStyles, MyIOChartModal, MyIODraggableCard, MyIOSelectionStore, MyIOSelectionStoreClass, type OpenDashboardPopupSettingsParams, type PersistResult, type SettingsError, type SettingsEvent, type StoreRow, type TbScope, type TelemetryFetcher, type TimedValue, type WaterRow, addDetectionContext, addNamespace, averageByDay, buildListItemsThingsboardByUniqueDatasource, buildWaterReportCSV, buildWaterStoresCSV, calcDeltaPercent, classify, classifyWaterLabel, classifyWaterLabels, createDateRangePicker, createInputDateRangePickerInsideDIV, decodePayload, decodePayloadBase64Xor, detectDeviceType, determineInterval, exportToCSV, exportToCSVAll, findValue, fmtPerc$1 as fmtPerc, fmtPerc as fmtPercLegacy, formatAllInSameUnit, formatAllInSameWaterUnit, formatDateForInput, formatDateToYMD, formatDateWithTimezoneOffset, formatEnergy, formatNumberReadable, formatTankHeadFromCm, formatWaterByGroup, formatWaterVolumeM3, getAvailableContexts, getDateRangeArray, getSaoPauloISOString, getSaoPauloISOStringFixed, getValueByDatakey, getValueByDatakeyLegacy, getWaterCategories, groupByDay, isWaterCategory, normalizeRecipients, numbers, openDashboardPopup, openDashboardPopupAllReport, openDashboardPopupEnergy, openDashboardPopupReport, openDashboardPopupSettings, openDemandModal, parseInputDateToDate, renderCardCompenteHeadOffice, renderCardComponent$1 as renderCardComponent, renderCardComponent as renderCardComponentEnhanced, renderCardComponentLegacy, renderCardComponentV2, strings, timeWindowFromInputYMD, toCSV, toFixedSafe };
1190
+ export { type CreateDateRangePickerOptions, type CreateInputDateRangePickerInsideDIVParams, type DateRangeControl, type DateRangeInputController, type DateRangeResult, type DemandModalInstance, type DemandModalParams, type DemandModalPdfConfig, type DemandModalStyles, type MyIOAuthConfig, type MyIOAuthInstance, MyIOChartModal, MyIODraggableCard, MyIOSelectionStore, MyIOSelectionStoreClass, type OpenDashboardPopupSettingsParams, type PersistResult, type SettingsError, type SettingsEvent, type StoreRow, type TbScope, type TelemetryFetcher, type TimedValue, type WaterRow, addDetectionContext, addNamespace, averageByDay, buildListItemsThingsboardByUniqueDatasource, buildMyioIngestionAuth, buildWaterReportCSV, buildWaterStoresCSV, calcDeltaPercent, classify, classifyWaterLabel, classifyWaterLabels, clearAllAuthCaches, createDateRangePicker, createInputDateRangePickerInsideDIV, decodePayload, decodePayloadBase64Xor, detectDeviceType, determineInterval, exportToCSV, exportToCSVAll, findValue, fmtPerc$1 as fmtPerc, fmtPerc as fmtPercLegacy, formatAllInSameUnit, formatAllInSameWaterUnit, formatDateForInput, formatDateToYMD, formatDateWithTimezoneOffset, formatEnergy, formatNumberReadable, formatTankHeadFromCm, formatWaterByGroup, formatWaterVolumeM3, getAuthCacheStats, getAvailableContexts, getDateRangeArray, getSaoPauloISOString, getSaoPauloISOStringFixed, getValueByDatakey, getValueByDatakeyLegacy, getWaterCategories, groupByDay, isWaterCategory, normalizeRecipients, numbers, openDashboardPopup, openDashboardPopupAllReport, openDashboardPopupEnergy, openDashboardPopupReport, openDashboardPopupSettings, openDemandModal, parseInputDateToDate, renderCardCompenteHeadOffice, renderCardComponent$1 as renderCardComponent, renderCardComponent as renderCardComponentEnhanced, renderCardComponentLegacy, renderCardComponentV2, strings, timeWindowFromInputYMD, toCSV, toFixedSafe };
package/dist/index.js CHANGED
@@ -1303,6 +1303,135 @@ function buildListItemsThingsboardByUniqueDatasource(datasources, data) {
1303
1303
  return items;
1304
1304
  }
1305
1305
 
1306
+ // src/thingsboard/auth/buildMyioIngestionAuth.ts
1307
+ var globalCache = /* @__PURE__ */ new Map();
1308
+ function generateCacheKey(config) {
1309
+ return `${config.dataApiHost}:${config.clientId}:${config.clientSecret}`;
1310
+ }
1311
+ function buildMyioIngestionAuth(config) {
1312
+ const {
1313
+ dataApiHost,
1314
+ clientId,
1315
+ clientSecret,
1316
+ renewSkewSeconds = 60,
1317
+ retryBaseMs = 500,
1318
+ retryMaxAttempts = 3
1319
+ } = config;
1320
+ if (!dataApiHost || !clientId || !clientSecret) {
1321
+ throw new Error("dataApiHost, clientId, and clientSecret are required");
1322
+ }
1323
+ const authUrl = new URL(`${dataApiHost}/api/v1/auth`);
1324
+ const cacheKey = generateCacheKey(config);
1325
+ if (!globalCache.has(cacheKey)) {
1326
+ globalCache.set(cacheKey, {
1327
+ token: null,
1328
+ expiresAt: 0,
1329
+ inFlight: null
1330
+ });
1331
+ }
1332
+ const cache = globalCache.get(cacheKey);
1333
+ function now() {
1334
+ return Date.now();
1335
+ }
1336
+ function aboutToExpire() {
1337
+ if (!cache.token) return true;
1338
+ const skewMs = renewSkewSeconds * 1e3;
1339
+ return now() >= cache.expiresAt - skewMs;
1340
+ }
1341
+ async function sleep(ms) {
1342
+ return new Promise((resolve) => setTimeout(resolve, ms));
1343
+ }
1344
+ async function requestNewToken() {
1345
+ const body = {
1346
+ client_id: clientId,
1347
+ client_secret: clientSecret
1348
+ };
1349
+ let attempt = 0;
1350
+ while (true) {
1351
+ try {
1352
+ const response = await fetch(authUrl, {
1353
+ method: "POST",
1354
+ headers: {
1355
+ "Content-Type": "application/json"
1356
+ },
1357
+ body: JSON.stringify(body)
1358
+ });
1359
+ if (!response.ok) {
1360
+ const text = await response.text().catch(() => "");
1361
+ throw new Error(
1362
+ `Auth failed: HTTP ${response.status} ${response.statusText} ${text}`
1363
+ );
1364
+ }
1365
+ const json = await response.json();
1366
+ if (!json || !json.access_token || !json.expires_in) {
1367
+ throw new Error("Auth response missing required fields (access_token, expires_in)");
1368
+ }
1369
+ cache.token = json.access_token;
1370
+ cache.expiresAt = now() + Number(json.expires_in) * 1e3;
1371
+ console.log(
1372
+ `[MyIOAuth] New token obtained for ${clientId}. Expires in ~${Math.round(Number(json.expires_in) / 60)} min`
1373
+ );
1374
+ return cache.token;
1375
+ } catch (error) {
1376
+ attempt++;
1377
+ console.warn(
1378
+ `[MyIOAuth] Error obtaining token (attempt ${attempt}/${retryMaxAttempts}):`,
1379
+ error instanceof Error ? error.message : error
1380
+ );
1381
+ if (attempt >= retryMaxAttempts) {
1382
+ throw error;
1383
+ }
1384
+ const backoff = retryBaseMs * Math.pow(2, attempt - 1);
1385
+ await sleep(backoff);
1386
+ }
1387
+ }
1388
+ }
1389
+ async function getToken() {
1390
+ if (cache.inFlight) {
1391
+ return cache.inFlight;
1392
+ }
1393
+ if (aboutToExpire()) {
1394
+ cache.inFlight = requestNewToken().finally(() => {
1395
+ cache.inFlight = null;
1396
+ });
1397
+ return cache.inFlight;
1398
+ }
1399
+ return cache.token;
1400
+ }
1401
+ function getExpiryInfo() {
1402
+ return {
1403
+ expiresAt: cache.expiresAt,
1404
+ expiresInSeconds: Math.max(0, Math.floor((cache.expiresAt - now()) / 1e3))
1405
+ };
1406
+ }
1407
+ function clearCache() {
1408
+ cache.token = null;
1409
+ cache.expiresAt = 0;
1410
+ cache.inFlight = null;
1411
+ }
1412
+ function isTokenValid() {
1413
+ if (!globalCache.has(cacheKey)) {
1414
+ return false;
1415
+ }
1416
+ return !aboutToExpire();
1417
+ }
1418
+ return {
1419
+ getToken,
1420
+ getExpiryInfo,
1421
+ clearCache,
1422
+ isTokenValid
1423
+ };
1424
+ }
1425
+ function clearAllAuthCaches() {
1426
+ globalCache.clear();
1427
+ }
1428
+ function getAuthCacheStats() {
1429
+ return {
1430
+ totalCaches: globalCache.size,
1431
+ cacheKeys: Array.from(globalCache.keys())
1432
+ };
1433
+ }
1434
+
1306
1435
  // src/utils/deviceType.js
1307
1436
  function normalize(str) {
1308
1437
  if (typeof str !== "string") return "";
@@ -9982,12 +10111,14 @@ export {
9982
10111
  addNamespace,
9983
10112
  averageByDay,
9984
10113
  buildListItemsThingsboardByUniqueDatasource,
10114
+ buildMyioIngestionAuth,
9985
10115
  buildWaterReportCSV,
9986
10116
  buildWaterStoresCSV,
9987
10117
  calcDeltaPercent,
9988
10118
  classify,
9989
10119
  classifyWaterLabel,
9990
10120
  classifyWaterLabels,
10121
+ clearAllAuthCaches,
9991
10122
  createDateRangePicker2 as createDateRangePicker,
9992
10123
  createInputDateRangePickerInsideDIV,
9993
10124
  decodePayload,
@@ -10009,6 +10140,7 @@ export {
10009
10140
  formatTankHeadFromCm,
10010
10141
  formatWaterByGroup,
10011
10142
  formatWaterVolumeM3,
10143
+ getAuthCacheStats,
10012
10144
  getAvailableContexts,
10013
10145
  getDateRangeArray,
10014
10146
  getSaoPauloISOString,
@@ -1309,6 +1309,135 @@
1309
1309
  return items;
1310
1310
  }
1311
1311
 
1312
+ // src/thingsboard/auth/buildMyioIngestionAuth.ts
1313
+ var globalCache = /* @__PURE__ */ new Map();
1314
+ function generateCacheKey(config) {
1315
+ return `${config.dataApiHost}:${config.clientId}:${config.clientSecret}`;
1316
+ }
1317
+ function buildMyioIngestionAuth(config) {
1318
+ const {
1319
+ dataApiHost,
1320
+ clientId,
1321
+ clientSecret,
1322
+ renewSkewSeconds = 60,
1323
+ retryBaseMs = 500,
1324
+ retryMaxAttempts = 3
1325
+ } = config;
1326
+ if (!dataApiHost || !clientId || !clientSecret) {
1327
+ throw new Error("dataApiHost, clientId, and clientSecret are required");
1328
+ }
1329
+ const authUrl = new URL(`${dataApiHost}/api/v1/auth`);
1330
+ const cacheKey = generateCacheKey(config);
1331
+ if (!globalCache.has(cacheKey)) {
1332
+ globalCache.set(cacheKey, {
1333
+ token: null,
1334
+ expiresAt: 0,
1335
+ inFlight: null
1336
+ });
1337
+ }
1338
+ const cache = globalCache.get(cacheKey);
1339
+ function now() {
1340
+ return Date.now();
1341
+ }
1342
+ function aboutToExpire() {
1343
+ if (!cache.token) return true;
1344
+ const skewMs = renewSkewSeconds * 1e3;
1345
+ return now() >= cache.expiresAt - skewMs;
1346
+ }
1347
+ async function sleep(ms) {
1348
+ return new Promise((resolve) => setTimeout(resolve, ms));
1349
+ }
1350
+ async function requestNewToken() {
1351
+ const body = {
1352
+ client_id: clientId,
1353
+ client_secret: clientSecret
1354
+ };
1355
+ let attempt = 0;
1356
+ while (true) {
1357
+ try {
1358
+ const response = await fetch(authUrl, {
1359
+ method: "POST",
1360
+ headers: {
1361
+ "Content-Type": "application/json"
1362
+ },
1363
+ body: JSON.stringify(body)
1364
+ });
1365
+ if (!response.ok) {
1366
+ const text = await response.text().catch(() => "");
1367
+ throw new Error(
1368
+ `Auth failed: HTTP ${response.status} ${response.statusText} ${text}`
1369
+ );
1370
+ }
1371
+ const json = await response.json();
1372
+ if (!json || !json.access_token || !json.expires_in) {
1373
+ throw new Error("Auth response missing required fields (access_token, expires_in)");
1374
+ }
1375
+ cache.token = json.access_token;
1376
+ cache.expiresAt = now() + Number(json.expires_in) * 1e3;
1377
+ console.log(
1378
+ `[MyIOAuth] New token obtained for ${clientId}. Expires in ~${Math.round(Number(json.expires_in) / 60)} min`
1379
+ );
1380
+ return cache.token;
1381
+ } catch (error) {
1382
+ attempt++;
1383
+ console.warn(
1384
+ `[MyIOAuth] Error obtaining token (attempt ${attempt}/${retryMaxAttempts}):`,
1385
+ error instanceof Error ? error.message : error
1386
+ );
1387
+ if (attempt >= retryMaxAttempts) {
1388
+ throw error;
1389
+ }
1390
+ const backoff = retryBaseMs * Math.pow(2, attempt - 1);
1391
+ await sleep(backoff);
1392
+ }
1393
+ }
1394
+ }
1395
+ async function getToken() {
1396
+ if (cache.inFlight) {
1397
+ return cache.inFlight;
1398
+ }
1399
+ if (aboutToExpire()) {
1400
+ cache.inFlight = requestNewToken().finally(() => {
1401
+ cache.inFlight = null;
1402
+ });
1403
+ return cache.inFlight;
1404
+ }
1405
+ return cache.token;
1406
+ }
1407
+ function getExpiryInfo() {
1408
+ return {
1409
+ expiresAt: cache.expiresAt,
1410
+ expiresInSeconds: Math.max(0, Math.floor((cache.expiresAt - now()) / 1e3))
1411
+ };
1412
+ }
1413
+ function clearCache() {
1414
+ cache.token = null;
1415
+ cache.expiresAt = 0;
1416
+ cache.inFlight = null;
1417
+ }
1418
+ function isTokenValid() {
1419
+ if (!globalCache.has(cacheKey)) {
1420
+ return false;
1421
+ }
1422
+ return !aboutToExpire();
1423
+ }
1424
+ return {
1425
+ getToken,
1426
+ getExpiryInfo,
1427
+ clearCache,
1428
+ isTokenValid
1429
+ };
1430
+ }
1431
+ function clearAllAuthCaches() {
1432
+ globalCache.clear();
1433
+ }
1434
+ function getAuthCacheStats() {
1435
+ return {
1436
+ totalCaches: globalCache.size,
1437
+ cacheKeys: Array.from(globalCache.keys())
1438
+ };
1439
+ }
1440
+
1312
1441
  // src/utils/deviceType.js
1313
1442
  function normalize(str) {
1314
1443
  if (typeof str !== "string") return "";
@@ -9971,12 +10100,14 @@
9971
10100
  exports.addNamespace = addNamespace;
9972
10101
  exports.averageByDay = averageByDay;
9973
10102
  exports.buildListItemsThingsboardByUniqueDatasource = buildListItemsThingsboardByUniqueDatasource;
10103
+ exports.buildMyioIngestionAuth = buildMyioIngestionAuth;
9974
10104
  exports.buildWaterReportCSV = buildWaterReportCSV;
9975
10105
  exports.buildWaterStoresCSV = buildWaterStoresCSV;
9976
10106
  exports.calcDeltaPercent = calcDeltaPercent;
9977
10107
  exports.classify = classify;
9978
10108
  exports.classifyWaterLabel = classifyWaterLabel;
9979
10109
  exports.classifyWaterLabels = classifyWaterLabels;
10110
+ exports.clearAllAuthCaches = clearAllAuthCaches;
9980
10111
  exports.createDateRangePicker = createDateRangePicker2;
9981
10112
  exports.createInputDateRangePickerInsideDIV = createInputDateRangePickerInsideDIV;
9982
10113
  exports.decodePayload = decodePayload;
@@ -9998,6 +10129,7 @@
9998
10129
  exports.formatTankHeadFromCm = formatTankHeadFromCm;
9999
10130
  exports.formatWaterByGroup = formatWaterByGroup;
10000
10131
  exports.formatWaterVolumeM3 = formatWaterVolumeM3;
10132
+ exports.getAuthCacheStats = getAuthCacheStats;
10001
10133
  exports.getAvailableContexts = getAvailableContexts;
10002
10134
  exports.getDateRangeArray = getDateRangeArray;
10003
10135
  exports.getSaoPauloISOString = getSaoPauloISOString;