diff --git a/docs/api.md b/docs/api.md index bc2bc61..dbe195d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -146,9 +146,9 @@ Main configuration interface for the plugin. ```typescript interface PomodoroSettings { - workTime: number; // Work session duration in minutes - shortBreakTime: number; // Short break duration in minutes - longBreakTime: number; // Long break duration in minutes + workMinutes: number; // Work session duration in minutes + shortBreakMinutes: number; // Short break duration in minutes + longBreakMinutes: number; // Long break duration in minutes intervalsBeforeLongBreak: number; // Work intervals before long break showIcon: boolean; // Display timer icon in status bar } @@ -189,9 +189,9 @@ Default configuration values: ```typescript const DEFAULT_SETTINGS: PomodoroSettings = { - workTime: 25, - shortBreakTime: 5, - longBreakTime: 15, + workMinutes: 25, + shortBreakMinutes: 5, + longBreakMinutes: 15, intervalsBeforeLongBreak: 4, showIcon: false }; diff --git a/docs/development.md b/docs/development.md index ed5822b..95d03d7 100644 --- a/docs/development.md +++ b/docs/development.md @@ -117,9 +117,9 @@ cd obsidian-pomodoro ```typescript interface PomodoroSettings { - workTime: number; // Work session duration (minutes) - shortBreakTime: number; // Short break duration (minutes) - longBreakTime: number; // Long break duration (minutes) + workMinutes: number; // Work session duration (minutes) + shortBreakMinutes: number; // Short break duration (minutes) + longBreakMinutes: number; // Long break duration (minutes) intervalsBeforeLongBreak: number; // Work sessions before long break showIcon: boolean; // Show/hide timer icon } diff --git a/package.json b/package.json index 952f0da..bc9bb3c 100644 --- a/package.json +++ b/package.json @@ -26,21 +26,21 @@ "author": "Miguel Pimentel", "license": "MIT", "devDependencies": { - "@eslint/js": "^9.33.0", - "@jest/globals": "^30.0.5", + "@eslint/js": "^9.35.0", + "@jest/globals": "^30.1.2", "@types/jest": "^30.0.0", - "@types/node": "^24.3.0", + "@types/node": "^24.5.2", "builtin-modules": "5.0.0", - "esbuild": "^0.25.9", + "esbuild": "^0.25.10", "esbuild-plugin-svg": "^0.1.0", - "eslint": "^9.33.0", + "eslint": "^9.35.0", "eslint-plugin-jest": "^29.0.1", - "globals": "^16.3.0", - "jest": "^30.0.5", + "globals": "^16.4.0", + "jest": "^30.1.3", "obsidian": "latest", - "ts-jest": "^29.4.1", + "ts-jest": "^29.4.3", "tslib": "2.4.0", "typescript": "^5.9.2", - "typescript-eslint": "^8.39.1" + "typescript-eslint": "^8.44.0" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 891c535..07fd072 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,44 +9,44 @@ importers: .: devDependencies: '@eslint/js': - specifier: ^9.33.0 - version: 9.33.0 + specifier: ^9.35.0 + version: 9.35.0 '@jest/globals': - specifier: ^30.0.5 - version: 30.0.5 + specifier: ^30.1.2 + version: 30.1.2 '@types/jest': specifier: ^30.0.0 version: 30.0.0 '@types/node': - specifier: ^24.3.0 - version: 24.3.0 + specifier: ^24.5.2 + version: 24.5.2 builtin-modules: specifier: 5.0.0 version: 5.0.0 esbuild: - specifier: ^0.25.9 - version: 0.25.9 + specifier: ^0.25.10 + version: 0.25.10 esbuild-plugin-svg: specifier: ^0.1.0 version: 0.1.0 eslint: - specifier: ^9.33.0 - version: 9.33.0 + specifier: ^9.35.0 + version: 9.35.0 eslint-plugin-jest: specifier: ^29.0.1 - version: 29.0.1(@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(jest@30.0.5(@types/node@24.3.0))(typescript@5.9.2) + version: 29.0.1(@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(jest@30.1.3(@types/node@24.5.2))(typescript@5.9.2) globals: - specifier: ^16.3.0 - version: 16.3.0 + specifier: ^16.4.0 + version: 16.4.0 jest: - specifier: ^30.0.5 - version: 30.0.5(@types/node@24.3.0) + specifier: ^30.1.3 + version: 30.1.3(@types/node@24.5.2) obsidian: specifier: latest version: 1.8.7(@codemirror/state@6.5.2)(@codemirror/view@6.37.2) ts-jest: - specifier: ^29.4.1 - version: 29.4.1(@babel/core@7.28.3)(@jest/transform@30.0.5)(@jest/types@30.0.5)(babel-jest@30.0.5(@babel/core@7.28.3))(esbuild@0.25.9)(jest-util@30.0.5)(jest@30.0.5(@types/node@24.3.0))(typescript@5.9.2) + specifier: ^29.4.3 + version: 29.4.3(@babel/core@7.28.4)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@30.0.5)(jest@30.1.3(@types/node@24.5.2))(typescript@5.9.2) tslib: specifier: 2.4.0 version: 2.4.0 @@ -54,25 +54,21 @@ importers: specifier: ^5.9.2 version: 5.9.2 typescript-eslint: - specifier: ^8.39.1 - version: 8.39.1(eslint@9.33.0)(typescript@5.9.2) + specifier: ^8.44.0 + version: 8.44.0(eslint@9.35.0)(typescript@5.9.2) packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.0': - resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.3': - resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} engines: {node: '>=6.9.0'} '@babel/generator@7.28.3': @@ -113,12 +109,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.3': - resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.3': - resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true @@ -217,12 +213,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.3': - resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.2': - resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -234,173 +230,173 @@ packages: '@codemirror/view@6.37.2': resolution: {integrity: sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==} - '@emnapi/core@1.4.5': - resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + '@emnapi/core@1.5.0': + resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} - '@emnapi/runtime@1.4.5': - resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - '@emnapi/wasi-threads@1.0.4': - resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.25.9': - resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + '@esbuild/aix-ppc64@0.25.10': + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.9': - resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + '@esbuild/android-arm64@0.25.10': + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.9': - resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + '@esbuild/android-arm@0.25.10': + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.9': - resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + '@esbuild/android-x64@0.25.10': + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.9': - resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + '@esbuild/darwin-arm64@0.25.10': + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.9': - resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + '@esbuild/darwin-x64@0.25.10': + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.9': - resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + '@esbuild/freebsd-arm64@0.25.10': + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.9': - resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + '@esbuild/freebsd-x64@0.25.10': + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.9': - resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + '@esbuild/linux-arm64@0.25.10': + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.9': - resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + '@esbuild/linux-arm@0.25.10': + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.9': - resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + '@esbuild/linux-ia32@0.25.10': + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.9': - resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + '@esbuild/linux-loong64@0.25.10': + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.9': - resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + '@esbuild/linux-mips64el@0.25.10': + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.9': - resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + '@esbuild/linux-ppc64@0.25.10': + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.9': - resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + '@esbuild/linux-riscv64@0.25.10': + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.9': - resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + '@esbuild/linux-s390x@0.25.10': + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.9': - resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + '@esbuild/linux-x64@0.25.10': + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.9': - resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + '@esbuild/netbsd-arm64@0.25.10': + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.9': - resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + '@esbuild/netbsd-x64@0.25.10': + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.9': - resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + '@esbuild/openbsd-arm64@0.25.10': + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.9': - resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + '@esbuild/openbsd-x64@0.25.10': + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.9': - resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + '@esbuild/openharmony-arm64@0.25.10': + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.9': - resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + '@esbuild/sunos-x64@0.25.10': + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.9': - resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + '@esbuild/win32-arm64@0.25.10': + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.9': - resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + '@esbuild/win32-ia32@0.25.10': + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.9': - resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + '@esbuild/win32-x64@0.25.10': + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -425,8 +421,8 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.33.0': - resolution: {integrity: sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==} + '@eslint/js@9.35.0': + resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': @@ -441,18 +437,14 @@ packages: resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -469,12 +461,12 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/console@30.0.5': - resolution: {integrity: sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA==} + '@jest/console@30.1.2': + resolution: {integrity: sha512-BGMAxj8VRmoD0MoA/jo9alMXSRoqW8KPeqOfEo1ncxnRLatTBCpRoOwlwlEMdudp68Q6WSGwYrrLtTGOh8fLzw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/core@30.0.5': - resolution: {integrity: sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg==} + '@jest/core@30.1.3': + resolution: {integrity: sha512-LIQz7NEDDO1+eyOA2ZmkiAyYvZuo6s1UxD/e2IHldR6D7UYogVq3arTmli07MkENLq6/3JEQjp0mA8rrHHJ8KQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -486,36 +478,36 @@ packages: resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/environment@30.0.5': - resolution: {integrity: sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA==} + '@jest/environment@30.1.2': + resolution: {integrity: sha512-N8t1Ytw4/mr9uN28OnVf0SYE2dGhaIxOVYcwsf9IInBKjvofAjbFRvedvBBlyTYk2knbJTiEjEJ2PyyDIBnd9w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect-utils@30.0.5': - resolution: {integrity: sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==} + '@jest/expect-utils@30.1.2': + resolution: {integrity: sha512-HXy1qT/bfdjCv7iC336ExbqqYtZvljrV8odNdso7dWK9bSeHtLlvwWWC3YSybSPL03Gg5rug6WLCZAZFH72m0A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect@30.0.5': - resolution: {integrity: sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA==} + '@jest/expect@30.1.2': + resolution: {integrity: sha512-tyaIExOwQRCxPCGNC05lIjWJztDwk2gPDNSDGg1zitXJJ8dC3++G/CRjE5mb2wQsf89+lsgAgqxxNpDLiCViTA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/fake-timers@30.0.5': - resolution: {integrity: sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw==} + '@jest/fake-timers@30.1.2': + resolution: {integrity: sha512-Beljfv9AYkr9K+ETX9tvV61rJTY706BhBUtiaepQHeEGfe0DbpvUA5Z3fomwc5Xkhns6NWrcFDZn+72fLieUnA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/get-type@30.0.1': - resolution: {integrity: sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==} + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/globals@30.0.5': - resolution: {integrity: sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA==} + '@jest/globals@30.1.2': + resolution: {integrity: sha512-teNTPZ8yZe3ahbYnvnVRDeOjr+3pu2uiAtNtrEsiMjVPPj+cXd5E/fr8BL7v/T7F31vYdEHrI5cC/2OoO/vM9A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/pattern@30.0.1': resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/reporters@30.0.5': - resolution: {integrity: sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g==} + '@jest/reporters@30.1.3': + resolution: {integrity: sha512-VWEQmJWfXMOrzdFEOyGjUEOuVXllgZsoPtEHZzfdNz18RmzJ5nlR6kp8hDdY8dDS1yGOXAY7DHT+AOHIPSBV0w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -527,24 +519,24 @@ packages: resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/snapshot-utils@30.0.5': - resolution: {integrity: sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ==} + '@jest/snapshot-utils@30.1.2': + resolution: {integrity: sha512-vHoMTpimcPSR7OxS2S0V1Cpg8eKDRxucHjoWl5u4RQcnxqQrV3avETiFpl8etn4dqxEGarBeHbIBety/f8mLXw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/source-map@30.0.1': resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-result@30.0.5': - resolution: {integrity: sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ==} + '@jest/test-result@30.1.3': + resolution: {integrity: sha512-P9IV8T24D43cNRANPPokn7tZh0FAFnYS2HIfi5vK18CjRkTDR9Y3e1BoEcAJnl4ghZZF4Ecda4M/k41QkvurEQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-sequencer@30.0.5': - resolution: {integrity: sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ==} + '@jest/test-sequencer@30.1.3': + resolution: {integrity: sha512-82J+hzC0qeQIiiZDThh+YUadvshdBswi5nuyXlEmXzrhw5ZQSRHeQ5LpVMD/xc8B3wPePvs6VMzHnntxL+4E3w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/transform@30.0.5': - resolution: {integrity: sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg==} + '@jest/transform@30.1.2': + resolution: {integrity: sha512-UYYFGifSgfjujf1Cbd3iU/IQoSd6uwsj8XHj5DSDf5ERDcWMdJOPTkHWXj4U+Z/uMagyOQZ6Vne8C4nRIrCxqA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@30.0.5': @@ -554,6 +546,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -561,8 +556,8 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@marijn/find-cluster-break@1.0.2': resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} @@ -590,8 +585,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@sinclair/typebox@0.34.40': - resolution: {integrity: sha512-gwBNIP8ZAYev/ORDWW0QvxdwPXwxBtLsdsJgSc7eDIRt8ubP+rxUBzPsrwnu16fgEF8Bx4lh/+mvQvJzcTM6Kw==} + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -603,8 +598,8 @@ packages: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} - '@tybys/wasm-util@0.10.0': - resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -639,8 +634,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@24.3.0': - resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} + '@types/node@24.5.2': + resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -654,63 +649,63 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.39.1': - resolution: {integrity: sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==} + '@typescript-eslint/eslint-plugin@8.44.0': + resolution: {integrity: sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.39.1 + '@typescript-eslint/parser': ^8.44.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.39.1': - resolution: {integrity: sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==} + '@typescript-eslint/parser@8.44.0': + resolution: {integrity: sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.39.1': - resolution: {integrity: sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==} + '@typescript-eslint/project-service@8.44.0': + resolution: {integrity: sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.39.1': - resolution: {integrity: sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==} + '@typescript-eslint/scope-manager@8.44.0': + resolution: {integrity: sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.39.1': - resolution: {integrity: sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==} + '@typescript-eslint/tsconfig-utils@8.44.0': + resolution: {integrity: sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.39.1': - resolution: {integrity: sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==} + '@typescript-eslint/type-utils@8.44.0': + resolution: {integrity: sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.39.1': - resolution: {integrity: sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==} + '@typescript-eslint/types@8.44.0': + resolution: {integrity: sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.39.1': - resolution: {integrity: sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==} + '@typescript-eslint/typescript-estree@8.44.0': + resolution: {integrity: sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.39.1': - resolution: {integrity: sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==} + '@typescript-eslint/utils@8.44.0': + resolution: {integrity: sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.39.1': - resolution: {integrity: sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==} + '@typescript-eslint/visitor-keys@8.44.0': + resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -832,8 +827,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.0: - resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: @@ -844,8 +839,8 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} anymatch@3.1.3: @@ -858,14 +853,14 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - babel-jest@30.0.5: - resolution: {integrity: sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==} + babel-jest@30.1.2: + resolution: {integrity: sha512-IQCus1rt9kaSh7PQxLYRY5NmkNrNlU2TpabzwV7T2jljnpdHOcmnYYv8QmE04Li4S3a2Lj8/yXyET5pBarPr6g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 - babel-plugin-istanbul@7.0.0: - resolution: {integrity: sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==} + babel-plugin-istanbul@7.0.1: + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} engines: {node: '>=12'} babel-plugin-jest-hoist@30.0.1: @@ -886,6 +881,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.8.6: + resolution: {integrity: sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==} + hasBin: true + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -899,8 +898,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.2: - resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} + browserslist@4.26.2: + resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -930,8 +929,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001735: - resolution: {integrity: sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==} + caniuse-lite@1.0.30001743: + resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -998,8 +997,8 @@ packages: resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} engines: {node: '>=8.0.0'} - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1007,8 +1006,8 @@ packages: supports-color: optional: true - dedent@1.6.0: - resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} + dedent@1.7.0: + resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -1042,8 +1041,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.203: - resolution: {integrity: sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==} + electron-to-chromium@1.5.222: + resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -1058,14 +1057,14 @@ packages: entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} esbuild-plugin-svg@0.1.0: resolution: {integrity: sha512-/9ZhvIpl+Ovl6glVK3BedvIwrOwSQnECw4Fy6ZwysWib3Ns7UkX6WNGjMOWtvQ1Cnm0uc7sptiKGm0BthKCAJA==} - esbuild@0.25.9: - resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + esbuild@0.25.10: + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} hasBin: true @@ -1106,8 +1105,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.33.0: - resolution: {integrity: sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==} + eslint@9.35.0: + resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1149,8 +1148,8 @@ packages: resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} engines: {node: '>= 0.8.0'} - expect@30.0.5: - resolution: {integrity: sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==} + expect@30.1.2: + resolution: {integrity: sha512-xvHszRavo28ejws8FpemjhwswGj4w/BetHIL8cU49u4sGyXDw2+p3YbeDbj6xzlxi6kWTjIRSTJ+9sNXPnF0Zg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} fast-deep-equal@3.1.3: @@ -1243,8 +1242,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@16.3.0: - resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} engines: {node: '>=18'} graceful-fs@4.2.11: @@ -1354,12 +1353,12 @@ packages: resolution: {integrity: sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-circus@30.0.5: - resolution: {integrity: sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug==} + jest-circus@30.1.3: + resolution: {integrity: sha512-Yf3dnhRON2GJT4RYzM89t/EXIWNxKTpWTL9BfF3+geFetWP4XSvJjiU1vrWplOiUkmq8cHLiwuhz+XuUp9DscA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-cli@30.0.5: - resolution: {integrity: sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw==} + jest-cli@30.1.3: + resolution: {integrity: sha512-G8E2Ol3OKch1DEeIBl41NP7OiC6LBhfg25Btv+idcusmoUSpqUkbrneMqbW9lVpI/rCKb/uETidb7DNteheuAQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -1368,8 +1367,8 @@ packages: node-notifier: optional: true - jest-config@30.0.5: - resolution: {integrity: sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==} + jest-config@30.1.3: + resolution: {integrity: sha512-M/f7gqdQEPgZNA181Myz+GXCe8jXcJsGjCMXUzRj22FIXsZOyHNte84e0exntOvdPaeh9tA0w+B8qlP2fAezfw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@types/node': '*' @@ -1383,36 +1382,36 @@ packages: ts-node: optional: true - jest-diff@30.0.5: - resolution: {integrity: sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==} + jest-diff@30.1.2: + resolution: {integrity: sha512-4+prq+9J61mOVXCa4Qp8ZjavdxzrWQXrI80GNxP8f4tkI2syPuPrJgdRPZRrfUTRvIoUwcmNLbqEJy9W800+NQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-docblock@30.0.1: resolution: {integrity: sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-each@30.0.5: - resolution: {integrity: sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ==} + jest-each@30.1.0: + resolution: {integrity: sha512-A+9FKzxPluqogNahpCv04UJvcZ9B3HamqpDNWNKDjtxVRYB8xbZLFuCr8JAJFpNp83CA0anGQFlpQna9Me+/tQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-environment-node@30.0.5: - resolution: {integrity: sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA==} + jest-environment-node@30.1.2: + resolution: {integrity: sha512-w8qBiXtqGWJ9xpJIA98M0EIoq079GOQRQUyse5qg1plShUCQ0Ek1VTTcczqKrn3f24TFAgFtT+4q3aOXvjbsuA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-haste-map@30.0.5: - resolution: {integrity: sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg==} + jest-haste-map@30.1.0: + resolution: {integrity: sha512-JLeM84kNjpRkggcGpQLsV7B8W4LNUWz7oDNVnY1Vjj22b5/fAb3kk3htiD+4Na8bmJmjJR7rBtS2Rmq/NEcADg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-leak-detector@30.0.5: - resolution: {integrity: sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg==} + jest-leak-detector@30.1.0: + resolution: {integrity: sha512-AoFvJzwxK+4KohH60vRuHaqXfWmeBATFZpzpmzNmYTtmRMiyGPVhkXpBqxUQunw+dQB48bDf4NpUs6ivVbRv1g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-matcher-utils@30.0.5: - resolution: {integrity: sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==} + jest-matcher-utils@30.1.2: + resolution: {integrity: sha512-7ai16hy4rSbDjvPTuUhuV8nyPBd6EX34HkBsBcBX2lENCuAQ0qKCPb/+lt8OSWUa9WWmGYLy41PrEzkwRwoGZQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-message-util@30.0.5: - resolution: {integrity: sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==} + jest-message-util@30.1.0: + resolution: {integrity: sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-mock@30.0.5: @@ -1432,44 +1431,44 @@ packages: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve-dependencies@30.0.5: - resolution: {integrity: sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw==} + jest-resolve-dependencies@30.1.3: + resolution: {integrity: sha512-DNfq3WGmuRyHRHfEet+Zm3QOmVFtIarUOQHHryKPc0YL9ROfgWZxl4+aZq/VAzok2SS3gZdniP+dO4zgo59hBg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve@30.0.5: - resolution: {integrity: sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg==} + jest-resolve@30.1.3: + resolution: {integrity: sha512-DI4PtTqzw9GwELFS41sdMK32Ajp3XZQ8iygeDMWkxlRhm7uUTOFSZFVZABFuxr0jvspn8MAYy54NxZCsuCTSOw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runner@30.0.5: - resolution: {integrity: sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw==} + jest-runner@30.1.3: + resolution: {integrity: sha512-dd1ORcxQraW44Uz029TtXj85W11yvLpDuIzNOlofrC8GN+SgDlgY4BvyxJiVeuabA1t6idjNbX59jLd2oplOGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runtime@30.0.5: - resolution: {integrity: sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A==} + jest-runtime@30.1.3: + resolution: {integrity: sha512-WS8xgjuNSphdIGnleQcJ3AKE4tBKOVP+tKhCD0u+Tb2sBmsU8DxfbBpZX7//+XOz81zVs4eFpJQwBNji2Y07DA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-snapshot@30.0.5: - resolution: {integrity: sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g==} + jest-snapshot@30.1.2: + resolution: {integrity: sha512-4q4+6+1c8B6Cy5pGgFvjDy/Pa6VYRiGu0yQafKkJ9u6wQx4G5PqI2QR6nxTl43yy7IWsINwz6oT4o6tD12a8Dg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@30.0.5: resolution: {integrity: sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-validate@30.0.5: - resolution: {integrity: sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw==} + jest-validate@30.1.0: + resolution: {integrity: sha512-7P3ZlCFW/vhfQ8pE7zW6Oi4EzvuB4sgR72Q1INfW9m0FGo0GADYlPwIkf4CyPq7wq85g+kPMtPOHNAdWHeBOaA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-watcher@30.0.5: - resolution: {integrity: sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg==} + jest-watcher@30.1.3: + resolution: {integrity: sha512-6jQUZCP1BTL2gvG9E4YF06Ytq4yMb4If6YoQGRR6PpjtqOXSP3sKe2kqwB6SQ+H9DezOfZaSLnmka1NtGm3fCQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-worker@30.0.5: - resolution: {integrity: sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ==} + jest-worker@30.1.0: + resolution: {integrity: sha512-uvWcSjlwAAgIu133Tt77A05H7RIk3Ho8tZL50bQM2AkvLdluw9NG48lRCl3Dt+MOH719n/0nnb5YxUwcuJiKRA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest@30.0.5: - resolution: {integrity: sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ==} + jest@30.1.3: + resolution: {integrity: sha512-Ry+p2+NLk6u8Agh5yVqELfUJvRfV51hhVBRIB5yZPY7mU0DGBmOuFG5GebZbMbm86cdQNK0fhJuDX8/1YorISQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -1607,8 +1606,8 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.21: + resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -1810,8 +1809,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom@4.0.0: @@ -1863,8 +1862,8 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-jest@29.4.1: - resolution: {integrity: sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==} + ts-jest@29.4.3: + resolution: {integrity: sha512-KTWbK2Wot8VXargsLoxhSoEQ9OyMdzQXQoUDeIulWu2Tf7gghuBHeg+agZqVLdTOHhQHVKAaeuctBDRkhWE7hg==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1909,8 +1908,8 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - typescript-eslint@8.39.1: - resolution: {integrity: sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==} + typescript-eslint@8.44.0: + resolution: {integrity: sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1926,8 +1925,8 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - undici-types@7.10.0: - resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + undici-types@7.12.0: + resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -1999,33 +1998,28 @@ packages: snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.0': {} + '@babel/compat-data@7.28.4': {} - '@babel/core@7.28.3': + '@babel/core@7.28.4': dependencies: - '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) - '@babel/helpers': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2034,17 +2028,17 @@ snapshots: '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.0 + '@babel/compat-data': 7.28.4 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.2 + browserslist: 4.26.2 lru-cache: 5.1.1 semver: 6.3.1 @@ -2052,17 +2046,17 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -2074,119 +2068,119 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.3': + '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 - '@babel/parser@7.28.3': + '@babel/parser@7.28.4': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.3)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.3)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.3)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.3)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.3)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.3)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.3)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.3)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.3)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.3)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.3)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.3)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.3)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.3)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 - '@babel/traverse@7.28.3': + '@babel/traverse@7.28.4': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.28.3 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.3 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/types': 7.28.2 - debug: 4.4.1 + '@babel/types': 7.28.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.2': + '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -2204,103 +2198,103 @@ snapshots: style-mod: 4.1.2 w3c-keyname: 2.2.8 - '@emnapi/core@1.4.5': + '@emnapi/core@1.5.0': dependencies: - '@emnapi/wasi-threads': 1.0.4 + '@emnapi/wasi-threads': 1.1.0 tslib: 2.4.0 optional: true - '@emnapi/runtime@1.4.5': + '@emnapi/runtime@1.5.0': dependencies: tslib: 2.4.0 optional: true - '@emnapi/wasi-threads@1.0.4': + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.4.0 optional: true - '@esbuild/aix-ppc64@0.25.9': + '@esbuild/aix-ppc64@0.25.10': optional: true - '@esbuild/android-arm64@0.25.9': + '@esbuild/android-arm64@0.25.10': optional: true - '@esbuild/android-arm@0.25.9': + '@esbuild/android-arm@0.25.10': optional: true - '@esbuild/android-x64@0.25.9': + '@esbuild/android-x64@0.25.10': optional: true - '@esbuild/darwin-arm64@0.25.9': + '@esbuild/darwin-arm64@0.25.10': optional: true - '@esbuild/darwin-x64@0.25.9': + '@esbuild/darwin-x64@0.25.10': optional: true - '@esbuild/freebsd-arm64@0.25.9': + '@esbuild/freebsd-arm64@0.25.10': optional: true - '@esbuild/freebsd-x64@0.25.9': + '@esbuild/freebsd-x64@0.25.10': optional: true - '@esbuild/linux-arm64@0.25.9': + '@esbuild/linux-arm64@0.25.10': optional: true - '@esbuild/linux-arm@0.25.9': + '@esbuild/linux-arm@0.25.10': optional: true - '@esbuild/linux-ia32@0.25.9': + '@esbuild/linux-ia32@0.25.10': optional: true - '@esbuild/linux-loong64@0.25.9': + '@esbuild/linux-loong64@0.25.10': optional: true - '@esbuild/linux-mips64el@0.25.9': + '@esbuild/linux-mips64el@0.25.10': optional: true - '@esbuild/linux-ppc64@0.25.9': + '@esbuild/linux-ppc64@0.25.10': optional: true - '@esbuild/linux-riscv64@0.25.9': + '@esbuild/linux-riscv64@0.25.10': optional: true - '@esbuild/linux-s390x@0.25.9': + '@esbuild/linux-s390x@0.25.10': optional: true - '@esbuild/linux-x64@0.25.9': + '@esbuild/linux-x64@0.25.10': optional: true - '@esbuild/netbsd-arm64@0.25.9': + '@esbuild/netbsd-arm64@0.25.10': optional: true - '@esbuild/netbsd-x64@0.25.9': + '@esbuild/netbsd-x64@0.25.10': optional: true - '@esbuild/openbsd-arm64@0.25.9': + '@esbuild/openbsd-arm64@0.25.10': optional: true - '@esbuild/openbsd-x64@0.25.9': + '@esbuild/openbsd-x64@0.25.10': optional: true - '@esbuild/openharmony-arm64@0.25.9': + '@esbuild/openharmony-arm64@0.25.10': optional: true - '@esbuild/sunos-x64@0.25.9': + '@esbuild/sunos-x64@0.25.10': optional: true - '@esbuild/win32-arm64@0.25.9': + '@esbuild/win32-arm64@0.25.10': optional: true - '@esbuild/win32-ia32@0.25.9': + '@esbuild/win32-ia32@0.25.10': optional: true - '@esbuild/win32-x64@0.25.9': + '@esbuild/win32-x64@0.25.10': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.33.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0)': dependencies: - eslint: 9.33.0 + eslint: 9.35.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -2308,7 +2302,7 @@ snapshots: '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -2322,7 +2316,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -2333,7 +2327,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.33.0': {} + '@eslint/js@9.35.0': {} '@eslint/object-schema@2.1.6': {} @@ -2344,22 +2338,20 @@ snapshots: '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.3': {} '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -2374,42 +2366,42 @@ snapshots: '@istanbuljs/schema@0.1.3': {} - '@jest/console@30.0.5': + '@jest/console@30.1.2': dependencies: '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 chalk: 4.1.2 - jest-message-util: 30.0.5 + jest-message-util: 30.1.0 jest-util: 30.0.5 slash: 3.0.0 - '@jest/core@30.0.5': + '@jest/core@30.1.3': dependencies: - '@jest/console': 30.0.5 + '@jest/console': 30.1.2 '@jest/pattern': 30.0.1 - '@jest/reporters': 30.0.5 - '@jest/test-result': 30.0.5 - '@jest/transform': 30.0.5 + '@jest/reporters': 30.1.3 + '@jest/test-result': 30.1.3 + '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.0 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.0.5 - jest-config: 30.0.5(@types/node@24.3.0) - jest-haste-map: 30.0.5 - jest-message-util: 30.0.5 + jest-config: 30.1.3(@types/node@24.5.2) + jest-haste-map: 30.1.0 + jest-message-util: 30.1.0 jest-regex-util: 30.0.1 - jest-resolve: 30.0.5 - jest-resolve-dependencies: 30.0.5 - jest-runner: 30.0.5 - jest-runtime: 30.0.5 - jest-snapshot: 30.0.5 + jest-resolve: 30.1.3 + jest-resolve-dependencies: 30.1.3 + jest-runner: 30.1.3 + jest-runtime: 30.1.3 + jest-snapshot: 30.1.2 jest-util: 30.0.5 - jest-validate: 30.0.5 - jest-watcher: 30.0.5 + jest-validate: 30.1.0 + jest-watcher: 30.1.3 micromatch: 4.0.8 pretty-format: 30.0.5 slash: 3.0.0 @@ -2421,39 +2413,39 @@ snapshots: '@jest/diff-sequences@30.0.1': {} - '@jest/environment@30.0.5': + '@jest/environment@30.1.2': dependencies: - '@jest/fake-timers': 30.0.5 + '@jest/fake-timers': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 jest-mock: 30.0.5 - '@jest/expect-utils@30.0.5': + '@jest/expect-utils@30.1.2': dependencies: - '@jest/get-type': 30.0.1 + '@jest/get-type': 30.1.0 - '@jest/expect@30.0.5': + '@jest/expect@30.1.2': dependencies: - expect: 30.0.5 - jest-snapshot: 30.0.5 + expect: 30.1.2 + jest-snapshot: 30.1.2 transitivePeerDependencies: - supports-color - '@jest/fake-timers@30.0.5': + '@jest/fake-timers@30.1.2': dependencies: '@jest/types': 30.0.5 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 24.3.0 - jest-message-util: 30.0.5 + '@types/node': 24.5.2 + jest-message-util: 30.1.0 jest-mock: 30.0.5 jest-util: 30.0.5 - '@jest/get-type@30.0.1': {} + '@jest/get-type@30.1.0': {} - '@jest/globals@30.0.5': + '@jest/globals@30.1.2': dependencies: - '@jest/environment': 30.0.5 - '@jest/expect': 30.0.5 + '@jest/environment': 30.1.2 + '@jest/expect': 30.1.2 '@jest/types': 30.0.5 jest-mock: 30.0.5 transitivePeerDependencies: @@ -2461,18 +2453,18 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 24.3.0 + '@types/node': 24.5.2 jest-regex-util: 30.0.1 - '@jest/reporters@30.0.5': + '@jest/reporters@30.1.3': dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 30.0.5 - '@jest/test-result': 30.0.5 - '@jest/transform': 30.0.5 + '@jest/console': 30.1.2 + '@jest/test-result': 30.1.3 + '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@jridgewell/trace-mapping': 0.3.30 - '@types/node': 24.3.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 24.5.2 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit-x: 0.2.2 @@ -2483,9 +2475,9 @@ snapshots: istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.2.0 - jest-message-util: 30.0.5 + jest-message-util: 30.1.0 jest-util: 30.0.5 - jest-worker: 30.0.5 + jest-worker: 30.1.0 slash: 3.0.0 string-length: 4.0.2 v8-to-istanbul: 9.3.0 @@ -2494,9 +2486,9 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.40 + '@sinclair/typebox': 0.34.41 - '@jest/snapshot-utils@30.0.5': + '@jest/snapshot-utils@30.1.2': dependencies: '@jest/types': 30.0.5 chalk: 4.1.2 @@ -2505,35 +2497,35 @@ snapshots: '@jest/source-map@30.0.1': dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 callsites: 3.1.0 graceful-fs: 4.2.11 - '@jest/test-result@30.0.5': + '@jest/test-result@30.1.3': dependencies: - '@jest/console': 30.0.5 + '@jest/console': 30.1.2 '@jest/types': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 - '@jest/test-sequencer@30.0.5': + '@jest/test-sequencer@30.1.3': dependencies: - '@jest/test-result': 30.0.5 + '@jest/test-result': 30.1.3 graceful-fs: 4.2.11 - jest-haste-map: 30.0.5 + jest-haste-map: 30.1.0 slash: 3.0.0 - '@jest/transform@30.0.5': + '@jest/transform@30.1.2': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@jest/types': 30.0.5 - '@jridgewell/trace-mapping': 0.3.30 - babel-plugin-istanbul: 7.0.0 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 - jest-haste-map: 30.0.5 + jest-haste-map: 30.1.0 jest-regex-util: 30.0.1 jest-util: 30.0.5 micromatch: 4.0.8 @@ -2549,20 +2541,25 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.3.0 + '@types/node': 24.5.2 '@types/yargs': 17.0.33 chalk: 4.1.2 '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 @@ -2571,9 +2568,9 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 - '@tybys/wasm-util': 0.10.0 + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.1 optional: true '@nodelib/fs.scandir@2.1.5': @@ -2593,7 +2590,7 @@ snapshots: '@pkgr/core@0.2.9': {} - '@sinclair/typebox@0.34.40': {} + '@sinclair/typebox@0.34.41': {} '@sinonjs/commons@3.0.1': dependencies: @@ -2605,31 +2602,31 @@ snapshots: '@trysound/sax@0.2.0': {} - '@tybys/wasm-util@0.10.0': + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.4.0 optional: true '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/codemirror@5.60.8': dependencies: @@ -2649,14 +2646,14 @@ snapshots: '@types/jest@30.0.0': dependencies: - expect: 30.0.5 + expect: 30.1.2 pretty-format: 30.0.5 '@types/json-schema@7.0.15': {} - '@types/node@24.3.0': + '@types/node@24.5.2': dependencies: - undici-types: 7.10.0 + undici-types: 7.12.0 '@types/stack-utils@2.0.3': {} @@ -2670,15 +2667,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.39.1 - '@typescript-eslint/type-utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.39.1 - eslint: 9.33.0 + '@typescript-eslint/parser': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.44.0 + '@typescript-eslint/type-utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.0 + eslint: 9.35.0 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -2687,57 +2684,57 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2)': + '@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.39.1 - '@typescript-eslint/types': 8.39.1 - '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.39.1 - debug: 4.4.1 - eslint: 9.33.0 + '@typescript-eslint/scope-manager': 8.44.0 + '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.0 + debug: 4.4.3 + eslint: 9.35.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.39.1(typescript@5.9.2)': + '@typescript-eslint/project-service@8.44.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.9.2) - '@typescript-eslint/types': 8.39.1 - debug: 4.4.1 + '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.9.2) + '@typescript-eslint/types': 8.44.0 + debug: 4.4.3 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.39.1': + '@typescript-eslint/scope-manager@8.44.0': dependencies: - '@typescript-eslint/types': 8.39.1 - '@typescript-eslint/visitor-keys': 8.39.1 + '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/visitor-keys': 8.44.0 - '@typescript-eslint/tsconfig-utils@8.39.1(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.44.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.39.1(eslint@9.33.0)(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.44.0(eslint@9.35.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.39.1 - '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - debug: 4.4.1 - eslint: 9.33.0 + '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + debug: 4.4.3 + eslint: 9.35.0 ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.39.1': {} + '@typescript-eslint/types@8.44.0': {} - '@typescript-eslint/typescript-estree@8.39.1(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.44.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/project-service': 8.39.1(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.9.2) - '@typescript-eslint/types': 8.39.1 - '@typescript-eslint/visitor-keys': 8.39.1 - debug: 4.4.1 + '@typescript-eslint/project-service': 8.44.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.9.2) + '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/visitor-keys': 8.44.0 + debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -2747,20 +2744,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.39.1(eslint@9.33.0)(typescript@5.9.2)': + '@typescript-eslint/utils@8.44.0(eslint@9.35.0)(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0) - '@typescript-eslint/scope-manager': 8.39.1 - '@typescript-eslint/types': 8.39.1 - '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - eslint: 9.33.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0) + '@typescript-eslint/scope-manager': 8.44.0 + '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2) + eslint: 9.35.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.39.1': + '@typescript-eslint/visitor-keys@8.44.0': dependencies: - '@typescript-eslint/types': 8.39.1 + '@typescript-eslint/types': 8.44.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -2843,7 +2840,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.2.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: @@ -2851,7 +2848,7 @@ snapshots: ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} anymatch@3.1.3: dependencies: @@ -2864,20 +2861,20 @@ snapshots: argparse@2.0.1: {} - babel-jest@30.0.5(@babel/core@7.28.3): + babel-jest@30.1.2(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.3 - '@jest/transform': 30.0.5 + '@babel/core': 7.28.4 + '@jest/transform': 30.1.2 '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.0 - babel-preset-jest: 30.0.1(@babel/core@7.28.3) + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.0.1(@babel/core@7.28.4) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-plugin-istanbul@7.0.0: + babel-plugin-istanbul@7.0.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 '@istanbuljs/load-nyc-config': 1.1.0 @@ -2890,36 +2887,38 @@ snapshots: babel-plugin-jest-hoist@30.0.1: dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/babel__core': 7.20.5 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.3): - dependencies: - '@babel/core': 7.28.3 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.3) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.3) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.3) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.3) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.3) - - babel-preset-jest@30.0.1(@babel/core@7.28.3): - dependencies: - '@babel/core': 7.28.3 + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) + + babel-preset-jest@30.0.1(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 babel-plugin-jest-hoist: 30.0.1 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.3) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) balanced-match@1.0.2: {} + baseline-browser-mapping@2.8.6: {} + boolbase@1.0.0: {} brace-expansion@1.1.12: @@ -2935,12 +2934,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.2: + browserslist@4.26.2: dependencies: - caniuse-lite: 1.0.30001735 - electron-to-chromium: 1.5.203 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.2) + baseline-browser-mapping: 2.8.6 + caniuse-lite: 1.0.30001743 + electron-to-chromium: 1.5.222 + node-releases: 2.0.21 + update-browserslist-db: 1.1.3(browserslist@4.26.2) bs-logger@0.2.6: dependencies: @@ -2960,7 +2960,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001735: {} + caniuse-lite@1.0.30001743: {} chalk@4.1.2: dependencies: @@ -3022,11 +3022,11 @@ snapshots: dependencies: css-tree: 1.1.3 - debug@4.4.1: + debug@4.4.3: dependencies: ms: 2.1.3 - dedent@1.6.0: {} + dedent@1.7.0: {} deep-is@0.1.4: {} @@ -3054,7 +3054,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.203: {} + electron-to-chromium@1.5.222: {} emittery@0.13.1: {} @@ -3064,7 +3064,7 @@ snapshots: entities@2.2.0: {} - error-ex@1.3.2: + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -3072,34 +3072,34 @@ snapshots: dependencies: svgo: 2.8.0 - esbuild@0.25.9: + esbuild@0.25.10: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.9 - '@esbuild/android-arm': 0.25.9 - '@esbuild/android-arm64': 0.25.9 - '@esbuild/android-x64': 0.25.9 - '@esbuild/darwin-arm64': 0.25.9 - '@esbuild/darwin-x64': 0.25.9 - '@esbuild/freebsd-arm64': 0.25.9 - '@esbuild/freebsd-x64': 0.25.9 - '@esbuild/linux-arm': 0.25.9 - '@esbuild/linux-arm64': 0.25.9 - '@esbuild/linux-ia32': 0.25.9 - '@esbuild/linux-loong64': 0.25.9 - '@esbuild/linux-mips64el': 0.25.9 - '@esbuild/linux-ppc64': 0.25.9 - '@esbuild/linux-riscv64': 0.25.9 - '@esbuild/linux-s390x': 0.25.9 - '@esbuild/linux-x64': 0.25.9 - '@esbuild/netbsd-arm64': 0.25.9 - '@esbuild/netbsd-x64': 0.25.9 - '@esbuild/openbsd-arm64': 0.25.9 - '@esbuild/openbsd-x64': 0.25.9 - '@esbuild/openharmony-arm64': 0.25.9 - '@esbuild/sunos-x64': 0.25.9 - '@esbuild/win32-arm64': 0.25.9 - '@esbuild/win32-ia32': 0.25.9 - '@esbuild/win32-x64': 0.25.9 + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 escalade@3.2.0: {} @@ -3107,13 +3107,13 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(jest@30.0.5(@types/node@24.3.0))(typescript@5.9.2): + eslint-plugin-jest@29.0.1(@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(jest@30.1.3(@types/node@24.5.2))(typescript@5.9.2): dependencies: - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - eslint: 9.33.0 + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + eslint: 9.35.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2) - jest: 30.0.5(@types/node@24.3.0) + '@typescript-eslint/eslint-plugin': 8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2) + jest: 30.1.3(@types/node@24.5.2) transitivePeerDependencies: - supports-color - typescript @@ -3127,17 +3127,17 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.33.0: + eslint@9.35.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.33.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.1 '@eslint/core': 0.15.2 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.33.0 + '@eslint/js': 9.35.0 '@eslint/plugin-kit': 0.3.5 - '@humanfs/node': 0.16.6 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 @@ -3145,7 +3145,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -3201,12 +3201,12 @@ snapshots: exit-x@0.2.2: {} - expect@30.0.5: + expect@30.1.2: dependencies: - '@jest/expect-utils': 30.0.5 - '@jest/get-type': 30.0.1 - jest-matcher-utils: 30.0.5 - jest-message-util: 30.0.5 + '@jest/expect-utils': 30.1.2 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.1.2 + jest-message-util: 30.1.0 jest-mock: 30.0.5 jest-util: 30.0.5 @@ -3303,7 +3303,7 @@ snapshots: globals@14.0.0: {} - globals@16.3.0: {} + globals@16.4.0: {} graceful-fs@4.2.11: {} @@ -3369,8 +3369,8 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.2 @@ -3385,8 +3385,8 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: - '@jridgewell/trace-mapping': 0.3.30 - debug: 4.4.1 + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -3408,22 +3408,22 @@ snapshots: jest-util: 30.0.5 p-limit: 3.1.0 - jest-circus@30.0.5: + jest-circus@30.1.3: dependencies: - '@jest/environment': 30.0.5 - '@jest/expect': 30.0.5 - '@jest/test-result': 30.0.5 + '@jest/environment': 30.1.2 + '@jest/expect': 30.1.2 + '@jest/test-result': 30.1.3 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 chalk: 4.1.2 co: 4.6.0 - dedent: 1.6.0 + dedent: 1.7.0 is-generator-fn: 2.1.0 - jest-each: 30.0.5 - jest-matcher-utils: 30.0.5 - jest-message-util: 30.0.5 - jest-runtime: 30.0.5 - jest-snapshot: 30.0.5 + jest-each: 30.1.0 + jest-matcher-utils: 30.1.2 + jest-message-util: 30.1.0 + jest-runtime: 30.1.3 + jest-snapshot: 30.1.2 jest-util: 30.0.5 p-limit: 3.1.0 pretty-format: 30.0.5 @@ -3434,17 +3434,17 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.0.5(@types/node@24.3.0): + jest-cli@30.1.3(@types/node@24.5.2): dependencies: - '@jest/core': 30.0.5 - '@jest/test-result': 30.0.5 + '@jest/core': 30.1.3 + '@jest/test-result': 30.1.3 '@jest/types': 30.0.5 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.0.5(@types/node@24.3.0) + jest-config: 30.1.3(@types/node@24.5.2) jest-util: 30.0.5 - jest-validate: 30.0.5 + jest-validate: 30.1.0 yargs: 17.7.2 transitivePeerDependencies: - '@types/node' @@ -3453,42 +3453,42 @@ snapshots: - supports-color - ts-node - jest-config@30.0.5(@types/node@24.3.0): + jest-config@30.1.3(@types/node@24.5.2): dependencies: - '@babel/core': 7.28.3 - '@jest/get-type': 30.0.1 + '@babel/core': 7.28.4 + '@jest/get-type': 30.1.0 '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.0.5 + '@jest/test-sequencer': 30.1.3 '@jest/types': 30.0.5 - babel-jest: 30.0.5(@babel/core@7.28.3) + babel-jest: 30.1.2(@babel/core@7.28.4) chalk: 4.1.2 ci-info: 4.3.0 deepmerge: 4.3.1 glob: 10.4.5 graceful-fs: 4.2.11 - jest-circus: 30.0.5 + jest-circus: 30.1.3 jest-docblock: 30.0.1 - jest-environment-node: 30.0.5 + jest-environment-node: 30.1.2 jest-regex-util: 30.0.1 - jest-resolve: 30.0.5 - jest-runner: 30.0.5 + jest-resolve: 30.1.3 + jest-runner: 30.1.3 jest-util: 30.0.5 - jest-validate: 30.0.5 + jest-validate: 30.1.0 micromatch: 4.0.8 parse-json: 5.2.0 pretty-format: 30.0.5 slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 24.3.0 + '@types/node': 24.5.2 transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-diff@30.0.5: + jest-diff@30.1.2: dependencies: '@jest/diff-sequences': 30.0.1 - '@jest/get-type': 30.0.1 + '@jest/get-type': 30.1.0 chalk: 4.1.2 pretty-format: 30.0.5 @@ -3496,52 +3496,52 @@ snapshots: dependencies: detect-newline: 3.1.0 - jest-each@30.0.5: + jest-each@30.1.0: dependencies: - '@jest/get-type': 30.0.1 + '@jest/get-type': 30.1.0 '@jest/types': 30.0.5 chalk: 4.1.2 jest-util: 30.0.5 pretty-format: 30.0.5 - jest-environment-node@30.0.5: + jest-environment-node@30.1.2: dependencies: - '@jest/environment': 30.0.5 - '@jest/fake-timers': 30.0.5 + '@jest/environment': 30.1.2 + '@jest/fake-timers': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 jest-mock: 30.0.5 jest-util: 30.0.5 - jest-validate: 30.0.5 + jest-validate: 30.1.0 - jest-haste-map@30.0.5: + jest-haste-map@30.1.0: dependencies: '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 jest-regex-util: 30.0.1 jest-util: 30.0.5 - jest-worker: 30.0.5 + jest-worker: 30.1.0 micromatch: 4.0.8 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - jest-leak-detector@30.0.5: + jest-leak-detector@30.1.0: dependencies: - '@jest/get-type': 30.0.1 + '@jest/get-type': 30.1.0 pretty-format: 30.0.5 - jest-matcher-utils@30.0.5: + jest-matcher-utils@30.1.2: dependencies: - '@jest/get-type': 30.0.1 + '@jest/get-type': 30.1.0 chalk: 4.1.2 - jest-diff: 30.0.5 + jest-diff: 30.1.2 pretty-format: 30.0.5 - jest-message-util@30.0.5: + jest-message-util@30.1.0: dependencies: '@babel/code-frame': 7.27.1 '@jest/types': 30.0.5 @@ -3556,106 +3556,106 @@ snapshots: jest-mock@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 jest-util: 30.0.5 - jest-pnp-resolver@1.2.3(jest-resolve@30.0.5): + jest-pnp-resolver@1.2.3(jest-resolve@30.1.3): optionalDependencies: - jest-resolve: 30.0.5 + jest-resolve: 30.1.3 jest-regex-util@30.0.1: {} - jest-resolve-dependencies@30.0.5: + jest-resolve-dependencies@30.1.3: dependencies: jest-regex-util: 30.0.1 - jest-snapshot: 30.0.5 + jest-snapshot: 30.1.2 transitivePeerDependencies: - supports-color - jest-resolve@30.0.5: + jest-resolve@30.1.3: dependencies: chalk: 4.1.2 graceful-fs: 4.2.11 - jest-haste-map: 30.0.5 - jest-pnp-resolver: 1.2.3(jest-resolve@30.0.5) + jest-haste-map: 30.1.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.1.3) jest-util: 30.0.5 - jest-validate: 30.0.5 + jest-validate: 30.1.0 slash: 3.0.0 unrs-resolver: 1.11.1 - jest-runner@30.0.5: + jest-runner@30.1.3: dependencies: - '@jest/console': 30.0.5 - '@jest/environment': 30.0.5 - '@jest/test-result': 30.0.5 - '@jest/transform': 30.0.5 + '@jest/console': 30.1.2 + '@jest/environment': 30.1.2 + '@jest/test-result': 30.1.3 + '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-docblock: 30.0.1 - jest-environment-node: 30.0.5 - jest-haste-map: 30.0.5 - jest-leak-detector: 30.0.5 - jest-message-util: 30.0.5 - jest-resolve: 30.0.5 - jest-runtime: 30.0.5 + jest-environment-node: 30.1.2 + jest-haste-map: 30.1.0 + jest-leak-detector: 30.1.0 + jest-message-util: 30.1.0 + jest-resolve: 30.1.3 + jest-runtime: 30.1.3 jest-util: 30.0.5 - jest-watcher: 30.0.5 - jest-worker: 30.0.5 + jest-watcher: 30.1.3 + jest-worker: 30.1.0 p-limit: 3.1.0 source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - jest-runtime@30.0.5: + jest-runtime@30.1.3: dependencies: - '@jest/environment': 30.0.5 - '@jest/fake-timers': 30.0.5 - '@jest/globals': 30.0.5 + '@jest/environment': 30.1.2 + '@jest/fake-timers': 30.1.2 + '@jest/globals': 30.1.2 '@jest/source-map': 30.0.1 - '@jest/test-result': 30.0.5 - '@jest/transform': 30.0.5 + '@jest/test-result': 30.1.3 + '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 chalk: 4.1.2 cjs-module-lexer: 2.1.0 collect-v8-coverage: 1.0.2 glob: 10.4.5 graceful-fs: 4.2.11 - jest-haste-map: 30.0.5 - jest-message-util: 30.0.5 + jest-haste-map: 30.1.0 + jest-message-util: 30.1.0 jest-mock: 30.0.5 jest-regex-util: 30.0.1 - jest-resolve: 30.0.5 - jest-snapshot: 30.0.5 + jest-resolve: 30.1.3 + jest-snapshot: 30.1.2 jest-util: 30.0.5 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@30.0.5: + jest-snapshot@30.1.2: dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.4 '@babel/generator': 7.28.3 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.3) - '@babel/types': 7.28.2 - '@jest/expect-utils': 30.0.5 - '@jest/get-type': 30.0.1 - '@jest/snapshot-utils': 30.0.5 - '@jest/transform': 30.0.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 + '@jest/expect-utils': 30.1.2 + '@jest/get-type': 30.1.0 + '@jest/snapshot-utils': 30.1.2 + '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.3) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) chalk: 4.1.2 - expect: 30.0.5 + expect: 30.1.2 graceful-fs: 4.2.11 - jest-diff: 30.0.5 - jest-matcher-utils: 30.0.5 - jest-message-util: 30.0.5 + jest-diff: 30.1.2 + jest-matcher-utils: 30.1.2 + jest-message-util: 30.1.0 jest-util: 30.0.5 pretty-format: 30.0.5 semver: 7.7.2 @@ -3666,46 +3666,46 @@ snapshots: jest-util@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 chalk: 4.1.2 ci-info: 4.3.0 graceful-fs: 4.2.11 picomatch: 4.0.3 - jest-validate@30.0.5: + jest-validate@30.1.0: dependencies: - '@jest/get-type': 30.0.1 + '@jest/get-type': 30.1.0 '@jest/types': 30.0.5 camelcase: 6.3.0 chalk: 4.1.2 leven: 3.1.0 pretty-format: 30.0.5 - jest-watcher@30.0.5: + jest-watcher@30.1.3: dependencies: - '@jest/test-result': 30.0.5 + '@jest/test-result': 30.1.3 '@jest/types': 30.0.5 - '@types/node': 24.3.0 + '@types/node': 24.5.2 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 jest-util: 30.0.5 string-length: 4.0.2 - jest-worker@30.0.5: + jest-worker@30.1.0: dependencies: - '@types/node': 24.3.0 + '@types/node': 24.5.2 '@ungap/structured-clone': 1.3.0 jest-util: 30.0.5 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.0.5(@types/node@24.3.0): + jest@30.1.3(@types/node@24.5.2): dependencies: - '@jest/core': 30.0.5 + '@jest/core': 30.1.3 '@jest/types': 30.0.5 import-local: 3.2.0 - jest-cli: 30.0.5(@types/node@24.3.0) + jest-cli: 30.1.3(@types/node@24.5.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -3814,7 +3814,7 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.19: {} + node-releases@2.0.21: {} normalize-path@3.0.0: {} @@ -3877,7 +3877,7 @@ snapshots: parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 - error-ex: 1.3.2 + error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -3982,15 +3982,15 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.2.0 + ansi-regex: 6.2.2 strip-bom@4.0.0: {} @@ -4038,12 +4038,12 @@ snapshots: dependencies: typescript: 5.9.2 - ts-jest@29.4.1(@babel/core@7.28.3)(@jest/transform@30.0.5)(@jest/types@30.0.5)(babel-jest@30.0.5(@babel/core@7.28.3))(esbuild@0.25.9)(jest-util@30.0.5)(jest@30.0.5(@types/node@24.3.0))(typescript@5.9.2): + ts-jest@29.4.3(@babel/core@7.28.4)(@jest/transform@30.1.2)(@jest/types@30.0.5)(babel-jest@30.1.2(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@30.0.5)(jest@30.1.3(@types/node@24.5.2))(typescript@5.9.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 30.0.5(@types/node@24.3.0) + jest: 30.1.3(@types/node@24.5.2) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -4052,11 +4052,11 @@ snapshots: typescript: 5.9.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.3 - '@jest/transform': 30.0.5 + '@babel/core': 7.28.4 + '@jest/transform': 30.1.2 '@jest/types': 30.0.5 - babel-jest: 30.0.5(@babel/core@7.28.3) - esbuild: 0.25.9 + babel-jest: 30.1.2(@babel/core@7.28.4) + esbuild: 0.25.10 jest-util: 30.0.5 tslib@2.4.0: {} @@ -4071,13 +4071,13 @@ snapshots: type-fest@4.41.0: {} - typescript-eslint@8.39.1(eslint@9.33.0)(typescript@5.9.2): + typescript-eslint@8.44.0(eslint@9.35.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.9.2))(eslint@9.33.0)(typescript@5.9.2) - '@typescript-eslint/parser': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.9.2) - '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.9.2) - eslint: 9.33.0 + '@typescript-eslint/eslint-plugin': 8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.35.0)(typescript@5.9.2))(eslint@9.35.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.0(eslint@9.35.0)(typescript@5.9.2) + eslint: 9.35.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -4087,7 +4087,7 @@ snapshots: uglify-js@3.19.3: optional: true - undici-types@7.10.0: {} + undici-types@7.12.0: {} unrs-resolver@1.11.1: dependencies: @@ -4113,9 +4113,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.3(browserslist@4.25.2): + update-browserslist-db@1.1.3(browserslist@4.26.2): dependencies: - browserslist: 4.25.2 + browserslist: 4.26.2 escalade: 3.2.0 picocolors: 1.1.1 @@ -4125,7 +4125,7 @@ snapshots: v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -4151,9 +4151,9 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} diff --git a/src/components/SettingsTab.ts b/src/components/SettingsTab.ts index ba12225..b70a8a6 100644 --- a/src/components/SettingsTab.ts +++ b/src/components/SettingsTab.ts @@ -16,14 +16,22 @@ export class PomodoroSettingTab extends PluginSettingTab { private validateAndUpdateSetting( value: string, - settingProperty: 'workTime' | 'shortBreakTime' | 'longBreakTime' | 'intervalsBeforeLongBreak', - resetAction: 'resetTimer' | 'resetPomodoroSession' + settingProperty: + | "workMinutes" + | "shortBreakMinutes" + | "longBreakMinutes" + | "intervalsBeforeLongBreak", + resetAction: "resetTimer" | "resetPomodoroSession" ): boolean { const numValue = parseInt(value.trim()); - if (!isNaN(numValue) && numValue > 0 && Number.isInteger(Number(value.trim()))) { + if ( + !isNaN(numValue) && + numValue > 0 && + Number.isInteger(Number(value.trim())) + ) { this.plugin.settings[settingProperty] = numValue; this.plugin.saveSettings(); - if (resetAction === 'resetTimer') { + if (resetAction === "resetTimer") { this.plugin.resetTimer(); } else { this.plugin.resetPomodoroSession(); @@ -35,7 +43,9 @@ export class PomodoroSettingTab extends PluginSettingTab { private updateCustomSoundVisibility(show: boolean): void { if (this.customSoundSetting && this.customSoundSetting.settingEl) { - this.customSoundSetting.settingEl.style.display = show ? '' : 'none'; + this.customSoundSetting.settingEl.style.display = show + ? "" + : "none"; } } @@ -49,140 +59,217 @@ export class PomodoroSettingTab extends PluginSettingTab { new Setting(containerEl) .setName("Work Duration") .setDesc("Duration of the work timer in minutes.") - .addText(text => text - .setPlaceholder("e.g., 25") - .setValue(this.plugin.settings.workTime.toString()) - .onChange(async (value) => { - await this.validateAndUpdateSetting(value, 'workTime', 'resetTimer'); - })); + .addText((text) => + text + .setPlaceholder("e.g., 25") + .setValue(this.plugin.settings.workMinutes.toString()) + .onChange(async (value) => { + await this.validateAndUpdateSetting( + value, + "workMinutes", + "resetTimer" + ); + }) + ); new Setting(containerEl) .setName("Short Break Duration") .setDesc("Duration of the short break timer in minutes.") - .addText(text => text - .setPlaceholder("e.g., 5") - .setValue(this.plugin.settings.shortBreakTime.toString()) - .onChange(async (value) => { - await this.validateAndUpdateSetting(value, 'shortBreakTime', 'resetTimer'); - })); + .addText((text) => + text + .setPlaceholder("e.g., 5") + .setValue(this.plugin.settings.shortBreakMinutes.toString()) + .onChange(async (value) => { + await this.validateAndUpdateSetting( + value, + "shortBreakMinutes", + "resetTimer" + ); + }) + ); new Setting(containerEl) .setName("Long Break Duration") .setDesc("Duration of the long break timer in minutes.") - .addText(text => text - .setPlaceholder("e.g., 15") - .setValue(this.plugin.settings.longBreakTime.toString()) - .onChange(async (value) => { - await this.validateAndUpdateSetting(value, 'longBreakTime', 'resetTimer'); - })); + .addText((text) => + text + .setPlaceholder("e.g., 15") + .setValue(this.plugin.settings.longBreakMinutes.toString()) + .onChange(async (value) => { + await this.validateAndUpdateSetting( + value, + "longBreakMinutes", + "resetTimer" + ); + }) + ); new Setting(containerEl) .setName("Intervals Before Long Break") - .setDesc("Number of work intervals before a long break is triggered.") - .addText(text => text - .setPlaceholder("e.g., 4") - .setValue(this.plugin.settings.intervalsBeforeLongBreak.toString()) - .onChange(async (value) => { - await this.validateAndUpdateSetting(value, 'intervalsBeforeLongBreak', 'resetPomodoroSession'); - })); + .setDesc( + "Number of work intervals before a long break is triggered." + ) + .addText((text) => + text + .setPlaceholder("e.g., 4") + .setValue( + this.plugin.settings.intervalsBeforeLongBreak.toString() + ) + .onChange(async (value) => { + await this.validateAndUpdateSetting( + value, + "intervalsBeforeLongBreak", + "resetPomodoroSession" + ); + }) + ); new Setting(containerEl) .setName("Auto-start Next Timer") - .setDesc("Automatically start the next timer in the cycle when the current timer completes. When disabled, timers pause after completion.") - .addToggle(toggle => toggle - .setValue(this.plugin.settings.autoProgressEnabled) - .onChange(async (value) => { - this.plugin.settings.autoProgressEnabled = value; - await this.plugin.saveSettings(); - })); + .setDesc( + "Automatically start the next timer in the cycle when the current timer completes. When disabled, timers pause after completion." + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.autoProgressEnabled) + .onChange(async (value) => { + this.plugin.settings.autoProgressEnabled = value; + value + ? (this.plugin.settings.persistentNotification = + false) + : null; + await this.plugin.saveSettings(); + this.display(); + }) + ); new Setting(containerEl) .setName("Show Timer Icon") - .setDesc("Display a timer icon next to the countdown in the status bar.") - .addToggle(toggle => toggle - .setValue(this.plugin.settings.showIcon) - .onChange(async (value) => { - this.plugin.settings.showIcon = value; - await this.plugin.saveSettings(); - })); + .setDesc( + "Display a timer icon next to the countdown in the status bar." + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.showIcon) + .onChange(async (value) => { + this.plugin.settings.showIcon = value; + await this.plugin.saveSettings(); + }) + ); new Setting(containerEl) .setName("Show in Status Bar") - .setDesc("Toggle the timer's visibility in the status bar. You can also use the 'Toggle status bar visibility' command.") - .addToggle(toggle => toggle - .setValue(this.plugin.settings.showInStatusBar) - .onChange(async (value) => { - this.plugin.settings.showInStatusBar = value; - await this.plugin.saveSettings(); - })); + .setDesc( + "Toggle the timer's visibility in the status bar. You can also use the 'Toggle status bar visibility' command." + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.showInStatusBar) + .onChange(async (value) => { + this.plugin.settings.showInStatusBar = value; + await this.plugin.saveSettings(); + }) + ); new Setting(containerEl).setName("Sound Notifications").setHeading(); new Setting(containerEl) .setName("Enable Sound Notifications") .setDesc("Play a sound when timers complete.") - .addToggle(toggle => toggle - .setValue(this.plugin.settings.soundEnabled) - .onChange(async (value) => { - this.plugin.settings.soundEnabled = value; - await this.plugin.saveSettings(); - })); + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.soundEnabled) + .onChange(async (value) => { + this.plugin.settings.soundEnabled = value; + await this.plugin.saveSettings(); + }) + ); + + new Setting(containerEl) + .setName("Persistent Notification") + .setDesc( + "Play sound continously, until timer is continued or reset" + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.persistentNotification) + .onChange(async (value) => { + this.plugin.settings.persistentNotification = value; + value + ? (this.plugin.settings.autoProgressEnabled = false) + : null; + await this.plugin.saveSettings(); + this.display(); + }) + ); new Setting(containerEl) .setName("Sound Selection") - .setDesc("Choose a built-in sound or select 'custom' to use your own.") - .addDropdown(dropdown => { + .setDesc( + "Choose a built-in sound or select 'custom' to use your own." + ) + .addDropdown((dropdown) => { const builtInSounds = this.soundManager.getBuiltInSounds(); - builtInSounds.forEach(sound => { - dropdown.addOption(sound, sound.replace('.wav', '')); + builtInSounds.forEach((sound) => { + dropdown.addOption(sound, sound.replace(".wav", "")); }); dropdown.addOption("custom", "Custom"); - + // Determine current selection - const currentSelection = this.plugin.settings.customSoundUrl?.trim() ? "custom" : this.plugin.settings.selectedSound; - dropdown - .setValue(currentSelection) - .onChange(async (value) => { - if (value === "custom") { - // Don't change selectedSound when switching to custom - this.updateCustomSoundVisibility(true); - } else { - this.plugin.settings.selectedSound = value; - this.plugin.settings.customSoundUrl = ""; - this.updateCustomSoundVisibility(false); - await this.plugin.saveSettings(); - } - }); + const currentSelection = + this.plugin.settings.customSoundUrl?.trim() + ? "custom" + : this.plugin.settings.selectedSound; + dropdown.setValue(currentSelection).onChange(async (value) => { + if (value === "custom") { + // Don't change selectedSound when switching to custom + this.updateCustomSoundVisibility(true); + } else { + this.plugin.settings.selectedSound = value; + this.plugin.settings.customSoundUrl = ""; + this.updateCustomSoundVisibility(false); + await this.plugin.saveSettings(); + } + }); }) - .addButton(button => button - .setButtonText("Preview") - .onClick(async () => { + .addButton((button) => + button.setButtonText("Preview").onClick(async () => { try { - const isCustomSelected = this.plugin.settings.customSoundUrl?.trim(); + const isCustomSelected = + this.plugin.settings.customSoundUrl?.trim(); if (isCustomSelected) { - await this.soundManager.previewSound(this.plugin.settings.customSoundUrl!.trim()); + await this.soundManager.previewSound( + this.plugin.settings.customSoundUrl!.trim() + ); } else { - await this.soundManager.previewSound(this.plugin.settings.selectedSound); + await this.soundManager.previewSound( + this.plugin.settings.selectedSound + ); } } catch (error) { console.warn("Preview failed:", error); } - })); + }) + ); const customSoundSetting = new Setting(containerEl) .setName("Custom Sound URL/Path") .setDesc("Use a custom sound file from your vault or a URL.") - .addText(text => text - .setPlaceholder("e.g., MyFolder/custom-sound.mp3 or https://example.com/sound.wav") - .setValue(this.plugin.settings.customSoundUrl || "") - .onChange(async (value) => { - this.plugin.settings.customSoundUrl = value.trim(); - await this.plugin.saveSettings(); - })); + .addText((text) => + text + .setPlaceholder( + "e.g., MyFolder/custom-sound.mp3 or https://example.com/sound.wav" + ) + .setValue(this.plugin.settings.customSoundUrl || "") + .onChange(async (value) => { + this.plugin.settings.customSoundUrl = value.trim(); + await this.plugin.saveSettings(); + }) + ); // Store references for dynamic visibility control this.customSoundSetting = customSoundSetting; - + // Set initial visibility const shouldShowCustom = this.plugin.settings.customSoundUrl?.trim(); this.updateCustomSoundVisibility(!!shouldShowCustom); @@ -190,27 +277,34 @@ export class PomodoroSettingTab extends PluginSettingTab { new Setting(containerEl) .setName("Volume") .setDesc("Adjust the volume for sound notifications (0-100%).") - .addSlider(slider => slider - .setLimits(0, 1, 0.1) - .setValue(this.plugin.settings.soundVolume) - .setDynamicTooltip() - .onChange(async (value) => { - this.plugin.settings.soundVolume = value; - await this.plugin.saveSettings(); - })) - .addButton(button => button - .setButtonText("Test Volume") - .onClick(async () => { + .addSlider((slider) => + slider + .setLimits(0, 1, 0.1) + .setValue(this.plugin.settings.soundVolume) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.soundVolume = value; + await this.plugin.saveSettings(); + }) + ) + .addButton((button) => + button.setButtonText("Test Volume").onClick(async () => { try { - const isCustomSelected = this.plugin.settings.customSoundUrl?.trim(); + const isCustomSelected = + this.plugin.settings.customSoundUrl?.trim(); if (isCustomSelected) { - await this.soundManager.previewSound(this.plugin.settings.customSoundUrl!.trim()); + await this.soundManager.previewSound( + this.plugin.settings.customSoundUrl!.trim() + ); } else { - await this.soundManager.previewSound(this.plugin.settings.selectedSound); + await this.soundManager.previewSound( + this.plugin.settings.selectedSound + ); } } catch (error) { console.warn("Volume test failed:", error); } - })); + }) + ); } -} \ No newline at end of file +} diff --git a/src/constants.ts b/src/constants.ts index 42ad882..86f0135 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -21,4 +21,4 @@ export const MOUSE_BUTTONS = { LEFT_CLICK: 0, MIDDLE_CLICK: 1, RIGHT_CLICK: 2, -} as const; \ No newline at end of file +} as const; diff --git a/src/icons.ts b/src/icons.ts index 8c37fea..7e8b534 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,14 +1,13 @@ -import timerIcon from './assets/icons/timer.svg'; -import timerResetIcon from './assets/icons/timer-reset.svg'; -import timerOffIcon from './assets/icons/timer-off.svg'; -import timerPauseIcon from './assets/icons/timer-pause.svg'; -import timerPlayIcon from './assets/icons/timer-play.svg'; - +import timerIcon from "./assets/icons/timer.svg"; +import timerResetIcon from "./assets/icons/timer-reset.svg"; +import timerOffIcon from "./assets/icons/timer-off.svg"; +import timerPauseIcon from "./assets/icons/timer-pause.svg"; +import timerPlayIcon from "./assets/icons/timer-play.svg"; export const ICONS_MAP: Record = { - 'pomobar-timer': timerIcon, - 'pomobar-timer-reset': timerResetIcon, - 'pomobar-timer-off': timerOffIcon, - 'pomobar-timer-pause': timerPauseIcon, - 'pomobar-timer-play': timerPlayIcon, -}; \ No newline at end of file + "pomobar-timer": timerIcon, + "pomobar-timer-reset": timerResetIcon, + "pomobar-timer-off": timerOffIcon, + "pomobar-timer-pause": timerPauseIcon, + "pomobar-timer-play": timerPlayIcon, +}; diff --git a/src/logic/soundManager.ts b/src/logic/soundManager.ts index 9c8f062..32398da 100644 --- a/src/logic/soundManager.ts +++ b/src/logic/soundManager.ts @@ -6,13 +6,24 @@ export class SoundManager { private settings: PomodoroSettings; private currentAudio: HTMLAudioElement | null = null; private blobUrls: string[] = []; - private readonly builtInSounds = ["chime.wav", "correct.wav", "ding.wav", "jingle.wav", "triangle.wav"]; + private readonly builtInSounds = [ + "chime.wav", + "correct.wav", + "ding.wav", + "jingle.wav", + "triangle.wav", + ]; private readonly cdnUrls: Record = { - "ding.wav": "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/ding.wav", - "chime.wav": "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/chime.wav", - "correct.wav": "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/correct.wav", - "jingle.wav": "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/jingle.wav", - "triangle.wav": "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/triangle.wav" + "ding.wav": + "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/ding.wav", + "chime.wav": + "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/chime.wav", + "correct.wav": + "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/correct.wav", + "jingle.wav": + "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/jingle.wav", + "triangle.wav": + "https://cdn.jsdelivr.net/gh/semanticdata/dotfiles@latest/assets/triangle.wav", }; constructor(plugin: Plugin, settings: PomodoroSettings) { @@ -28,14 +39,18 @@ export class SoundManager { if (this.isBuiltInSound(soundName)) { return this.cdnUrls[soundName] || soundName; } - + // Handle vault-relative paths for custom sounds - if (!soundName.startsWith('http://') && !soundName.startsWith('https://')) { + if ( + !soundName.startsWith("http://") && + !soundName.startsWith("https://") + ) { try { // Read the file from the vault as binary data - const arrayBuffer = await this.plugin.app.vault.adapter.readBinary(soundName); + const arrayBuffer = + await this.plugin.app.vault.adapter.readBinary(soundName); // Determine MIME type based on file extension - const extension = soundName.split('.').pop()?.toLowerCase(); + const extension = soundName.split(".").pop()?.toLowerCase(); const mimeType = this.getMimeType(extension); // Create a blob URL for the audio data const blob = new Blob([arrayBuffer], { type: mimeType }); @@ -43,12 +58,15 @@ export class SoundManager { this.blobUrls.push(blobUrl); return blobUrl; } catch (error) { - console.error(`Failed to load vault audio file: ${soundName}`, error); + console.error( + `Failed to load vault audio file: ${soundName}`, + error + ); // Fall back to the original path return soundName; } } - + return soundName; } @@ -58,12 +76,18 @@ export class SoundManager { private getMimeType(extension?: string): string { switch (extension) { - case 'wav': return 'audio/wav'; - case 'mp3': return 'audio/mpeg'; - case 'ogg': return 'audio/ogg'; - case 'm4a': return 'audio/mp4'; - case 'webm': return 'audio/webm'; - default: return 'audio/wav'; + case "wav": + return "audio/wav"; + case "mp3": + return "audio/mpeg"; + case "ogg": + return "audio/ogg"; + case "m4a": + return "audio/mp4"; + case "webm": + return "audio/webm"; + default: + return "audio/wav"; } } @@ -71,24 +95,32 @@ export class SoundManager { const audio = new Audio(); audio.volume = this.settings.soundVolume; audio.preload = "auto"; - + return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { reject(new Error("Audio loading timeout")); }, 5000); - audio.addEventListener("canplaythrough", () => { - clearTimeout(timeoutId); - resolve(audio); - }, { once: true }); - - audio.addEventListener("error", () => { - clearTimeout(timeoutId); - reject(new Error(`Failed to load audio: ${soundUrl}`)); - }, { once: true }); + audio.addEventListener( + "canplaythrough", + () => { + clearTimeout(timeoutId); + resolve(audio); + }, + { once: true } + ); + + audio.addEventListener( + "error", + () => { + clearTimeout(timeoutId); + reject(new Error(`Failed to load audio: ${soundUrl}`)); + }, + { once: true } + ); audio.src = soundUrl; - }).then(loadedAudio => { + }).then((loadedAudio) => { this.currentAudio = loadedAudio; return loadedAudio; }); @@ -98,11 +130,12 @@ export class SoundManager { if (!this.settings.soundEnabled) { return; } - try { - const soundToPlay = this.settings.customSoundUrl && this.settings.customSoundUrl.trim() !== "" - ? this.settings.customSoundUrl.trim() - : this.settings.selectedSound; + const soundToPlay = + this.settings.customSoundUrl && + this.settings.customSoundUrl.trim() !== "" + ? this.settings.customSoundUrl.trim() + : this.settings.selectedSound; await this.playSound(soundToPlay); } catch { @@ -116,10 +149,11 @@ export class SoundManager { await audio.play(); } - async previewSound(soundName?: string): Promise { - const soundToPreview = soundName || - (this.settings.customSoundUrl && this.settings.customSoundUrl.trim() !== "" + const soundToPreview = + soundName || + (this.settings.customSoundUrl && + this.settings.customSoundUrl.trim() !== "" ? this.settings.customSoundUrl.trim() : this.settings.selectedSound); @@ -141,7 +175,7 @@ export class SoundManager { cleanup(): void { this.stopCurrentAudio(); // Clean up blob URLs to prevent memory leaks - this.blobUrls.forEach(url => URL.revokeObjectURL(url)); + this.blobUrls.forEach((url) => URL.revokeObjectURL(url)); this.blobUrls = []; } -} \ No newline at end of file +} diff --git a/src/logic/timer.ts b/src/logic/timer.ts index 99a3460..2cd90d4 100644 --- a/src/logic/timer.ts +++ b/src/logic/timer.ts @@ -1,7 +1,12 @@ -import { Plugin, Notice } from "obsidian"; +import { moment, Plugin, Notice } from "obsidian"; import { PomodoroSettings } from "../types"; import { ICONS_MAP } from "../icons"; -import { TIMER_STATES, TIMER_INTERVAL_MS, CSS_CLASSES, MOUSE_BUTTONS } from "../constants"; +import { + TIMER_STATES, + TIMER_INTERVAL_MS, + CSS_CLASSES, + MOUSE_BUTTONS, +} from "../constants"; import { SoundManager } from "./soundManager"; export class PomodoroTimer { @@ -9,14 +14,22 @@ export class PomodoroTimer { private settings: PomodoroSettings; private statusBarItem: HTMLElement; private soundManager: SoundManager; - private remainingTime = 0; - private isRunning = false; + private timeEnd: moment.Moment | moment.Duration | null = null; + // Type based state machine: + // null = OFF + // Moment = running until (Utc EpochTimeStamp) + // Duration = paused state, ready for action (Miliseconds) private currentDurationIndex = 0; private workIntervalCount = 0; private currentInterval: number | null = null; private registeredIntervals: Set = new Set(); - constructor(plugin: Plugin, settings: PomodoroSettings, statusBarItem: HTMLElement, soundManager: SoundManager) { + constructor( + plugin: Plugin, + settings: PomodoroSettings, + statusBarItem: HTMLElement, + soundManager: SoundManager + ) { this.plugin = plugin; this.settings = settings; this.statusBarItem = statusBarItem; @@ -31,7 +44,7 @@ export class PomodoroTimer { // Create icon container - will be shown/hidden based on settings const iconContainer = document.createElement("span"); iconContainer.classList.add(CSS_CLASSES.ICON); - iconContainer.innerHTML = ICONS_MAP['pomobar-timer']; + iconContainer.innerHTML = ICONS_MAP["pomobar-timer"]; this.statusBarItem.appendChild(iconContainer); // Create text container @@ -45,34 +58,48 @@ export class PomodoroTimer { this.updateIcon(); // Event listeners - this.plugin.registerDomEvent(this.statusBarItem, 'click', (e: MouseEvent) => { - if (e.button === MOUSE_BUTTONS.LEFT_CLICK) { - this.isRunning ? this.pauseTimer() : this.startTimer(); + this.plugin.registerDomEvent( + this.statusBarItem, + "click", + (e: MouseEvent) => { + if (e.button === MOUSE_BUTTONS.LEFT_CLICK) { + this.toggleTimer(); + } } - }); - - this.plugin.registerDomEvent(this.statusBarItem, 'auxclick', (e: MouseEvent) => { - if (e.button === MOUSE_BUTTONS.MIDDLE_CLICK) { - this.cycleDuration(); + ); + + this.plugin.registerDomEvent( + this.statusBarItem, + "auxclick", + (e: MouseEvent) => { + if (e.button === MOUSE_BUTTONS.MIDDLE_CLICK) { + this.cycleDuration(); + } } - }); - - this.plugin.registerDomEvent(this.statusBarItem, 'contextmenu', (e: MouseEvent) => { - e.preventDefault(); - if (!this.isRunning) { - this.resetTimer(); + ); + + this.plugin.registerDomEvent( + this.statusBarItem, + "contextmenu", + (e: MouseEvent) => { + e.preventDefault(); + if (!this.isRunning) { + this.resetTimer(); + } } - }); + ); } private updateIconVisibility() { - const iconContainer = this.statusBarItem.querySelector(`.${CSS_CLASSES.ICON}`) as HTMLElement; + const iconContainer = this.statusBarItem.querySelector( + `.${CSS_CLASSES.ICON}` + ) as HTMLElement; if (iconContainer) { if (this.settings.showIcon) { - iconContainer.style.display = ''; + iconContainer.style.display = ""; this.statusBarItem.classList.remove(CSS_CLASSES.NO_ICON); } else { - iconContainer.style.display = 'none'; + iconContainer.style.display = "none"; this.statusBarItem.classList.add(CSS_CLASSES.NO_ICON); } } @@ -80,49 +107,84 @@ export class PomodoroTimer { private updateStatusBarVisibility() { if (this.settings.showInStatusBar) { - this.statusBarItem.style.display = ''; + this.statusBarItem.style.display = ""; } else { - this.statusBarItem.style.display = 'none'; + this.statusBarItem.style.display = "none"; } } private isAtDefaultDuration(): boolean { // Check if the timer is at its fresh/default duration for the current state if (this.currentDurationIndex === TIMER_STATES.WORK) { - return this.remainingTime === this.settings.workTime * 60; + return ( + this.timeEnd?.toISOString() === + moment + .duration(this.settings.workMinutes, "minutes") + .toISOString() + ); } else if (this.currentDurationIndex === TIMER_STATES.SHORT_BREAK) { - return this.remainingTime === this.settings.shortBreakTime * 60; - } else { - return this.remainingTime === this.settings.longBreakTime * 60; + return ( + this.timeEnd?.toISOString() === + moment + .duration(this.settings.shortBreakMinutes, "minutes") + .toISOString() + ); + } else if (this.currentDurationIndex === TIMER_STATES.LONG_BREAK) { + return ( + this.timeEnd?.toISOString() === + moment + .duration(this.settings.longBreakMinutes, "minutes") + .toISOString() + ); } + return false; } private updateIcon() { - const iconContainer = this.statusBarItem.querySelector(`.${CSS_CLASSES.ICON}`) as HTMLElement; + const iconContainer = this.statusBarItem.querySelector( + `.${CSS_CLASSES.ICON}` + ) as HTMLElement; if (iconContainer) { - let iconKey = 'pomobar-timer'; - + let iconKey = "pomobar-timer"; + if (this.isRunning) { - iconKey = 'pomobar-timer-play'; // Running state - play icon - } else if (this.remainingTime === 0) { - iconKey = 'pomobar-timer-off'; // Timer finished - } else if (this.isAtDefaultDuration()) { - iconKey = 'pomobar-timer'; // Fresh timer at default duration - timer icon - } else { - iconKey = 'pomobar-timer-pause'; // Paused while running - pause icon + iconKey = "pomobar-timer-pause"; // Running state + } else if (this.timeEnd === null) { + // Timer disabled + iconKey = "pomobar-timer-off"; + } else if (moment.isDuration(this.timeEnd)) { + // Paused state (Duration) + if (this.isAtDefaultDuration()) { + iconKey = "pomobar-timer"; // Fresh timer at default duration + } else { + iconKey = "pomobar-timer-play"; // Paused with time left + } } - + iconContainer.innerHTML = ICONS_MAP[iconKey]; } } - private getCurrentDurationTime(): number { + private updateDisplay(time?: moment.Duration) { + const textEl = this.statusBarItem.querySelector(`.${CSS_CLASSES.TEXT}`); + if (!time) { + time = this.timeRemaining; + } + if (textEl) { + textEl.textContent = `${time.minutes()}:${time + .seconds() + .toString() + .padStart(2, "0")}`; + } + } + + private getCurrentTimerDuration(): moment.Duration { if (this.currentDurationIndex === TIMER_STATES.WORK) { - return this.settings.workTime * 60; + return moment.duration(this.settings.workMinutes, "minutes"); } else if (this.currentDurationIndex === TIMER_STATES.SHORT_BREAK) { - return this.settings.shortBreakTime * 60; + return moment.duration(this.settings.shortBreakMinutes, "minutes"); } else { - return this.settings.longBreakTime * 60; + return moment.duration(this.settings.longBreakMinutes, "minutes"); } } @@ -135,41 +197,36 @@ export class PomodoroTimer { this.resetTimer(); } - startTimer() { + toggleTimer() { if (!this.isRunning) { - this.isRunning = true; - this.statusBarItem.classList.add(CSS_CLASSES.ACTIVE); - this.statusBarItem.classList.remove(CSS_CLASSES.PAUSED); - this.updateIcon(); + this.timeEnd = moment.utc(moment.now()).add(this.timeRemaining); const intervalId = window.setInterval(() => { - if (this.remainingTime > 0) { - this.remainingTime--; - this.updateDisplay(); + const time = this.timeRemaining; + this.updateDisplay(time); + if (time.asMilliseconds() > 0) { + null; } else { - new Notice("PomoBar: Time's up! Your most recent timer has finished.", 5000); - - this.soundManager.playCompletionSound(); - - if (this.currentDurationIndex === TIMER_STATES.WORK) { - this.workIntervalCount++; - if (this.workIntervalCount >= this.settings.intervalsBeforeLongBreak) { - this.currentDurationIndex = TIMER_STATES.LONG_BREAK; - this.workIntervalCount = 0; - } else { - this.currentDurationIndex = TIMER_STATES.SHORT_BREAK; - } - } else { - this.currentDurationIndex = TIMER_STATES.WORK; - } - if (this.settings.autoProgressEnabled) { // Continue running - start the next timer automatically - this.remainingTime = this.getCurrentDurationTime(); - this.updateDisplay(); + this.advanceTimer(); + + this.soundManager.playCompletionSound(); + new Notice( + "PomoBar: Time's up! Your most recent timer has finished.", + 10000 + ); + } else if (this.settings.persistentNotification) { + // Keep on chiming until user interacts with the timer + this.soundManager.playCompletionSound(); + new Notice( + "PomoBar: Time's up! Your most recent timer has finished.", + 1000 + ); } else { // Current behavior - pause after timer completion - this.resetTimer(); + // this.resetTimer(); + this.advanceTimer(); this.pauseTimer(); } } @@ -178,36 +235,59 @@ export class PomodoroTimer { this.currentInterval = intervalId; this.registeredIntervals.add(intervalId); this.plugin.registerInterval(intervalId); + + this.statusBarItem.classList.add(CSS_CLASSES.ACTIVE); + this.statusBarItem.classList.remove(CSS_CLASSES.PAUSED); + this.updateDisplay(); + this.updateIcon(); + } else if (this.timeRemaining.asMilliseconds() < 0) { + this.advanceTimer(); + } else { + this.pauseTimer(); } } private clearCurrentInterval() { if (this.currentInterval) { + console.log("Cleared interval", this.currentInterval); window.clearInterval(this.currentInterval); this.registeredIntervals.delete(this.currentInterval); this.currentInterval = null; } } + advanceTimer() { + if (this.currentDurationIndex === TIMER_STATES.WORK) { + this.workIntervalCount++; + if ( + this.workIntervalCount >= this.settings.intervalsBeforeLongBreak + ) { + this.currentDurationIndex = TIMER_STATES.LONG_BREAK; + this.workIntervalCount = 0; + } else { + this.currentDurationIndex = TIMER_STATES.SHORT_BREAK; + } + } else { + this.currentDurationIndex = TIMER_STATES.WORK; + } + this.timeEnd = moment + .utc(moment.now()) + .add(this.getCurrentTimerDuration()); + } + pauseTimer() { this.clearCurrentInterval(); - this.isRunning = false; + this.timeEnd = this.timeRemaining; this.statusBarItem.classList.remove(CSS_CLASSES.ACTIVE); this.statusBarItem.classList.add(CSS_CLASSES.PAUSED); + this.updateDisplay(); this.updateIcon(); } resetTimer() { this.clearCurrentInterval(); - this.isRunning = false; - - if (this.currentDurationIndex === TIMER_STATES.WORK) { - this.remainingTime = this.settings.workTime * 60; - } else if (this.currentDurationIndex === TIMER_STATES.SHORT_BREAK) { - this.remainingTime = this.settings.shortBreakTime * 60; - } else { - this.remainingTime = this.settings.longBreakTime * 60; - } + this.soundManager.stopCurrentAudio(); + this.timeEnd = null; this.statusBarItem.classList.remove(CSS_CLASSES.ACTIVE); this.statusBarItem.classList.remove(CSS_CLASSES.PAUSED); @@ -231,29 +311,34 @@ export class PomodoroTimer { this.resetTimer(); } - private updateDisplay() { - const minutes = Math.floor(this.remainingTime / 60); - const seconds = this.remainingTime % 60; - const textEl = this.statusBarItem.querySelector(`.${CSS_CLASSES.TEXT}`); - if (textEl) { - textEl.textContent = `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; - } - } - - get currentDuration() { + get timerType() { return this.currentDurationIndex; } - get workCount() { + get workIntervalsCount() { return this.workIntervalCount; } - get running() { - return this.isRunning; + get isRunning() { + return ( + //moment.isMoment(this.timeEnd) + this.currentInterval != null + ); } - get timeRemaining() { - return this.remainingTime; + get timeRemaining(): moment.Duration { + if (moment.isMoment(this.timeEnd)) { + // RUNNING state + return moment.duration( + this.timeEnd.diff(moment.now(), "milliseconds") + ); + } else if (moment.isDuration(this.timeEnd)) { + // PAUSED state + return this.timeEnd; + } + + // OFF state + return this.getCurrentTimerDuration(); } resetToWorkState() { @@ -270,7 +355,7 @@ export class PomodoroTimer { cleanup() { // Clean up any remaining intervals - this.registeredIntervals.forEach(intervalId => { + this.registeredIntervals.forEach((intervalId) => { window.clearInterval(intervalId); }); this.registeredIntervals.clear(); @@ -278,21 +363,26 @@ export class PomodoroTimer { window.clearInterval(this.currentInterval); this.currentInterval = null; } - + // Clean up sound manager this.soundManager.cleanup(); } // Test accessors - only used for testing private properties - get _isRunning() { + get _isRunning(): boolean { return this.isRunning; } set _isRunning(value: boolean) { - this.isRunning = value; + if (value) { + this.clearCurrentInterval(); + this.toggleTimer(); + } else { + this.resetTimer(); + } } - get _currentDurationIndex() { + get _currentDurationIndex(): number { return this.currentDurationIndex; } @@ -300,7 +390,7 @@ export class PomodoroTimer { this.currentDurationIndex = value; } - get _workIntervalCount() { + get _workIntervalCount(): number { return this.workIntervalCount; } @@ -308,15 +398,21 @@ export class PomodoroTimer { this.workIntervalCount = value; } - get _remainingTime() { - return this.remainingTime; + get _timeRemaining(): moment.Duration { + return this.timeRemaining; } - set _remainingTime(value: number) { - this.remainingTime = value; + set _timeEnd(value: moment.Moment | moment.Duration | number) { + if (moment.isMoment(value)) { + this.timeEnd = value; + } else if (moment.isDuration(this.timeEnd)) { + this.timeEnd = moment.utc(moment.now()).add(value); + } else { + this.timeEnd = moment.utc(moment.now()).add(value, "seconds"); + } } _isAtDefaultDuration() { return this.isAtDefaultDuration(); } -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index d7a496f..7442778 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,70 +15,73 @@ export default class PomodoroPlugin extends Plugin { this.statusBarItem = this.addStatusBarItem(); this.soundManager = new SoundManager(this, this.settings); - this.timer = new PomodoroTimer(this, this.settings, this.statusBarItem, this.soundManager); + this.timer = new PomodoroTimer( + this, + this.settings, + this.statusBarItem, + this.soundManager + ); - this.addSettingTab(new PomodoroSettingTab(this.app, this, this.soundManager)); + this.addSettingTab( + new PomodoroSettingTab(this.app, this, this.soundManager) + ); // Add commands for keyboard shortcuts this.addCommand({ - id: 'toggle-timer', - name: 'Start/Pause timer', + id: "toggle-timer", + name: "Toggle timer", callback: () => { if (this.timer) { - if (this.timer.running) { - this.timer.pauseTimer(); - } else { - this.timer.startTimer(); - } + this.timer.toggleTimer(); } - } + }, }); this.addCommand({ - id: 'reset-timer', - name: 'Reset current timer', + id: "reset-timer", + name: "Reset current timer", callback: () => { - if (this.timer && !this.timer.running) { + if (this.timer && !this.timer.isRunning) { this.timer.resetTimer(); } - } + }, }); this.addCommand({ - id: 'cycle-timer', - name: 'Cycle to next timer duration', + id: "cycle-timer", + name: "Cycle to next timer duration", callback: () => { - if (this.timer && !this.timer.running) { + if (this.timer && !this.timer.isRunning) { this.timer.cycleDuration(); } - } + }, }); this.addCommand({ - id: 'toggle-icon-visibility', - name: 'Toggle timer icon visibility', + id: "toggle-icon-visibility", + name: "Toggle timer icon visibility", callback: () => { this.settings.showIcon = !this.settings.showIcon; this.saveSettings(); - } + }, }); this.addCommand({ - id: 'toggle-status-bar', - name: 'Toggle status bar visibility', + id: "toggle-status-bar", + name: "Toggle status bar visibility", callback: () => { this.timer.toggleStatusBarVisibility(); this.saveSettings(); // Save the updated setting - } + }, }); this.addCommand({ - id: 'toggle-sound-notifications', - name: 'Toggle sound notifications', + id: "toggle-sound-notifications", + name: "Toggle sound notifications", callback: () => { this.settings.soundEnabled = !this.settings.soundEnabled; this.saveSettings(); - } + }, }); } @@ -104,7 +107,7 @@ export default class PomodoroPlugin extends Plugin { } get currentDurationIndex() { - return this.timer?.currentDuration ?? 0; + return this.timer?.timerType ?? 0; } set currentDurationIndex(_value: number) { @@ -112,7 +115,7 @@ export default class PomodoroPlugin extends Plugin { } get workIntervalCount() { - return this.timer?.workCount ?? 0; + return this.timer?.workIntervalsCount ?? 0; } set workIntervalCount(_value: number) { @@ -139,4 +142,4 @@ export default class PomodoroPlugin extends Plugin { get _timer() { return this.timer; } -} \ No newline at end of file +} diff --git a/src/types.ts b/src/types.ts index 8ab0590..c3904b4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,11 +1,12 @@ export interface PomodoroSettings { - workTime: number; - shortBreakTime: number; - longBreakTime: number; + workMinutes: number; + shortBreakMinutes: number; + longBreakMinutes: number; intervalsBeforeLongBreak: number; showIcon: boolean; showInStatusBar: boolean; soundEnabled: boolean; + persistentNotification: boolean; selectedSound: string; soundVolume: number; customSoundUrl?: string; @@ -13,14 +14,15 @@ export interface PomodoroSettings { } export const DEFAULT_SETTINGS: PomodoroSettings = { - workTime: 25, - shortBreakTime: 5, - longBreakTime: 15, + workMinutes: 25, + shortBreakMinutes: 5, + longBreakMinutes: 15, intervalsBeforeLongBreak: 4, showIcon: false, showInStatusBar: true, soundEnabled: false, + persistentNotification: false, selectedSound: "chime.wav", soundVolume: 0.5, autoProgressEnabled: false, -}; \ No newline at end of file +}; diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index 4cdb29f..75c3bde 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -1,6 +1,6 @@ import './setup'; import PomodoroPlugin from '../src/main'; -import { App } from 'obsidian'; +import { moment, App } from 'obsidian'; import { PluginWithPrivates } from './setup'; describe('PomodoroPlugin', () => { @@ -41,18 +41,18 @@ describe('PomodoroPlugin', () => { describe('Initialization and Settings', () => { it('should load default settings on onload if no saved data', async () => { await plugin.onload(); - expect(plugin.settings.workTime).toBe(25); - expect(plugin.settings.shortBreakTime).toBe(5); - expect(plugin.settings.longBreakTime).toBe(15); + expect(plugin.settings.workMinutes).toBe(25); + expect(plugin.settings.shortBreakMinutes).toBe(5); + expect(plugin.settings.longBreakMinutes).toBe(15); expect(plugin.settings.intervalsBeforeLongBreak).toBe(4); expect((plugin as PluginWithPrivates)._statusBarItem).toBeDefined(); }); it('should load saved settings on onload', async () => { const savedSettings = { - workTime: 30, - shortBreakTime: 7, - longBreakTime: 20, + workMinutes: 30, + shortBreakMinutes: 7, + longBreakMinutes: 20, intervalsBeforeLongBreak: 3, }; plugin.loadData = jest.fn().mockResolvedValue(savedSettings); @@ -63,6 +63,7 @@ describe('PomodoroPlugin', () => { showIcon: false, // Default value showInStatusBar: true, // Default value soundEnabled: false, // Default value + persistentNotification: false, // Default value selectedSound: "chime.wav", // Default value soundVolume: 0.5, // Default value autoProgressEnabled: false, // Default value @@ -80,10 +81,10 @@ describe('PomodoroPlugin', () => { it('should initialize with correct default state', async () => { await plugin.onload(); const timer = (plugin as PluginWithPrivates)._timer; - expect(timer.timeRemaining).toBe(plugin.settings.workTime * 60); - expect(timer.running).toBe(false); - expect(timer.currentDuration).toBe(0); - expect(timer.workCount).toBe(0); + expect(timer.timeRemaining.seconds()).toStrictEqual(moment.duration(plugin.settings.workMinutes, "minutes").seconds()); + expect(timer.isRunning).toBe(false); + expect(timer.timerType).toBe(0); + expect(timer.workIntervalsCount).toBe(0); }); }); @@ -172,7 +173,7 @@ describe('PomodoroPlugin', () => { describe('Toggle Timer Command', () => { it('should start timer when not running', async () => { const timer = (plugin as PluginWithPrivates)._timer; - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); // Find and execute the toggle-timer command const mockAddCommand = plugin.addCommand as jest.Mock; @@ -180,18 +181,18 @@ describe('PomodoroPlugin', () => { expect(toggleCommand).toBeDefined(); // Execute the command callback - jest.spyOn(timer, 'startTimer'); + jest.spyOn(timer, 'toggleTimer'); toggleCommand[0].callback(); - expect(timer.startTimer).toHaveBeenCalled(); + expect(timer.toggleTimer).toHaveBeenCalled(); }); it('should pause timer when running', async () => { const timer = (plugin as PluginWithPrivates)._timer; // Start the timer first - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); // Execute toggle command const mockAddCommand = plugin.addCommand as jest.Mock; @@ -227,7 +228,7 @@ describe('PomodoroPlugin', () => { describe('Reset Timer Command', () => { it('should reset timer when not running', async () => { const timer = (plugin as PluginWithPrivates)._timer; - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); const mockAddCommand = plugin.addCommand as jest.Mock; const resetCommand = mockAddCommand.mock.calls.find(call => call[0].id === 'reset-timer'); @@ -240,8 +241,8 @@ describe('PomodoroPlugin', () => { it('should not reset timer when running', async () => { const timer = (plugin as PluginWithPrivates)._timer; - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); const mockAddCommand = plugin.addCommand as jest.Mock; const resetCommand = mockAddCommand.mock.calls.find(call => call[0].id === 'reset-timer'); @@ -256,7 +257,7 @@ describe('PomodoroPlugin', () => { describe('Cycle Timer Command', () => { it('should cycle timer when not running', async () => { const timer = (plugin as PluginWithPrivates)._timer; - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); const mockAddCommand = plugin.addCommand as jest.Mock; const cycleCommand = mockAddCommand.mock.calls.find(call => call[0].id === 'cycle-timer'); @@ -269,8 +270,8 @@ describe('PomodoroPlugin', () => { it('should not cycle timer when running', async () => { const timer = (plugin as PluginWithPrivates)._timer; - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); const mockAddCommand = plugin.addCommand as jest.Mock; const cycleCommand = mockAddCommand.mock.calls.find(call => call[0].id === 'cycle-timer'); @@ -336,7 +337,7 @@ describe('PomodoroPlugin', () => { const mockAddCommand = plugin.addCommand as jest.Mock; const expectedCommandNames = { - 'toggle-timer': 'Start/Pause timer', + 'toggle-timer': 'Toggle timer', 'reset-timer': 'Reset current timer', 'cycle-timer': 'Cycle to next timer duration', 'toggle-icon-visibility': 'Toggle timer icon visibility', diff --git a/tests/settings-tab.test.ts b/tests/settings-tab.test.ts index e0da621..a56cb12 100644 --- a/tests/settings-tab.test.ts +++ b/tests/settings-tab.test.ts @@ -151,13 +151,13 @@ describe('PomodoroSettingTab', () => { }); describe('Settings Interactions', () => { - it('should update workTime on valid input', async () => { + it('should update workMinutes on valid input', async () => { settingTab.display(); const settings = (Setting as jest.Mock).mock.results; // Simulate text change for work time - const workTimeSetting = settings[1].value; - const workTimeOnChange = workTimeSetting.addText.mock.calls[0][0]; + const workMinutesSetting = settings[1].value; + const workMinutesOnChange = workMinutesSetting.addText.mock.calls[0][0]; const textComponent = { setPlaceholder: jest.fn(), setValue: jest.fn(), @@ -168,23 +168,23 @@ describe('PomodoroSettingTab', () => { textComponent.setValue = jest.fn().mockReturnValue(textComponent); textComponent.onChange = jest.fn().mockReturnValue(textComponent); textComponent.onInput = jest.fn().mockReturnValue(textComponent); - workTimeOnChange(textComponent); + workMinutesOnChange(textComponent); const onChangeCallback = textComponent.onChange.mock.calls[0][0]; await onChangeCallback('30'); - expect(mockPlugin.settings.workTime).toBe(30); + expect(mockPlugin.settings.workMinutes).toBe(30); expect(mockPlugin.saveSettings).toHaveBeenCalled(); expect(mockPlugin.resetTimer).toHaveBeenCalled(); }); - it('should not update workTime on invalid input', async () => { + it('should not update workMinutes on invalid input', async () => { settingTab.display(); const settings = (Setting as jest.Mock).mock.results; - const initialWorkTime = mockPlugin.settings.workTime; + const initialWorkMinutes = mockPlugin.settings.workMinutes; - const workTimeSetting = settings[1].value; - const workTimeOnChange = workTimeSetting.addText.mock.calls[0][0]; + const workMinutesSetting = settings[1].value; + const workMinutesOnChange = workMinutesSetting.addText.mock.calls[0][0]; const textComponent = { setPlaceholder: jest.fn(), setValue: jest.fn(), @@ -195,12 +195,12 @@ describe('PomodoroSettingTab', () => { textComponent.setValue = jest.fn().mockReturnValue(textComponent); textComponent.onChange = jest.fn().mockReturnValue(textComponent); textComponent.onInput = jest.fn().mockReturnValue(textComponent); - workTimeOnChange(textComponent); + workMinutesOnChange(textComponent); const onChangeCallback = textComponent.onChange.mock.calls[0][0]; await onChangeCallback('invalid'); - expect(mockPlugin.settings.workTime).toBe(initialWorkTime); + expect(mockPlugin.settings.workMinutes).toBe(initialWorkMinutes); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); expect(mockPlugin.resetTimer).not.toHaveBeenCalled(); }); @@ -226,7 +226,7 @@ describe('PomodoroSettingTab', () => { await onChangeCallback('10'); - expect(mockPlugin.settings.shortBreakTime).toBe(10); + expect(mockPlugin.settings.shortBreakMinutes).toBe(10); expect(mockPlugin.saveSettings).toHaveBeenCalled(); expect(mockPlugin.resetTimer).toHaveBeenCalled(); }); @@ -252,7 +252,7 @@ describe('PomodoroSettingTab', () => { await onChangeCallback('20'); - expect(mockPlugin.settings.longBreakTime).toBe(20); + expect(mockPlugin.settings.longBreakMinutes).toBe(20); expect(mockPlugin.saveSettings).toHaveBeenCalled(); expect(mockPlugin.resetTimer).toHaveBeenCalled(); }); @@ -350,52 +350,52 @@ describe('PomodoroSettingTab', () => { describe('Work Time Validation', () => { it('should not update on zero value', async () => { - const initialValue = mockPlugin.settings.workTime; + const initialValue = mockPlugin.settings.workMinutes; const onChangeCallback = getOnChangeCallback(1); await onChangeCallback('0'); - expect(mockPlugin.settings.workTime).toBe(initialValue); + expect(mockPlugin.settings.workMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on negative value', async () => { - const initialValue = mockPlugin.settings.workTime; + const initialValue = mockPlugin.settings.workMinutes; const onChangeCallback = getOnChangeCallback(1); await onChangeCallback('-5'); - expect(mockPlugin.settings.workTime).toBe(initialValue); + expect(mockPlugin.settings.workMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on decimal value', async () => { - const initialValue = mockPlugin.settings.workTime; + const initialValue = mockPlugin.settings.workMinutes; const onChangeCallback = getOnChangeCallback(1); await onChangeCallback('25.5'); - expect(mockPlugin.settings.workTime).toBe(initialValue); + expect(mockPlugin.settings.workMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on empty string', async () => { - const initialValue = mockPlugin.settings.workTime; + const initialValue = mockPlugin.settings.workMinutes; const onChangeCallback = getOnChangeCallback(1); await onChangeCallback(''); - expect(mockPlugin.settings.workTime).toBe(initialValue); + expect(mockPlugin.settings.workMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on whitespace-only string', async () => { - const initialValue = mockPlugin.settings.workTime; + const initialValue = mockPlugin.settings.workMinutes; const onChangeCallback = getOnChangeCallback(1); await onChangeCallback(' '); - expect(mockPlugin.settings.workTime).toBe(initialValue); + expect(mockPlugin.settings.workMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); @@ -404,71 +404,71 @@ describe('PomodoroSettingTab', () => { await onChangeCallback(' 30 '); - expect(mockPlugin.settings.workTime).toBe(30); + expect(mockPlugin.settings.workMinutes).toBe(30); expect(mockPlugin.saveSettings).toHaveBeenCalled(); }); }); describe('Short Break Time Validation', () => { it('should not update on zero value', async () => { - const initialValue = mockPlugin.settings.shortBreakTime; + const initialValue = mockPlugin.settings.shortBreakMinutes; const onChangeCallback = getOnChangeCallback(2); await onChangeCallback('0'); - expect(mockPlugin.settings.shortBreakTime).toBe(initialValue); + expect(mockPlugin.settings.shortBreakMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on negative value', async () => { - const initialValue = mockPlugin.settings.shortBreakTime; + const initialValue = mockPlugin.settings.shortBreakMinutes; const onChangeCallback = getOnChangeCallback(2); await onChangeCallback('-3'); - expect(mockPlugin.settings.shortBreakTime).toBe(initialValue); + expect(mockPlugin.settings.shortBreakMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on decimal value', async () => { - const initialValue = mockPlugin.settings.shortBreakTime; + const initialValue = mockPlugin.settings.shortBreakMinutes; const onChangeCallback = getOnChangeCallback(2); await onChangeCallback('5.7'); - expect(mockPlugin.settings.shortBreakTime).toBe(initialValue); + expect(mockPlugin.settings.shortBreakMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); }); describe('Long Break Time Validation', () => { it('should not update on zero value', async () => { - const initialValue = mockPlugin.settings.longBreakTime; + const initialValue = mockPlugin.settings.longBreakMinutes; const onChangeCallback = getOnChangeCallback(3); await onChangeCallback('0'); - expect(mockPlugin.settings.longBreakTime).toBe(initialValue); + expect(mockPlugin.settings.longBreakMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on negative value', async () => { - const initialValue = mockPlugin.settings.longBreakTime; + const initialValue = mockPlugin.settings.longBreakMinutes; const onChangeCallback = getOnChangeCallback(3); await onChangeCallback('-10'); - expect(mockPlugin.settings.longBreakTime).toBe(initialValue); + expect(mockPlugin.settings.longBreakMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); it('should not update on decimal value', async () => { - const initialValue = mockPlugin.settings.longBreakTime; + const initialValue = mockPlugin.settings.longBreakMinutes; const onChangeCallback = getOnChangeCallback(3); await onChangeCallback('15.3'); - expect(mockPlugin.settings.longBreakTime).toBe(initialValue); + expect(mockPlugin.settings.longBreakMinutes).toBe(initialValue); expect(mockPlugin.saveSettings).not.toHaveBeenCalled(); }); }); diff --git a/tests/settings.test.ts b/tests/settings.test.ts index 5436904..4862170 100644 --- a/tests/settings.test.ts +++ b/tests/settings.test.ts @@ -30,7 +30,7 @@ describe('Settings Management', () => { }); it('should save settings', async () => { - plugin.settings.workTime = 30; + plugin.settings.workMinutes = 30; await plugin.saveSettings(); expect(plugin.saveData).toHaveBeenCalledWith(plugin.settings); }); @@ -39,7 +39,7 @@ describe('Settings Management', () => { const timer = (plugin as PluginWithPrivates)._timer; const updateSettingsSpy = jest.spyOn(timer, 'updateSettings'); - plugin.settings.workTime = 45; + plugin.settings.workMinutes = 45; await plugin.saveSettings(); expect(updateSettingsSpy).toHaveBeenCalledWith(plugin.settings); diff --git a/tests/setup.ts b/tests/setup.ts index a7914bf..c1529a8 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -6,7 +6,15 @@ export interface PluginWithPrivates extends PomodoroPlugin { _timer: PomodoroTimer; } -jest.mock('obsidian'); // This will use the __mocks__/obsidian.ts automatically +jest.mock('obsidian', () => { + const obsidian = jest.requireActual('obsidian'); + const moment = jest.requireActual('moment'); + + return { + ...obsidian, + moment + } +}); // This will use the __mocks__/obsidian.ts automatically // Mock document.createElement globally to ensure consistent behavior global.document = { diff --git a/tests/timer.test.ts b/tests/timer.test.ts index dcebbf8..ae2a4fb 100644 --- a/tests/timer.test.ts +++ b/tests/timer.test.ts @@ -1,6 +1,6 @@ import './setup'; import PomodoroPlugin from '../src/main'; -import { App } from 'obsidian'; +import { moment, App } from 'obsidian'; import { PluginWithPrivates } from './setup'; import { PomodoroTimer } from '../src/logic/timer'; @@ -44,8 +44,8 @@ describe('PomodoroTimer', () => { jest.useFakeTimers(); const timer = (plugin as PluginWithPrivates)._timer; - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); expect(mockStatusBarItem.classList.add).toHaveBeenCalledWith('pomodoro-active'); expect(mockStatusBarItem.classList.remove).toHaveBeenCalledWith('pomodoro-paused'); expect(window.setInterval).toHaveBeenCalledTimes(1); @@ -60,9 +60,9 @@ describe('PomodoroTimer', () => { (mockStatusBarItem.classList.add as jest.Mock).mockClear(); (mockStatusBarItem.classList.remove as jest.Mock).mockClear(); - timer.startTimer(); + timer.toggleTimer(); timer.pauseTimer(); - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); expect(mockStatusBarItem.classList.remove).toHaveBeenCalledWith('pomodoro-active'); expect(mockStatusBarItem.classList.add).toHaveBeenCalledWith('pomodoro-paused'); expect(window.clearInterval).toHaveBeenCalledTimes(1); @@ -72,31 +72,31 @@ describe('PomodoroTimer', () => { const timer = (plugin as PluginWithPrivates)._timer; timer.resetTimer(); - expect(timer.running).toBe(false); - expect(timer.timeRemaining).toBe(plugin.settings.workTime * 60); + expect(timer.isRunning).toBe(false); + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.workMinutes, "minutes").toISOString()); }); it('should cycle durations correctly', () => { const timer = (plugin as PluginWithPrivates)._timer; timer.cycleDuration(); - expect(timer.currentDuration).toBe(1); // Short break + expect(timer.timerType).toBe(1); // Short break timer.cycleDuration(); - expect(timer.currentDuration).toBe(2); // Long break + expect(timer.timerType).toBe(2); // Long break timer.cycleDuration(); - expect(timer.currentDuration).toBe(0); // Work + expect(timer.timerType).toBe(0); // Work }); it('should not cycle duration when timer is running', () => { const timer = (plugin as PluginWithPrivates)._timer; - timer.startTimer(); - const initialDuration = timer.currentDuration; + timer.toggleTimer(); + const initialTimerType = timer.timerType; timer.cycleDuration(); - expect(timer.currentDuration).toBe(initialDuration); + expect(timer.timerType).toBe(initialTimerType); timer.pauseTimer(); }); @@ -107,7 +107,7 @@ describe('PomodoroTimer', () => { const textEl = { textContent: '' }; mockStatusBarItem.querySelector = jest.fn().mockReturnValue(textEl); - timer.startTimer(); + timer.toggleTimer(); // Advance timer by 1 second jest.advanceTimersByTime(1000); @@ -127,15 +127,15 @@ describe('PomodoroTimer', () => { // Set timer to different state timer['currentDurationIndex'] = 2; // Long break timer['workIntervalCount'] = 3; - timer.startTimer(); + timer.toggleTimer(); // Call resetToWorkState (covers lines 191-194) timer.resetToWorkState(); expect(timer['currentDurationIndex']).toBe(0); // Work state expect(timer['workIntervalCount']).toBe(0); // Reset count - expect(timer.running).toBe(false); // Should be paused - expect(timer.timeRemaining).toBe(plugin.settings.workTime * 60); // Work duration + expect(timer.isRunning).toBe(false); // Should be paused + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.workMinutes, "minutes").toISOString()); // Work duration }); it('should reset to work state even when already in work state', () => { @@ -144,13 +144,13 @@ describe('PomodoroTimer', () => { // Already in work state but with some progress timer['currentDurationIndex'] = 0; // Work state timer['workIntervalCount'] = 2; - timer['remainingTime'] = 500; // Some progress made + timer['timeEnd'] = moment.utc(moment.now()).add(500, "seconds"); // Some progress made timer.resetToWorkState(); expect(timer['currentDurationIndex']).toBe(0); // Still work state expect(timer['workIntervalCount']).toBe(0); // Reset count - expect(timer.timeRemaining).toBe(plugin.settings.workTime * 60); // Reset duration + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.workMinutes, "minutes").toISOString()); // Reset duration }); }); @@ -213,10 +213,10 @@ describe('PomodoroTimer', () => { describe('Left Click Events', () => { it('should start timer when not running', () => { - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); // Spy on method before calling handler - const startTimerSpy = jest.spyOn(timer, 'startTimer').mockImplementation(() => { + const startTimerSpy = jest.spyOn(timer, 'toggleTimer').mockImplementation(() => { timer._isRunning = true; }); @@ -236,8 +236,8 @@ describe('PomodoroTimer', () => { }); it('should pause timer when running', () => { - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); const pauseTimerSpy = jest.spyOn(timer, 'pauseTimer').mockImplementation(() => { timer._isRunning = false; @@ -258,7 +258,7 @@ describe('PomodoroTimer', () => { describe('Middle Click Events', () => { it('should cycle duration when not running', () => { - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); const cycleDurationSpy = jest.spyOn(timer, 'cycleDuration').mockImplementation(() => {}); @@ -276,8 +276,8 @@ describe('PomodoroTimer', () => { }); it('should still call cycleDuration when running (no restriction in auxclick)', () => { - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); const cycleDurationSpy = jest.spyOn(timer, 'cycleDuration').mockImplementation(() => {}); @@ -296,7 +296,7 @@ describe('PomodoroTimer', () => { describe('Right Click Events', () => { it('should reset timer when not running', () => { - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); const resetTimerSpy = jest.spyOn(timer, 'resetTimer').mockImplementation(() => {}); @@ -318,8 +318,8 @@ describe('PomodoroTimer', () => { }); it('should not reset timer when running', () => { - timer.startTimer(); - expect(timer.running).toBe(true); + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); const resetTimerSpy = jest.spyOn(timer, 'resetTimer').mockImplementation(() => {}); @@ -376,9 +376,9 @@ describe('PomodoroTimer', () => { expect(iconContainer?.innerHTML).toContain('Mock SVG'); // From our SVG mock }); - it('should show play icon when running', () => { - timer.startTimer(); - expect(timer.running).toBe(true); + it('should show pause icon when running', () => { + timer.toggleTimer(); + expect(timer.isRunning).toBe(true); const statusBarItem = (plugin as PluginWithPrivates)._statusBarItem; const iconContainer = statusBarItem.querySelector('.pomodoro-icon'); @@ -388,12 +388,12 @@ describe('PomodoroTimer', () => { expect(iconContainer?.innerHTML).toContain('Mock SVG'); }); - it('should show pause icon when paused mid-session', () => { + it('should show play icon when paused mid-session', () => { // Start timer then pause it - timer.startTimer(); + timer.toggleTimer(); timer.pauseTimer(); - expect(timer.running).toBe(false); + expect(timer.isRunning).toBe(false); expect(timer._isAtDefaultDuration()).toBe(true); // Still at full duration in our mock const statusBarItem = (plugin as PluginWithPrivates)._statusBarItem; @@ -404,11 +404,11 @@ describe('PomodoroTimer', () => { }); it('should update icon when cycling durations', () => { - const initialDuration = timer.currentDuration; + const initialDuration = timer.timerType; timer.cycleDuration(); - expect(timer.currentDuration).not.toBe(initialDuration); + expect(timer.timerType).not.toBe(initialDuration); const statusBarItem = (plugin as PluginWithPrivates)._statusBarItem; const iconContainer = statusBarItem.querySelector('.pomodoro-icon'); @@ -435,22 +435,18 @@ describe('PomodoroTimer', () => { timer._workIntervalCount = 0; // Mock timer completion - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; - const initialWorkCount = timer.workCount; + const initialWorkCount = timer.workIntervalsCount; // Simulate timer reaching zero (this would normally happen in the interval) // We need to trigger the timer completion logic manually since we can't wait for intervals - if (timer._remainingTime === 0) { - timer._workIntervalCount++; - timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK - timer.resetTimer(); - timer.pauseTimer(); - } + timer.advanceTimer(); + timer.pauseTimer(); - expect(timer.workCount).toBe(initialWorkCount + 1); - expect(timer.currentDuration).toBe(1); // Should be in short break + expect(timer.workIntervalsCount).toBe(initialWorkCount + 1); + expect(timer.timerType).toBe(1); // Should be in short break }); it('should transition to long break after configured work intervals', () => { @@ -458,39 +454,29 @@ describe('PomodoroTimer', () => { timer._currentDurationIndex = 0; // TIMER_STATES.WORK timer._workIntervalCount = 3; // One less than default intervalsBeforeLongBreak (4) - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate timer completion leading to long break - if (timer._remainingTime === 0) { - timer._workIntervalCount++; - if (timer._workIntervalCount >= plugin.settings.intervalsBeforeLongBreak) { - timer._currentDurationIndex = 2; // TIMER_STATES.LONG_BREAK - timer._workIntervalCount = 0; - } - timer.resetTimer(); - timer.pauseTimer(); - } + timer.advanceTimer(); + timer.pauseTimer(); - expect(timer.currentDuration).toBe(2); // Should be in long break - expect(timer.workCount).toBe(0); // Should reset work count + expect(timer.timerType).toBe(2); // Should be in long break + expect(timer.workIntervalsCount).toBe(0); // Should reset work count }); it('should transition from break back to work', () => { // Set up break timer timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate break completion - if (timer._remainingTime === 0) { - timer._currentDurationIndex = 0; // TIMER_STATES.WORK - timer.resetTimer(); - timer.pauseTimer(); - } + timer.advanceTimer(); + timer.pauseTimer(); - expect(timer.currentDuration).toBe(0); // Should be back to work + expect(timer.timerType).toBe(0); // Should be back to work }); }); @@ -513,37 +499,30 @@ describe('PomodoroTimer', () => { it('should pause after work timer completes', () => { timer._currentDurationIndex = 0; // TIMER_STATES.WORK timer._workIntervalCount = 0; - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate timer completion logic (without auto-progression) - if (timer._remainingTime === 0) { - timer._workIntervalCount++; - timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK - timer.resetTimer(); - timer.pauseTimer(); - } - - expect(timer.running).toBe(false); - expect(timer.currentDuration).toBe(1); // Should be in short break - expect(timer.timeRemaining).toBe(plugin.settings.shortBreakTime * 60); + timer.advanceTimer(); + timer.pauseTimer(); + + expect(timer.isRunning).toBe(false); + expect(timer.timerType).toBe(1); // Should be in short break + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.shortBreakMinutes, "minutes").toISOString()); }); it('should pause after break timer completes', () => { timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate break completion logic (without auto-progression) - if (timer._remainingTime === 0) { - timer._currentDurationIndex = 0; // TIMER_STATES.WORK - timer.resetTimer(); - timer.pauseTimer(); - } - - expect(timer.running).toBe(false); - expect(timer.currentDuration).toBe(0); // Should be back to work - expect(timer.timeRemaining).toBe(plugin.settings.workTime * 60); + timer.advanceTimer(); + timer.pauseTimer(); + + expect(timer.isRunning).toBe(false); + expect(timer.timerType).toBe(0); // Should be back to work + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.workMinutes, "minutes").toISOString()); }); }); @@ -557,99 +536,67 @@ describe('PomodoroTimer', () => { it('should continue running after work timer completes', () => { timer._currentDurationIndex = 0; // TIMER_STATES.WORK timer._workIntervalCount = 0; - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate timer completion logic (with auto-progression) - if (timer._remainingTime === 0) { - timer._workIntervalCount++; - timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK - - if (plugin.settings.autoProgressEnabled) { - timer._remainingTime = timer['getCurrentDurationTime'](); - } else { - timer.resetTimer(); - timer.pauseTimer(); - } - } - - expect(timer.running).toBe(true); - expect(timer.currentDuration).toBe(1); // Should be in short break - expect(timer.timeRemaining).toBe(plugin.settings.shortBreakTime * 60); + timer.advanceTimer(); + + expect(timer.isRunning).toBe(true); + expect(timer.timerType).toBe(1); // Should be in short break + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.shortBreakMinutes, "minutes").toISOString()); }); it('should continue running after break timer completes', () => { timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate break completion logic (with auto-progression) - if (timer._remainingTime === 0) { - timer._currentDurationIndex = 0; // TIMER_STATES.WORK - - if (plugin.settings.autoProgressEnabled) { - timer._remainingTime = timer['getCurrentDurationTime'](); - } else { - timer.resetTimer(); - timer.pauseTimer(); - } - } - - expect(timer.running).toBe(true); - expect(timer.currentDuration).toBe(0); // Should be back to work - expect(timer.timeRemaining).toBe(plugin.settings.workTime * 60); + timer.advanceTimer(); + + expect(timer.isRunning).toBe(true); + expect(timer.timerType).toBe(0); // Should be back to work + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.workMinutes, "minutes").toISOString()); }); it('should transition to long break with auto-progression', () => { timer._currentDurationIndex = 0; // TIMER_STATES.WORK timer._workIntervalCount = 3; // One less than default (4) - timer._remainingTime = 0; + timer._timeEnd = moment.duration(0); timer._isRunning = true; // Simulate work completion leading to long break - if (timer._remainingTime === 0) { - timer._workIntervalCount++; - if (timer._workIntervalCount >= plugin.settings.intervalsBeforeLongBreak) { - timer._currentDurationIndex = 2; // TIMER_STATES.LONG_BREAK - timer._workIntervalCount = 0; - } - - if (plugin.settings.autoProgressEnabled) { - timer._remainingTime = timer['getCurrentDurationTime'](); - } else { - timer.resetTimer(); - timer.pauseTimer(); - } - } - - expect(timer.running).toBe(true); - expect(timer.currentDuration).toBe(2); // Should be in long break - expect(timer.workCount).toBe(0); // Should reset work count - expect(timer.timeRemaining).toBe(plugin.settings.longBreakTime * 60); + timer.advanceTimer(); + + expect(timer.isRunning).toBe(true); + expect(timer.timerType).toBe(2); // Should be in long break + expect(timer.workIntervalsCount).toBe(0); // Should reset work count + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.longBreakMinutes, "minutes").toISOString()); }); it('should still allow manual pause during auto-progression', () => { // Enable auto-progression but test manual control timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK timer._isRunning = true; - timer._remainingTime = 300; // 5 minutes remaining + timer._timeEnd = moment.utc(moment.now()).add(plugin.settings.shortBreakMinutes, "minutes"); // 5 minutes remaining timer.pauseTimer(); - expect(timer.running).toBe(false); - expect(timer.timeRemaining).toBe(300); // Time should be preserved + expect(timer.isRunning).toBe(false); + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.shortBreakMinutes, "minutes").toISOString()); // Time should be preserved }); it('should allow manual reset during auto-progression', () => { timer._currentDurationIndex = 1; // TIMER_STATES.SHORT_BREAK timer._isRunning = false; - timer._remainingTime = 300; // Some time remaining + timer._timeEnd = moment.utc(moment.now()).add(plugin.settings.shortBreakMinutes, "minutes"); // Some time remaining timer.resetTimer(); - expect(timer.running).toBe(false); - expect(timer.currentDuration).toBe(1); // Should stay in short break - expect(timer.timeRemaining).toBe(plugin.settings.shortBreakTime * 60); + expect(timer.isRunning).toBe(false); + expect(timer.timerType).toBe(1); // Should stay in short break + expect(timer.timeRemaining.toISOString()).toStrictEqual(moment.duration(plugin.settings.shortBreakMinutes, "minutes").toISOString()); }); }); });