diff --git a/changelog.md b/changelog.md index 05750e6..8968a87 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog +## 2026-03-27 - 11.12.0 - feat(web-ui) +pause dashboard polling, sockets, and chart updates when the tab is hidden + +- replace interval-based auto-refresh with scheduled actions using visibility-aware auto-pause +- disconnect and reconnect the TypedSocket on tab visibility changes to avoid background log buildup +- batch pushed log entries per animation frame and add an in-flight refresh guard to reduce unnecessary re-renders and overlapping requests +- update state subscriptions to use select() and document the new tab visibility optimization behavior +- bump smartdb, smartproxy, smartstate, remoteingress, dees-element, and tstest dependencies + ## 2026-03-26 - 11.11.0 - feat(docker,cache,proxy) improve container runtime defaults and add configurable connection limits diff --git a/package.json b/package.json index 57968ff..7c700ca 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@git.zone/tsbuild": "^4.4.0", "@git.zone/tsbundle": "^2.10.0", "@git.zone/tsrun": "^2.0.2", - "@git.zone/tstest": "^3.6.1", + "@git.zone/tstest": "^3.6.3", "@git.zone/tswatch": "^3.3.2", "@types/node": "^25.5.0" }, @@ -36,13 +36,13 @@ "@api.global/typedsocket": "^4.1.2", "@apiclient.xyz/cloudflare": "^7.1.0", "@design.estate/dees-catalog": "^3.49.0", - "@design.estate/dees-element": "^2.2.3", + "@design.estate/dees-element": "^2.2.4", "@push.rocks/lik": "^6.4.0", "@push.rocks/projectinfo": "^5.1.0", "@push.rocks/qenv": "^6.1.3", "@push.rocks/smartacme": "^9.3.0", "@push.rocks/smartdata": "^7.1.3", - "@push.rocks/smartdb": "^1.0.1", + "@push.rocks/smartdb": "^2.0.0", "@push.rocks/smartdns": "^7.9.0", "@push.rocks/smartfs": "^1.5.0", "@push.rocks/smartguard": "^3.1.0", @@ -53,16 +53,16 @@ "@push.rocks/smartnetwork": "^4.5.2", "@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpromise": "^4.2.3", - "@push.rocks/smartproxy": "^26.3.0", + "@push.rocks/smartproxy": "^27.0.0", "@push.rocks/smartradius": "^1.1.1", "@push.rocks/smartrequest": "^5.0.1", "@push.rocks/smartrx": "^3.0.10", - "@push.rocks/smartstate": "^2.2.1", + "@push.rocks/smartstate": "^2.3.0", "@push.rocks/smartunique": "^3.0.9", "@push.rocks/taskbuffer": "^8.0.2", "@serve.zone/catalog": "^2.9.0", "@serve.zone/interfaces": "^5.3.0", - "@serve.zone/remoteingress": "^4.14.3", + "@serve.zone/remoteingress": "^4.15.3", "@tsclass/tsclass": "^9.5.0", "lru-cache": "^11.2.7", "uuid": "^13.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bf43ce..8facc27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^3.49.0 version: 3.49.0(@tiptap/pm@2.27.2) '@design.estate/dees-element': - specifier: ^2.2.3 - version: 2.2.3 + specifier: ^2.2.4 + version: 2.2.4 '@push.rocks/lik': specifier: ^6.4.0 version: 6.4.0 @@ -45,8 +45,8 @@ importers: specifier: ^7.1.3 version: 7.1.3(socks@2.8.7) '@push.rocks/smartdb': - specifier: ^1.0.1 - version: 1.0.1 + specifier: ^2.0.0 + version: 2.0.0 '@push.rocks/smartdns': specifier: ^7.9.0 version: 7.9.0 @@ -78,8 +78,8 @@ importers: specifier: ^4.2.3 version: 4.2.3 '@push.rocks/smartproxy': - specifier: ^26.3.0 - version: 26.3.0 + specifier: ^27.0.0 + version: 27.0.0 '@push.rocks/smartradius': specifier: ^1.1.1 version: 1.1.1 @@ -90,8 +90,8 @@ importers: specifier: ^3.0.10 version: 3.0.10 '@push.rocks/smartstate': - specifier: ^2.2.1 - version: 2.2.1 + specifier: ^2.3.0 + version: 2.3.0 '@push.rocks/smartunique': specifier: ^3.0.9 version: 3.0.9 @@ -105,8 +105,8 @@ importers: specifier: ^5.3.0 version: 5.3.0 '@serve.zone/remoteingress': - specifier: ^4.14.3 - version: 4.14.3 + specifier: ^4.15.3 + version: 4.15.3 '@tsclass/tsclass': specifier: ^9.5.0 version: 9.5.0 @@ -127,8 +127,8 @@ importers: specifier: ^2.0.2 version: 2.0.2 '@git.zone/tstest': - specifier: ^3.6.1 - version: 3.6.1(socks@2.8.7)(typescript@6.0.2) + specifier: ^3.6.3 + version: 3.6.3(socks@2.8.7)(typescript@6.0.2) '@git.zone/tswatch': specifier: ^3.3.2 version: 3.3.2(@tiptap/pm@2.27.2) @@ -181,48 +181,48 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.1016.0': - resolution: {integrity: sha512-E9umet1PolP6I8TpjQQ2W88aIIguyiRQJE98ag6N6QeLgjSZsF+h9l3KclwCRvqUFU68x+HRwrgXxvbIBVFLbA==} + '@aws-sdk/client-s3@3.1018.0': + resolution: {integrity: sha512-BiGKMjrkAJkyse1ECpVyxVYugf82FB3cM9zgKpx3boFuWobyolG5ri6XjoMIY8fpHddaO8ZClXEedACyelSLWA==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.24': - resolution: {integrity: sha512-vvf82RYQu2GidWAuQq+uIzaPz9V0gSCXVqdVzRosgl5rXcspXOpSD3wFreGGW6AYymPr97Z69kjVnLePBxloDw==} + '@aws-sdk/core@3.973.25': + resolution: {integrity: sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ==} engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.5': resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.22': - resolution: {integrity: sha512-cXp0VTDWT76p3hyK5D51yIKEfpf6/zsUvMfaB8CkyqadJxMQ8SbEeVroregmDlZbtG31wkj9ei0WnftmieggLg==} + '@aws-sdk/credential-provider-env@3.972.23': + resolution: {integrity: sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.24': - resolution: {integrity: sha512-h694K7+tRuepSRJr09wTvQfaEnjzsKZ5s7fbESrVds02GT/QzViJ94/HCNwM7bUfFxqpPXHxulZfL6Cou0dwPg==} + '@aws-sdk/credential-provider-http@3.972.25': + resolution: {integrity: sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.24': - resolution: {integrity: sha512-O46fFmv0RDFWiWEA9/e6oW92BnsyAXuEgTTasxHligjn2RCr9L/DK773m/NoFaL3ZdNAUz8WxgxunleMnHAkeQ==} + '@aws-sdk/credential-provider-ini@3.972.25': + resolution: {integrity: sha512-G/v/PicYn4qs7xCv4vT6I4QKdvMyRvsgIFNBkUueCGlbLo7/PuKcNKgUozmLSsaYnE7jIl6UrfkP07EUubr48w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.24': - resolution: {integrity: sha512-sIk8oa6AzDoUhxsR11svZESqvzGuXesw62Rl2oW6wguZx8i9cdGCvkFg+h5K7iucUZP8wyWibUbJMc+J66cu5g==} + '@aws-sdk/credential-provider-login@3.972.25': + resolution: {integrity: sha512-bUdmyJeVua7SmD+g2a65x2/0YqsGn4K2k4GawI43js0odaNaIzpIhLtHehUnPnfLuyhPWbJR1NyuIO4iMVfM0w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.25': - resolution: {integrity: sha512-m7dR0Dsva2P+VUpL+VkC0WwiDby5pgmWXkRVDB5rlwv0jXJrQJf7YMtCoM8Wjk0H9jPeCYOxOXXcIgp/qp5Alg==} + '@aws-sdk/credential-provider-node@3.972.26': + resolution: {integrity: sha512-5XSK74rCXxCNj+UWv5bjq1EccYkiyW4XOHFU9NXnsCcQF8dJuHdua1qFg0m/LIwVOWklbKsrcnMtfxIXwgvwzQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.22': - resolution: {integrity: sha512-Os32s8/4gTZjBk5BtoS/cuTILaj+K72d0dVG7TCJX/fC4598cxwLDmf1AEHEpER5oL3K//yETjvFaz0V8oO5Xw==} + '@aws-sdk/credential-provider-process@3.972.23': + resolution: {integrity: sha512-IL/TFW59++b7MpHserjUblGrdP5UXy5Ekqqx1XQkERXBFJcZr74I7VaSrQT5dxdRMU16xGK4L0RQ5fQG1pMgnA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.24': - resolution: {integrity: sha512-PaFv7snEfypU2yXkpvfyWgddEbDLtgVe51wdZlinhc2doubBjUzJZZpgwuF2Jenl1FBydMhNpMjD6SBUM3qdSA==} + '@aws-sdk/credential-provider-sso@3.972.25': + resolution: {integrity: sha512-r4OGAfHmlEa1QBInHWz+/dOD4tRljcjVNQe9wJ/AJNXEj1d2WdsRLppvRFImRV6FIs+bTpjtL0a23V5ELQpRPw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.24': - resolution: {integrity: sha512-J6H4R1nvr3uBTqD/EeIPAskrBtET4WFfNhpFySr2xW7bVZOXpQfPjrLSIx65jcNjBmLXzWq8QFLdVoGxiGG/SA==} + '@aws-sdk/credential-provider-web-identity@3.972.25': + resolution: {integrity: sha512-uM1OtoJgj+yK3MlAmda8uR9WJJCdm5HB25JyCeFL5a5q1Fbafalf4uKidFO3/L0Pgd+Fsflkb4cM6jHIswi3QQ==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-bucket-endpoint@3.972.8': @@ -233,8 +233,8 @@ packages: resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.4': - resolution: {integrity: sha512-fhCbZXPAyy8btnNbnBlR7Cc1nD54cETSvGn2wey71ehsM89AKPO8Dpco9DBAAgvrUdLrdHQepBXcyX4vxC5OwA==} + '@aws-sdk/middleware-flexible-checksums@3.974.5': + resolution: {integrity: sha512-SPSvF0G1t8m8CcB0L+ClNFszzQOvXaxmRj25oRWDf6aU+TuN2PXPFAJ9A6lt1IvX4oGAqqbTdMPTYs/SSHUYYQ==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.8': @@ -249,36 +249,36 @@ packages: resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.972.8': - resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} + '@aws-sdk/middleware-recursion-detection@3.972.9': + resolution: {integrity: sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.24': - resolution: {integrity: sha512-4sXxVC/enYgMkZefNMOzU6C6KtAXEvwVJLgNcUx1dvROH6GvKB5Sm2RGnGzTp0/PwkibIyMw4kOzF8tbLfaBAQ==} + '@aws-sdk/middleware-sdk-s3@3.972.26': + resolution: {integrity: sha512-5q7UGSTtt7/KF0Os8wj2VZtlLxeWJVb0e2eDrDJlWot2EIxUNKDDMPFq/FowUqrwZ40rO2bu6BypxaKNvQhI+g==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.8': resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.25': - resolution: {integrity: sha512-QxiMPofvOt8SwSynTOmuZfvvPM1S9QfkESBxB22NMHTRXCJhR5BygLl8IXfC4jELiisQgwsgUby21GtXfX3f/g==} + '@aws-sdk/middleware-user-agent@3.972.26': + resolution: {integrity: sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.14': - resolution: {integrity: sha512-fSESKvh1VbfjtV3QMnRkCPZWkUbQof6T/DOpiLp33yP2wA+rbwwnZeG3XT3Ekljgw2I8X4XaQPnw+zSR8yxJ5Q==} + '@aws-sdk/nested-clients@3.996.15': + resolution: {integrity: sha512-k6WAVNkub5DrU46iPQvH1m0xc1n+0dX79+i287tYJzf5g1yU2rX3uf4xNeL5JvK1NtYgfwMnsxHqhOXFBn367A==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.9': - resolution: {integrity: sha512-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng==} + '@aws-sdk/region-config-resolver@3.972.10': + resolution: {integrity: sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.12': - resolution: {integrity: sha512-abRObSqjVeKUUHIZfAp78PTYrEsxCgVKDs/YET357pzT5C02eDDEvmWyeEC2wglWcYC4UTbBFk22gd2YJUlCQg==} + '@aws-sdk/signature-v4-multi-region@3.996.14': + resolution: {integrity: sha512-4nZSrBr1NO+48HCM/6BRU8mnRjuHZjcpziCvLXZk5QVftwWz5Mxqbhwdz4xf7WW88buaTB8uRO2MHklSX1m0vg==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1015.0': - resolution: {integrity: sha512-3OSD4y110nisRhHzFOjoEeHU4GQL4KpzkX9PxzWaiZe0Yg2+thZKM0Pn9DjYwezH5JYfh/K++xK/SE0IHGrmCQ==} + '@aws-sdk/token-providers@3.1018.0': + resolution: {integrity: sha512-97OPNJHy37wmGOX44xAcu6E9oSTiqK9uPcy/fWpmN5uB3JuEp1f6x60Xot/jp+FxwhQWIFUsVJFnm3QKqt7T6Q==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.6': @@ -300,8 +300,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.8': resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.973.11': - resolution: {integrity: sha512-1qdXbXo2s5MMLpUvw00284LsbhtlQ4ul7Zzdn5n+7p4WVgCMLqhxImpHIrjSoc72E/fyc4Wq8dLtUld2Gsh+lA==} + '@aws-sdk/util-user-agent-node@3.973.12': + resolution: {integrity: sha512-8phW0TS8ntENJgDcFewYT/Q8dOmarpvSxEjATu2GUBAutiHr++oEGCiBUwxslCMNvwW2cAPZNT53S/ym8zm/gg==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -309,8 +309,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.15': - resolution: {integrity: sha512-PxMRlCFNiQnke9YR29vjFQwz4jq+6Q04rOVFeTDR2K7Qpv9h9FOWOxG+zJjageimYbWqE3bTuLjmryWHAWbvaA==} + '@aws-sdk/xml-builder@3.972.16': + resolution: {integrity: sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.4': @@ -347,11 +347,11 @@ packages: '@design.estate/dees-comms@1.0.30': resolution: {integrity: sha512-KchMlklJfKAjQiJiR0xmofXtQ27VgZtBIxcMwPE9d+h3jJRv+lPZxzBQVOM0eyM0uS44S5vJMZ11IeV4uDXSHg==} - '@design.estate/dees-domtools@2.5.1': - resolution: {integrity: sha512-ojzRSkOpQvxpd4drCNF1wadvPwthI6xIJpYjBbOwlgxkFCrlgxlOxHzRKEVnj5wWeUPqykKhddKp33LKW9mydw==} + '@design.estate/dees-domtools@2.5.3': + resolution: {integrity: sha512-E30vu4Cl49nSQAFlazT2Eo9VVR3VG3RGc2NLmVe7i8NMC/Sm2HQisXlpKMZYBOoY8YwdG8W2MiXaD0lbGyibCw==} - '@design.estate/dees-element@2.2.3': - resolution: {integrity: sha512-MpAvJPrJDTDad8hUtdOzMgMFRE7n84O7INhvSlkTTLB3b84j8EKjwfUCMErGAo7Bq5zfw4LG7NnKhLYXXXjkXA==} + '@design.estate/dees-element@2.2.4': + resolution: {integrity: sha512-O9cA6flBMMd+pBwMQrZXwAWel9yVxgokolb+Em6gvkXxPJ0P/B5UDn4Vc2d4ts3ta55PTBm+l2dPeDVGx/bl7Q==} '@design.estate/dees-wcctools@3.8.0': resolution: {integrity: sha512-CC14iVKUrguzD9jIrdPBd9fZ4egVJEZMxl5y8iy0l7WLumeoYvGsoXj5INVkRPLRVLqziIdi4Je1hXqHt2NU+g==} @@ -557,8 +557,8 @@ packages: resolution: {integrity: sha512-Rnp/wYHzI8A1pVBKOOePRJgQiBZdW+GEjpQk2uhvXz6A+ljUV2SXKc7NpQVVDsjEZaNFeAI9jMYOdk3lm3yMDA==} hasBin: true - '@git.zone/tstest@3.6.1': - resolution: {integrity: sha512-htFCPxxtER26/L9NiF1oASp5vVZ1jZnFoV9NyysU3kY3VW7wGbvPRKxScf6NR9yjeSL9AuWerFKIYJ50lkbAQg==} + '@git.zone/tstest@3.6.3': + resolution: {integrity: sha512-xvHZia3VEYO3ztXNqN3+ytF07vA2imZJwpD7GOv/wVM/hY58bUVvs/YnJ0C4nwiCGiHV87LU27NDEGfoNPYZcw==} hasBin: true '@git.zone/tswatch@3.3.2': @@ -1125,8 +1125,8 @@ packages: '@push.rocks/smartdata@7.1.3': resolution: {integrity: sha512-7vQJ9pdRk450yn2m9tmGPdSRlQVmxFPZjHD4sGYsfqCQPg+GLFusu+H16zpf+jKzAq4F2ZBMPaYymJHXvXiVcw==} - '@push.rocks/smartdb@1.0.1': - resolution: {integrity: sha512-m79Gzn7rHBMRYDohy1Mwm3niyRrre0g1ev3NxrIO6dnjo584pa3b0vTKwN+8R+tURJPppiMWfkPscpeKtrwIIg==} + '@push.rocks/smartdb@2.0.0': + resolution: {integrity: sha512-RGaXGOS+5c7Hru2XwoyavQuoZqrfIzUfF/AnnVA0GYOrj4P2S89fngp8QDczVyZq/IbkByYXz59foQmN/WDlWA==} '@push.rocks/smartdelay@3.0.5': resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==} @@ -1260,8 +1260,8 @@ packages: '@push.rocks/smartpromise@4.2.3': resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==} - '@push.rocks/smartproxy@26.3.0': - resolution: {integrity: sha512-pdq2l/2vfaZGpNKDo/fG6+9qTcGv3pWzkYcBo3rSHhQvRLuYpYeFDxTKp7+cQpn0j+H2/X5SlECQtTQNnkF4qw==} + '@push.rocks/smartproxy@27.0.0': + resolution: {integrity: sha512-1scXCoXUM0Ify81une5LldTfbKaBFN8aa5xTiFg2PAS6R4QoGsYuj/aCmErVwBDzCF4G+je4Lh0wxLkMKy7QBA==} '@push.rocks/smartpuppeteer@2.0.5': resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==} @@ -1299,8 +1299,8 @@ packages: '@push.rocks/smartspawn@3.0.3': resolution: {integrity: sha512-DyrGPV69wwOiJgKkyruk5hS3UEGZ99xFAqBE9O2nM8VXCRLbbty3xt1Ug5Z092ZZmJYaaGMSnMw3ijyZJFCT0Q==} - '@push.rocks/smartstate@2.2.1': - resolution: {integrity: sha512-fLrilAJNI6QOs0hcBRD9eTwU2Rlo6NlDCKQo9N/zyp0VJ6AV1UVdEZcVIQILu1CO0RUHX9aBAbFunJrb2+Zrkg==} + '@push.rocks/smartstate@2.3.0': + resolution: {integrity: sha512-NfyQYlhYJdVS5XvXEREB7NixaOv0CFuBDMImjN9JtF3dToJvjciBTMhKMGOJ6IKpza39y2zrH+gABapYNfzZ3A==} '@push.rocks/smartstorage@6.3.2': resolution: {integrity: sha512-g8rXlVZ+6iKmzNoybtwQntdb7EWA6WnVmbXNOdwDKWR8w4o/7UMErj+H5mt57iqYIy1pzQAoTb8IWJNsti7XQw==} @@ -1570,8 +1570,8 @@ packages: '@serve.zone/interfaces@5.3.0': resolution: {integrity: sha512-venO7wtDR9ixzD9NhdERBGjNKbFA5LL0yHw4eqGh0UpmvtXVc3SFG0uuHDilOKMZqZ8bttV88qVsFy1aSTJrtA==} - '@serve.zone/remoteingress@4.14.3': - resolution: {integrity: sha512-Ob2NPA0jjp0V88Aj5bwBF2vjwxq9rw7XBYbV1EiVSOBpVhKbb19O01jAD0A61cEqeXsiTONsX3SnMfLi7Laj3Q==} + '@serve.zone/remoteingress@4.15.3': + resolution: {integrity: sha512-kg/bmR+qcFRFuigTDr5Fao72cb7m/mSkI5APm7KZDKSUYTFuytNoj6KCIE0ICkc3Nh34y8oDwFJsS6oFo64AyQ==} '@sindresorhus/is@5.6.0': resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} @@ -1971,8 +1971,8 @@ packages: '@types/clean-css@4.2.11': resolution: {integrity: sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/fs-extra@11.0.4': resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} @@ -2201,12 +2201,15 @@ packages: bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - bare-stream@2.10.0: - resolution: {integrity: sha512-DOPZF/DDcDruKDA43cOw6e9Quq5daua7ygcAwJE/pKJsRWhgSSemi7qVNGE5kyDIxIeN1533G/zfbvWX7Wcb9w==} + bare-stream@2.11.0: + resolution: {integrity: sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==} peerDependencies: + bare-abort-controller: '*' bare-buffer: '*' bare-events: '*' peerDependenciesMeta: + bare-abort-controller: + optional: true bare-buffer: optional: true bare-events: @@ -2228,14 +2231,14 @@ packages: bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.0.3: + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} - brace-expansion@5.0.4: - resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} broadcast-channel@7.3.0: @@ -2609,6 +2612,10 @@ packages: resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} hasBin: true + fast-xml-parser@5.5.9: + resolution: {integrity: sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==} + hasBin: true + fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -2749,8 +2756,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} hasBin: true @@ -3049,8 +3056,8 @@ packages: lucide@0.577.0: resolution: {integrity: sha512-PpC/m5eOItp/WU/GlQPFBXDOhq6HibL73KzYP37OX3LM7VmzWQF8voEj8QRWUFvy9FIKfeDQkWYoyS1D/MdWFA==} - mailparser@3.9.4: - resolution: {integrity: sha512-ZmCnrMnRod+Cq6h7afn9DMFlT1B5gf484Ji+55NhUR4+w4q9z9d7lHHvL8pXP6kp/ehr8pGLQZBmjHpjvItuTQ==} + mailparser@3.9.6: + resolution: {integrity: sha512-EJYTDWMrOS1kddK1mTsRkrx2Ngh2nYsg54SRMWVVWGVEGbHH4tod8tqqU9hIRPgGQVboSjFubDn9cboSitbM3Q==} make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} @@ -3386,12 +3393,12 @@ packages: encoding: optional: true - node-forge@1.3.3: - resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} - nodemailer@8.0.2: - resolution: {integrity: sha512-zbj002pZAIkWQFxyAaqoxvn+zoIwRnS40hgjqTXudKOOJkiFFgBeNqjgD3/YCR12sZnrghWYBY+yP1ZucdDRpw==} + nodemailer@8.0.4: + resolution: {integrity: sha512-k+jf6N8PfQJ0Fe8ZhJlgqU5qJU44Lpvp2yvidH3vp1lPnVQMgi4yEEMPXg5eJS1gFIJTVq1NHBk7Ia9ARdSBdQ==} engines: {node: '>=6.0.0'} normalize-newline@4.1.0: @@ -3527,8 +3534,8 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-to-regexp@8.4.0: + resolution: {integrity: sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==} pdf-lib@1.17.1: resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==} @@ -3560,8 +3567,8 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pixelmatch@5.3.0: @@ -4037,8 +4044,8 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type-fest@5.4.4: - resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} + type-fest@5.5.0: + resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} engines: {node: '>=20'} typed-query-selector@2.12.1: @@ -4379,29 +4386,29 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1016.0': + '@aws-sdk/client-s3@3.1018.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.24 - '@aws-sdk/credential-provider-node': 3.972.25 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/credential-provider-node': 3.972.26 '@aws-sdk/middleware-bucket-endpoint': 3.972.8 '@aws-sdk/middleware-expect-continue': 3.972.8 - '@aws-sdk/middleware-flexible-checksums': 3.974.4 + '@aws-sdk/middleware-flexible-checksums': 3.974.5 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-location-constraint': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 - '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-sdk-s3': 3.972.24 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-sdk-s3': 3.972.26 '@aws-sdk/middleware-ssec': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.25 - '@aws-sdk/region-config-resolver': 3.972.9 - '@aws-sdk/signature-v4-multi-region': 3.996.12 + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/region-config-resolver': 3.972.10 + '@aws-sdk/signature-v4-multi-region': 3.996.14 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.11 + '@aws-sdk/util-user-agent-node': 3.973.12 '@smithy/config-resolver': 4.4.13 '@smithy/core': 3.23.12 '@smithy/eventstream-serde-browser': 4.2.12 @@ -4439,10 +4446,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.24': + '@aws-sdk/core@3.973.25': dependencies: '@aws-sdk/types': 3.973.6 - '@aws-sdk/xml-builder': 3.972.15 + '@aws-sdk/xml-builder': 3.972.16 '@smithy/core': 3.23.12 '@smithy/node-config-provider': 4.3.12 '@smithy/property-provider': 4.2.12 @@ -4460,17 +4467,17 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.22': + '@aws-sdk/credential-provider-env@3.972.23': dependencies: - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.24': + '@aws-sdk/credential-provider-http@3.972.25': dependencies: - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/types': 3.973.6 '@smithy/fetch-http-handler': 5.3.15 '@smithy/node-http-handler': 4.5.0 @@ -4481,16 +4488,16 @@ snapshots: '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.24': + '@aws-sdk/credential-provider-ini@3.972.25': dependencies: - '@aws-sdk/core': 3.973.24 - '@aws-sdk/credential-provider-env': 3.972.22 - '@aws-sdk/credential-provider-http': 3.972.24 - '@aws-sdk/credential-provider-login': 3.972.24 - '@aws-sdk/credential-provider-process': 3.972.22 - '@aws-sdk/credential-provider-sso': 3.972.24 - '@aws-sdk/credential-provider-web-identity': 3.972.24 - '@aws-sdk/nested-clients': 3.996.14 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/credential-provider-env': 3.972.23 + '@aws-sdk/credential-provider-http': 3.972.25 + '@aws-sdk/credential-provider-login': 3.972.25 + '@aws-sdk/credential-provider-process': 3.972.23 + '@aws-sdk/credential-provider-sso': 3.972.25 + '@aws-sdk/credential-provider-web-identity': 3.972.25 + '@aws-sdk/nested-clients': 3.996.15 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -4500,10 +4507,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.24': + '@aws-sdk/credential-provider-login@3.972.25': dependencies: - '@aws-sdk/core': 3.973.24 - '@aws-sdk/nested-clients': 3.996.14 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.15 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/protocol-http': 5.3.12 @@ -4513,14 +4520,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.25': + '@aws-sdk/credential-provider-node@3.972.26': dependencies: - '@aws-sdk/credential-provider-env': 3.972.22 - '@aws-sdk/credential-provider-http': 3.972.24 - '@aws-sdk/credential-provider-ini': 3.972.24 - '@aws-sdk/credential-provider-process': 3.972.22 - '@aws-sdk/credential-provider-sso': 3.972.24 - '@aws-sdk/credential-provider-web-identity': 3.972.24 + '@aws-sdk/credential-provider-env': 3.972.23 + '@aws-sdk/credential-provider-http': 3.972.25 + '@aws-sdk/credential-provider-ini': 3.972.25 + '@aws-sdk/credential-provider-process': 3.972.23 + '@aws-sdk/credential-provider-sso': 3.972.25 + '@aws-sdk/credential-provider-web-identity': 3.972.25 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -4530,20 +4537,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.22': + '@aws-sdk/credential-provider-process@3.972.23': dependencies: - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.24': + '@aws-sdk/credential-provider-sso@3.972.25': dependencies: - '@aws-sdk/core': 3.973.24 - '@aws-sdk/nested-clients': 3.996.14 - '@aws-sdk/token-providers': 3.1015.0 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.15 + '@aws-sdk/token-providers': 3.1018.0 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -4552,10 +4559,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.24': + '@aws-sdk/credential-provider-web-identity@3.972.25': dependencies: - '@aws-sdk/core': 3.973.24 - '@aws-sdk/nested-clients': 3.996.14 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.15 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -4581,12 +4588,12 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.974.4': + '@aws-sdk/middleware-flexible-checksums@3.974.5': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/crc64-nvme': 3.972.5 '@aws-sdk/types': 3.973.6 '@smithy/is-array-buffer': 4.2.2 @@ -4617,7 +4624,7 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.972.8': + '@aws-sdk/middleware-recursion-detection@3.972.9': dependencies: '@aws-sdk/types': 3.973.6 '@aws/lambda-invoke-store': 0.2.4 @@ -4625,9 +4632,9 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.24': + '@aws-sdk/middleware-sdk-s3@3.972.26': dependencies: - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-arn-parser': 3.972.3 '@smithy/core': 3.23.12 @@ -4648,9 +4655,9 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.25': + '@aws-sdk/middleware-user-agent@3.972.26': dependencies: - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@smithy/core': 3.23.12 @@ -4659,20 +4666,20 @@ snapshots: '@smithy/util-retry': 4.2.12 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.14': + '@aws-sdk/nested-clients@3.996.15': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.24 + '@aws-sdk/core': 3.973.25 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 - '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.25 - '@aws-sdk/region-config-resolver': 3.972.9 + '@aws-sdk/middleware-recursion-detection': 3.972.9 + '@aws-sdk/middleware-user-agent': 3.972.26 + '@aws-sdk/region-config-resolver': 3.972.10 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.11 + '@aws-sdk/util-user-agent-node': 3.973.12 '@smithy/config-resolver': 4.4.13 '@smithy/core': 3.23.12 '@smithy/fetch-http-handler': 5.3.15 @@ -4702,7 +4709,7 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.972.9': + '@aws-sdk/region-config-resolver@3.972.10': dependencies: '@aws-sdk/types': 3.973.6 '@smithy/config-resolver': 4.4.13 @@ -4710,19 +4717,19 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.12': + '@aws-sdk/signature-v4-multi-region@3.996.14': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.24 + '@aws-sdk/middleware-sdk-s3': 3.972.26 '@aws-sdk/types': 3.973.6 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1015.0': + '@aws-sdk/token-providers@3.1018.0': dependencies: - '@aws-sdk/core': 3.973.24 - '@aws-sdk/nested-clients': 3.996.14 + '@aws-sdk/core': 3.973.25 + '@aws-sdk/nested-clients': 3.996.15 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -4759,16 +4766,16 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.11': + '@aws-sdk/util-user-agent-node@3.973.12': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.25 + '@aws-sdk/middleware-user-agent': 3.972.26 '@aws-sdk/types': 3.973.6 '@smithy/node-config-provider': 4.3.12 '@smithy/types': 4.13.1 '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.15': + '@aws-sdk/xml-builder@3.972.16': dependencies: '@smithy/types': 4.13.1 fast-xml-parser: 5.5.8 @@ -4798,8 +4805,8 @@ snapshots: '@design.estate/dees-catalog@3.49.0(@tiptap/pm@2.27.2)': dependencies: - '@design.estate/dees-domtools': 2.5.1 - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-domtools': 2.5.3 + '@design.estate/dees-element': 2.2.4 '@design.estate/dees-wcctools': 3.8.0 '@fortawesome/fontawesome-svg-core': 7.2.0 '@fortawesome/free-brands-svg-icons': 7.2.0 @@ -4838,7 +4845,7 @@ snapshots: '@push.rocks/smartdelay': 3.0.5 broadcast-channel: 7.3.0 - '@design.estate/dees-domtools@2.5.1': + '@design.estate/dees-domtools@2.5.3': dependencies: '@api.global/typedrequest': 3.3.0 '@design.estate/dees-comms': 1.0.30 @@ -4849,7 +4856,7 @@ snapshots: '@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartrouter': 1.3.3 '@push.rocks/smartrx': 3.0.10 - '@push.rocks/smartstate': 2.2.1 + '@push.rocks/smartstate': 2.3.0 '@push.rocks/smartstring': 4.1.0 '@push.rocks/smarturl': 3.1.0 '@push.rocks/webrequest': 4.0.5 @@ -4864,9 +4871,9 @@ snapshots: - supports-color - vue - '@design.estate/dees-element@2.2.3': + '@design.estate/dees-element@2.2.4': dependencies: - '@design.estate/dees-domtools': 2.5.1 + '@design.estate/dees-domtools': 2.5.3 '@push.rocks/isounique': 1.0.5 '@push.rocks/smartrx': 3.0.10 lit: 3.3.2 @@ -4878,8 +4885,8 @@ snapshots: '@design.estate/dees-wcctools@3.8.0': dependencies: - '@design.estate/dees-domtools': 2.5.1 - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-domtools': 2.5.3 + '@design.estate/dees-element': 2.2.4 '@push.rocks/smartdelay': 3.0.5 lit: 3.3.2 transitivePeerDependencies: @@ -5077,7 +5084,7 @@ snapshots: '@push.rocks/smartshell': 3.3.8 tsx: 4.21.0 - '@git.zone/tstest@3.6.1(socks@2.8.7)(typescript@6.0.2)': + '@git.zone/tstest@3.6.3(socks@2.8.7)(typescript@6.0.2)': dependencies: '@git.zone/tsbundle': 2.10.0 '@git.zone/tsrun': 2.0.2 @@ -5865,14 +5872,11 @@ snapshots: - '@aws-sdk/credential-providers' - '@mongodb-js/zstd' - '@nuxt/kit' - - bare-abort-controller - - bare-buffer - encoding - gcp-metadata - kerberos - mongodb-client-encryption - react - - react-native-b4a - snappy - socks - supports-color @@ -5920,7 +5924,7 @@ snapshots: '@push.rocks/smartbucket@3.3.10': dependencies: - '@aws-sdk/client-s3': 3.1016.0 + '@aws-sdk/client-s3': 3.1018.0 '@push.rocks/smartmime': 2.0.4 '@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpromise': 4.2.3 @@ -5983,7 +5987,7 @@ snapshots: dependencies: '@push.rocks/smartpromise': 4.2.3 '@types/node-forge': 1.3.14 - node-forge: 1.3.3 + node-forge: 1.4.0 '@push.rocks/smartdata@7.1.3(socks@2.8.7)': dependencies: @@ -6015,14 +6019,9 @@ snapshots: - supports-color - vue - '@push.rocks/smartdb@1.0.1': + '@push.rocks/smartdb@2.0.0': dependencies: - '@push.rocks/smartfs': 1.5.0 - '@push.rocks/smartpath': 6.0.0 - '@push.rocks/smartpromise': 4.2.3 - '@push.rocks/smartrx': 3.0.10 - bson: 7.2.0 - mingo: 7.2.0 + '@push.rocks/smartrust': 1.3.2 '@push.rocks/smartdelay@3.0.5': dependencies: @@ -6286,14 +6285,14 @@ snapshots: '@push.rocks/smartrust': 1.3.2 '@tsclass/tsclass': 9.5.0 lru-cache: 11.2.7 - mailparser: 3.9.4 + mailparser: 3.9.6 uuid: 13.0.0 transitivePeerDependencies: - supports-color '@push.rocks/smartmustache@3.0.2': dependencies: - handlebars: 4.7.8 + handlebars: 4.7.9 '@push.rocks/smartnetwork@4.5.2': dependencies: @@ -6332,7 +6331,7 @@ snapshots: '@push.rocks/smartntml@2.0.8': dependencies: - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-element': 2.2.4 '@happy-dom/global-registrator': 15.11.7 '@push.rocks/smartpromise': 4.2.3 fake-indexeddb: 6.2.5 @@ -6383,7 +6382,7 @@ snapshots: '@push.rocks/smartpromise@4.2.3': {} - '@push.rocks/smartproxy@26.3.0': + '@push.rocks/smartproxy@27.0.0': dependencies: '@push.rocks/smartcrypto': 2.0.4 '@push.rocks/smartlog': 3.2.1 @@ -6441,7 +6440,7 @@ snapshots: dependencies: '@push.rocks/lik': 6.4.0 '@push.rocks/smartrx': 3.0.10 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.0 '@push.rocks/smartrust@1.3.2': dependencies: @@ -6490,7 +6489,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@push.rocks/smartstate@2.2.1': + '@push.rocks/smartstate@2.3.0': dependencies: '@push.rocks/smarthash': 3.2.6 '@push.rocks/smartjson': 6.0.0 @@ -6547,11 +6546,11 @@ snapshots: '@push.rocks/smartrust': 1.3.2 '@push.rocks/smartrx': 3.0.10 chokidar: 5.0.0 - picomatch: 4.0.3 + picomatch: 4.0.4 '@push.rocks/smartxml@2.0.0': dependencies: - fast-xml-parser: 5.5.8 + fast-xml-parser: 5.5.9 '@push.rocks/smartyaml@2.0.5': dependencies: @@ -6564,7 +6563,7 @@ snapshots: '@push.rocks/taskbuffer@3.5.0': dependencies: - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-element': 2.2.4 '@push.rocks/lik': 6.4.0 '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartlog': 3.2.1 @@ -6580,7 +6579,7 @@ snapshots: '@push.rocks/taskbuffer@6.1.2': dependencies: - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-element': 2.2.4 '@push.rocks/lik': 6.4.0 '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartlog': 3.2.1 @@ -6596,7 +6595,7 @@ snapshots: '@push.rocks/taskbuffer@8.0.2': dependencies: - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-element': 2.2.4 '@push.rocks/lik': 6.4.0 '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartlog': 3.2.1 @@ -6795,8 +6794,8 @@ snapshots: '@serve.zone/catalog@2.9.0(@tiptap/pm@2.27.2)': dependencies: '@design.estate/dees-catalog': 3.49.0(@tiptap/pm@2.27.2) - '@design.estate/dees-domtools': 2.5.1 - '@design.estate/dees-element': 2.2.3 + '@design.estate/dees-domtools': 2.5.3 + '@design.estate/dees-element': 2.2.4 '@design.estate/dees-wcctools': 3.8.0 transitivePeerDependencies: - '@nuxt/kit' @@ -6811,9 +6810,10 @@ snapshots: '@push.rocks/smartlog-interfaces': 3.0.2 '@tsclass/tsclass': 9.5.0 - '@serve.zone/remoteingress@4.14.3': + '@serve.zone/remoteingress@4.15.3': dependencies: '@push.rocks/qenv': 6.1.3 + '@push.rocks/smartnftables': 1.0.1 '@push.rocks/smartrust': 1.3.2 '@sindresorhus/is@5.6.0': {} @@ -7330,7 +7330,7 @@ snapshots: '@tsclass/tsclass@9.5.0': dependencies: - type-fest: 5.4.4 + type-fest: 5.5.0 '@tybys/wasm-util@0.10.1': dependencies: @@ -7344,7 +7344,7 @@ snapshots: '@types/node': 25.5.0 source-map: 0.6.1 - '@types/debug@4.1.12': + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -7489,7 +7489,7 @@ snapshots: asn1js: 3.0.7 axios: 1.13.6(debug@4.4.3) debug: 4.4.3 - node-forge: 1.3.3 + node-forge: 1.4.0 transitivePeerDependencies: - supports-color @@ -7561,7 +7561,7 @@ snapshots: dependencies: bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.10.0(bare-events@2.8.2) + bare-stream: 2.11.0(bare-events@2.8.2) bare-url: 2.4.0 fast-fifo: 1.3.2 transitivePeerDependencies: @@ -7574,14 +7574,13 @@ snapshots: dependencies: bare-os: 3.8.0 - bare-stream@2.10.0(bare-events@2.8.2): + bare-stream@2.11.0(bare-events@2.8.2): dependencies: streamx: 2.25.0 teex: 1.0.1 optionalDependencies: bare-events: 2.8.2 transitivePeerDependencies: - - bare-abort-controller - react-native-b4a bare-url@2.4.0: @@ -7596,16 +7595,16 @@ snapshots: bowser@2.14.1: {} - brace-expansion@1.1.12: + brace-expansion@1.1.13: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.0.3: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.4: + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 @@ -7985,6 +7984,12 @@ snapshots: path-expression-matcher: 1.2.0 strnum: 2.2.2 + fast-xml-parser@5.5.9: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.2.0 + strnum: 2.2.2 + fault@2.0.1: dependencies: format: 0.2.2 @@ -8164,7 +8169,7 @@ snapshots: graceful-fs@4.2.11: {} - handlebars@4.7.8: + handlebars@4.7.9: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -8511,7 +8516,7 @@ snapshots: lucide@0.577.0: {} - mailparser@3.9.4: + mailparser@3.9.6: dependencies: '@zone-eu/mailsplit': 5.4.8 encoding-japanese: 2.2.0 @@ -8520,7 +8525,7 @@ snapshots: iconv-lite: 0.7.2 libmime: 5.3.7 linkify-it: 5.0.0 - nodemailer: 8.0.2 + nodemailer: 8.0.4 punycode.js: 2.3.1 tlds: 1.261.0 @@ -8867,7 +8872,7 @@ snapshots: micromark@4.0.2: dependencies: - '@types/debug': 4.1.12 + '@types/debug': 4.1.13 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -8905,15 +8910,15 @@ snapshots: minimatch@10.2.4: dependencies: - brace-expansion: 5.0.4 + brace-expansion: 5.0.5 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.13 minimatch@9.0.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.3 minimist@1.2.8: {} @@ -9024,9 +9029,9 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-forge@1.3.3: {} + node-forge@1.4.0: {} - nodemailer@8.0.2: {} + nodemailer@8.0.4: {} normalize-newline@4.1.0: dependencies: @@ -9154,7 +9159,7 @@ snapshots: lru-cache: 11.2.7 minipass: 7.1.3 - path-to-regexp@8.3.0: {} + path-to-regexp@8.4.0: {} pdf-lib@1.17.1: dependencies: @@ -9179,7 +9184,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@4.0.3: {} + picomatch@4.0.4: {} pixelmatch@5.3.0: dependencies: @@ -9814,7 +9819,7 @@ snapshots: type-fest@4.41.0: {} - type-fest@5.4.4: + type-fest@5.5.0: dependencies: tagged-tag: 1.0.0 diff --git a/readme.md b/readme.md index 40cbed7..d236de8 100644 --- a/readme.md +++ b/readme.md @@ -93,6 +93,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community - **Domain-centric certificate overview** with backoff status and one-click reprovisioning - **Remote ingress management** with connection token generation and one-click copy - **Read-only configuration display** — DcRouter is configured through code +- **Smart tab visibility handling** — auto-pauses all polling, WebSocket connections, and chart updates when the browser tab is hidden, preventing resource waste and tab freezing ### 🔧 Programmatic API Client - **Object-oriented API** — resource classes (`Route`, `Certificate`, `ApiToken`, `RemoteIngress`, `Email`) with instance methods diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 676e1f1..0031fab 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '11.11.0', + version: '11.12.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 676e1f1..0031fab 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '11.11.0', + version: '11.12.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts_web/appstate.ts b/ts_web/appstate.ts index 635019f..7cd4ef9 100644 --- a/ts_web/appstate.ts +++ b/ts_web/appstate.ts @@ -1186,18 +1186,33 @@ export const toggleApiTokenAction = routeManagementStatePart.createAction<{ let socketClient: plugins.typedsocket.TypedSocket | null = null; const socketRouter = new plugins.domtools.plugins.typedrequest.TypedRouter(); +// Batched log entry handler — buffers incoming entries and flushes once per animation frame +let logEntryBuffer: interfaces.data.ILogEntry[] = []; +let logFlushScheduled = false; + +function flushLogEntries() { + logFlushScheduled = false; + if (logEntryBuffer.length === 0) return; + const current = logStatePart.getState()!; + const updated = [...current.recentLogs, ...logEntryBuffer]; + logEntryBuffer = []; + // Cap at 2000 entries + if (updated.length > 2000) { + updated.splice(0, updated.length - 2000); + } + logStatePart.setState({ ...current, recentLogs: updated } as ILogState); +} + // Register handler for pushed log entries from the server socketRouter.addTypedHandler( new plugins.domtools.plugins.typedrequest.TypedHandler( 'pushLogEntry', async (dataArg) => { - const current = logStatePart.getState()!; - const updated = [...current.recentLogs, dataArg.entry]; - // Cap at 2000 entries - if (updated.length > 2000) { - updated.splice(0, updated.length - 2000); + logEntryBuffer.push(dataArg.entry); + if (!logFlushScheduled) { + logFlushScheduled = true; + requestAnimationFrame(flushLogEntries); } - logStatePart.setState({ ...current, recentLogs: updated } as ILogState); return {}; } ) @@ -1228,8 +1243,21 @@ async function disconnectSocket() { } } +// In-flight guard to prevent concurrent refresh requests +let isRefreshing = false; + // Combined refresh action for efficient polling async function dispatchCombinedRefreshAction() { + if (isRefreshing) return; + isRefreshing = true; + try { + await dispatchCombinedRefreshActionInner(); + } finally { + isRefreshing = false; + } +} + +async function dispatchCombinedRefreshActionInner() { const context = getActionContext(); if (!context.identity) return; const currentView = uiStatePart.getState()!.activeView; @@ -1355,48 +1383,48 @@ async function dispatchCombinedRefreshAction() { } } -// Initialize auto-refresh -let refreshInterval: NodeJS.Timeout | null = null; -let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessary restarts +// Create a proper action for the combined refresh so we can use createScheduledAction +const combinedRefreshAction = statsStatePart.createAction(async (statePartArg) => { + await dispatchCombinedRefreshAction(); + // Return current state — dispatchCombinedRefreshAction already updates all state parts directly + return statePartArg.getState()!; +}); -// Initialize auto-refresh when UI state is ready -(() => { - const startAutoRefresh = () => { - const uiState = uiStatePart.getState()!; - const loginState = loginStatePart.getState()!; +// Scheduled refresh process with autoPause: 'visibility' — automatically pauses when tab is hidden +let refreshProcess: ReturnType | null = null; - // Only start if conditions are met and not already running at the same rate - if (uiState.autoRefresh && loginState.isLoggedIn) { - // Check if we need to restart the interval (rate changed or not running) - if (!refreshInterval || currentRefreshRate !== uiState.refreshInterval) { - stopAutoRefresh(); - currentRefreshRate = uiState.refreshInterval; - refreshInterval = setInterval(() => { - // Use combined refresh action for efficiency - dispatchCombinedRefreshAction(); - }, uiState.refreshInterval); - } - } else { - stopAutoRefresh(); +const startAutoRefresh = () => { + const uiState = uiStatePart.getState()!; + const loginState = loginStatePart.getState()!; + + if (uiState.autoRefresh && loginState.isLoggedIn) { + // Dispose old process if interval changed or not running + if (refreshProcess) { + refreshProcess.dispose(); + refreshProcess = null; } - }; - - const stopAutoRefresh = () => { - if (refreshInterval) { - clearInterval(refreshInterval); - refreshInterval = null; - currentRefreshRate = 0; + refreshProcess = statsStatePart.createScheduledAction({ + action: combinedRefreshAction, + payload: undefined, + intervalMs: uiState.refreshInterval, + autoPause: 'visibility', + }); + } else { + if (refreshProcess) { + refreshProcess.dispose(); + refreshProcess = null; } - }; + } +}; - // Watch for relevant changes only - let previousAutoRefresh = uiStatePart.getState()!.autoRefresh; - let previousRefreshInterval = uiStatePart.getState()!.refreshInterval; - let previousIsLoggedIn = loginStatePart.getState()!.isLoggedIn; - - uiStatePart.state.subscribe((state) => { - // Only restart if relevant values changed - if (state.autoRefresh !== previousAutoRefresh || +// Watch for relevant changes +let previousAutoRefresh = uiStatePart.getState()!.autoRefresh; +let previousRefreshInterval = uiStatePart.getState()!.refreshInterval; +let previousIsLoggedIn = loginStatePart.getState()!.isLoggedIn; + +uiStatePart.select((s) => ({ autoRefresh: s.autoRefresh, refreshInterval: s.refreshInterval })) + .subscribe((state) => { + if (state.autoRefresh !== previousAutoRefresh || state.refreshInterval !== previousRefreshInterval) { previousAutoRefresh = state.autoRefresh; previousRefreshInterval = state.refreshInterval; @@ -1404,26 +1432,33 @@ let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessar } }); - loginStatePart.state.subscribe((state) => { - // Only restart if login state changed - if (state.isLoggedIn !== previousIsLoggedIn) { - previousIsLoggedIn = state.isLoggedIn; - startAutoRefresh(); +loginStatePart.select((s) => s.isLoggedIn).subscribe((isLoggedIn) => { + if (isLoggedIn !== previousIsLoggedIn) { + previousIsLoggedIn = isLoggedIn; + startAutoRefresh(); - // Connect/disconnect TypedSocket based on login state - if (state.isLoggedIn) { - connectSocket(); - } else { - disconnectSocket(); - } + // Connect/disconnect TypedSocket based on login state + if (isLoggedIn) { + connectSocket(); + } else { + disconnectSocket(); } - }); + } +}); - // Initial start - startAutoRefresh(); - - // Connect TypedSocket if already logged in (e.g., persistent session) - if (loginStatePart.getState()!.isLoggedIn) { +// Pause/resume WebSocket when tab visibility changes +document.addEventListener('visibilitychange', () => { + if (document.hidden) { + disconnectSocket(); + } else if (loginStatePart.getState()!.isLoggedIn) { connectSocket(); } -})(); \ No newline at end of file +}); + +// Initial start +startAutoRefresh(); + +// Connect TypedSocket if already logged in (e.g., persistent session) +if (loginStatePart.getState()!.isLoggedIn) { + connectSocket(); +} \ No newline at end of file diff --git a/ts_web/elements/ops-view-certificates.ts b/ts_web/elements/ops-view-certificates.ts index 6f515e7..fbd2b43 100644 --- a/ts_web/elements/ops-view-certificates.ts +++ b/ts_web/elements/ops-view-certificates.ts @@ -25,7 +25,7 @@ export class OpsViewCertificates extends DeesElement { constructor() { super(); - const sub = appstate.certificateStatePart.state.subscribe((newState) => { + const sub = appstate.certificateStatePart.select().subscribe((newState) => { this.certState = newState; }); this.rxSubscriptions.push(sub); diff --git a/ts_web/elements/ops-view-emails.ts b/ts_web/elements/ops-view-emails.ts index 0baa2fb..3af28be 100644 --- a/ts_web/elements/ops-view-emails.ts +++ b/ts_web/elements/ops-view-emails.ts @@ -28,7 +28,7 @@ export class OpsViewEmails extends DeesElement { async connectedCallback() { await super.connectedCallback(); - this.stateSubscription = appstate.emailOpsStatePart.state.subscribe((state) => { + this.stateSubscription = appstate.emailOpsStatePart.select().subscribe((state) => { this.emails = state.emails; this.isLoading = state.isLoading; }); diff --git a/ts_web/elements/ops-view-network.ts b/ts_web/elements/ops-view-network.ts index 625a562..086c2e7 100644 --- a/ts_web/elements/ops-view-network.ts +++ b/ts_web/elements/ops-view-network.ts @@ -47,10 +47,11 @@ export class OpsViewNetwork extends DeesElement { // Track if we need to update the chart to avoid unnecessary re-renders private lastChartUpdate = 0; private chartUpdateThreshold = 1000; // Minimum ms between chart updates - + private trafficUpdateTimer: any = null; private requestsPerSecHistory: number[] = []; // Track requests/sec over time for trend private historyLoaded = false; // Whether server-side throughput history has been loaded + private visibilityHandler: (() => void) | null = null; constructor() { super(); @@ -59,28 +60,42 @@ export class OpsViewNetwork extends DeesElement { this.updateNetworkData(); this.startTrafficUpdateTimer(); } - + async connectedCallback() { await super.connectedCallback(); - + + // Pause/resume traffic timer when tab visibility changes + this.visibilityHandler = () => { + if (document.hidden) { + this.stopTrafficUpdateTimer(); + } else { + this.startTrafficUpdateTimer(); + } + }; + document.addEventListener('visibilitychange', this.visibilityHandler); + // When network view becomes visible, ensure we fetch network data await appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null); } - + async disconnectedCallback() { await super.disconnectedCallback(); this.stopTrafficUpdateTimer(); + if (this.visibilityHandler) { + document.removeEventListener('visibilitychange', this.visibilityHandler); + this.visibilityHandler = null; + } } private subscribeToStateParts() { // Subscribe and track unsubscribe functions - const statsUnsubscribe = appstate.statsStatePart.state.subscribe((state) => { + const statsUnsubscribe = appstate.statsStatePart.select().subscribe((state) => { this.statsState = state; this.updateNetworkData(); }); this.rxSubscriptions.push(statsUnsubscribe); - const networkUnsubscribe = appstate.networkStatePart.state.subscribe((state) => { + const networkUnsubscribe = appstate.networkStatePart.select().subscribe((state) => { this.networkState = state; this.updateNetworkData(); }); diff --git a/ts_web/elements/ops-view-remoteingress.ts b/ts_web/elements/ops-view-remoteingress.ts index c1979c1..b073f6d 100644 --- a/ts_web/elements/ops-view-remoteingress.ts +++ b/ts_web/elements/ops-view-remoteingress.ts @@ -25,7 +25,7 @@ export class OpsViewRemoteIngress extends DeesElement { constructor() { super(); - const sub = appstate.remoteIngressStatePart.state.subscribe((newState) => { + const sub = appstate.remoteIngressStatePart.select().subscribe((newState) => { this.riState = newState; }); this.rxSubscriptions.push(sub); diff --git a/ts_web/readme.md b/ts_web/readme.md index a2e8dcf..ae48733 100644 --- a/ts_web/readme.md +++ b/ts_web/readme.md @@ -111,7 +111,7 @@ ts_web/ ### State Management -The app uses `@push.rocks/smartstate` with multiple state parts: +The app uses `@push.rocks/smartstate` v2.3+ with multiple state parts, scheduled actions with `autoPause: 'visibility'`, and batched updates: | State Part | Mode | Description | |-----------|------|-------------| @@ -125,6 +125,16 @@ The app uses `@push.rocks/smartstate` with multiple state parts: | `certificateStatePart` | Soft | Certificate list, summary, loading state | | `remoteIngressStatePart` | Soft | Edge list, statuses, new edge secret | +### Tab Visibility Optimization + +The dashboard automatically pauses all background activity when the browser tab is hidden and resumes when visible: + +- **Auto-refresh polling** uses `createScheduledAction` with `autoPause: 'visibility'` — stops HTTP requests while the tab is sleeping +- **In-flight guard** prevents concurrent refresh requests from piling up +- **WebSocket connection** disconnects when hidden and reconnects when visible, preventing log entry accumulation +- **Network traffic timer** pauses chart updates when the tab is backgrounded +- **Log entry batching** — incoming WebSocket log pushes are buffered and flushed once per animation frame to avoid per-entry re-renders + ### Actions ```typescript diff --git a/ts_web/router.ts b/ts_web/router.ts index 6641f5d..cb5805f 100644 --- a/ts_web/router.ts +++ b/ts_web/router.ts @@ -38,7 +38,7 @@ class AppRouter { } private setupStateSync(): void { - appstate.uiStatePart.state.subscribe((uiState) => { + appstate.uiStatePart.select().subscribe((uiState) => { if (this.suppressStateUpdate) return; const currentPath = window.location.pathname;