diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63c8f0208f..3bdb7c9706 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,7 +99,7 @@ importers: version: 6.0.3 vitest: specifier: 4.1.8 - version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)) + version: 4.1.8(@types/node@25.9.1)(@vitest/coverage-istanbul@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)) packages: @@ -119,6 +119,36 @@ packages: resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} @@ -127,11 +157,27 @@ packages: resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.29.7': resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.7': resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} @@ -223,6 +269,16 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + + '@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'} @@ -697,6 +753,11 @@ packages: resolution: {integrity: sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/coverage-istanbul@4.1.8': + resolution: {integrity: sha512-/h514nMZMKI6foh21mVgO1zlCH6pdDamwKMbla1uLU2GMxTlfp0PQMxovWozmzQdCIQYZ2XXEzg2zNZom2zAOg==} + peerDependencies: + vitest: 4.1.8 + '@vitest/coverage-v8@4.1.8': resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} peerDependencies: @@ -839,6 +900,11 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + baseline-browser-mapping@2.10.37: + resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==} + engines: {node: '>=6.0.0'} + hasBin: true + before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} @@ -856,6 +922,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -872,6 +943,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -1053,6 +1127,9 @@ packages: duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + electron-to-chromium@1.5.372: + resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -1296,6 +1373,10 @@ packages: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1650,6 +1731,11 @@ packages: resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1671,6 +1757,11 @@ packages: json-with-bigint@3.5.8: resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} @@ -1798,6 +1889,9 @@ packages: resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -1910,6 +2004,10 @@ packages: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} + normalize-package-data@6.0.2: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} engines: {node: ^16.14.0 || >=18.0.0} @@ -2285,6 +2383,10 @@ packages: resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} engines: {node: '>=12'} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.8.1: resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} engines: {node: '>=10'} @@ -2636,6 +2738,12 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2792,6 +2900,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -2849,14 +2960,106 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.29.7': + optional: true + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + optional: true + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + optional: true + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + optional: true + + '@babel/helper-globals@7.29.7': + optional: true + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + optional: true + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/helper-string-parser@7.29.7': {} '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.29.7': + optional: true + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + optional: true + '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + optional: true + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/types@7.29.7': dependencies: '@babel/helper-string-parser': 7.29.7 @@ -2959,6 +3162,21 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@istanbuljs/schema@0.1.6': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + optional: true + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + optional: true + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -3441,6 +3659,23 @@ snapshots: '@typescript-eslint/types': 8.60.1 eslint-visitor-keys: 5.0.1 + '@vitest/coverage-istanbul@4.1.8(vitest@4.1.8)': + dependencies: + '@babel/core': 7.29.7 + '@istanbuljs/schema': 0.1.6 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.1 + tinyrainbow: 3.1.0 + vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-istanbul@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)) + transitivePeerDependencies: + - supports-color + optional: true + '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -3453,7 +3688,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@25.9.1)(@vitest/coverage-istanbul@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)) '@vitest/expect@4.1.8': dependencies: @@ -3599,6 +3834,9 @@ snapshots: balanced-match@4.0.4: {} + baseline-browser-mapping@2.10.37: + optional: true + before-after-hook@4.0.0: {} bottleneck@2.19.5: {} @@ -3616,6 +3854,15 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.37 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.372 + node-releases: 2.0.47 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + optional: true + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -3635,6 +3882,9 @@ snapshots: callsites@3.1.0: {} + caniuse-lite@1.0.30001799: + optional: true + chai@6.2.2: {} chalk@2.4.2: @@ -3821,6 +4071,9 @@ snapshots: dependencies: readable-stream: 2.3.8 + electron-to-chromium@1.5.372: + optional: true + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} @@ -4130,6 +4383,9 @@ snapshots: generator-function@2.0.1: {} + gensync@1.0.0-beta.2: + optional: true + get-caller-file@2.0.5: {} get-east-asian-width@1.6.0: {} @@ -4469,6 +4725,9 @@ snapshots: dependencies: argparse: 2.0.1 + jsesc@3.1.0: + optional: true + json-buffer@3.0.1: {} json-parse-better-errors@1.0.2: {} @@ -4483,6 +4742,9 @@ snapshots: json-with-bigint@3.5.8: {} + json5@2.2.3: + optional: true + jsonfile@6.2.1: dependencies: universalify: 2.0.1 @@ -4585,6 +4847,11 @@ snapshots: lru-cache@11.5.1: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + optional: true + lunr@2.3.9: {} magic-string@0.30.21: @@ -4695,6 +4962,9 @@ snapshots: emojilib: 2.4.0 skin-tone: 2.0.0 + node-releases@2.0.47: + optional: true + normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 @@ -5046,6 +5316,9 @@ snapshots: semver-regex@4.0.5: {} + semver@6.3.1: + optional: true + semver@7.8.1: {} set-function-length@1.2.2: @@ -5419,6 +5692,13 @@ snapshots: universalify@2.0.1: {} + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + optional: true + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -5449,7 +5729,7 @@ snapshots: jiti: 2.6.1 yaml: 2.9.0 - vitest@4.1.8(@types/node@25.9.1)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)): + vitest@4.1.8(@types/node@25.9.1)(@vitest/coverage-istanbul@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 '@vitest/mocker': 4.1.8(vite@8.0.10(@types/node@25.9.1)(jiti@2.6.1)(yaml@2.9.0)) @@ -5473,6 +5753,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.9.1 + '@vitest/coverage-istanbul': 4.1.8(vitest@4.1.8) '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) transitivePeerDependencies: - msw @@ -5549,6 +5830,9 @@ snapshots: y18n@5.0.8: {} + yallist@3.1.1: + optional: true + yaml@2.9.0: {} yargs-parser@20.2.9: {} diff --git a/src/test/api/CommercetoolsApi.test.ts b/src/test/api/CommercetoolsApi.test.ts index a1ced6e5fb..3c40fbe9b1 100644 --- a/src/test/api/CommercetoolsApi.test.ts +++ b/src/test/api/CommercetoolsApi.test.ts @@ -5076,4 +5076,524 @@ describe('CommercetoolsApi', () => { }) }) }) + + describe('Product Types (additional coverage)', () => { + describe('deleteProductTypeById', () => { + it('should make a DELETE request to the correct endpoint with the version param', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .delete('/test-project-key/product-types/my-product-type-id') + .query({ version: 4 }) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.deleteProductTypeById({ id: 'my-product-type-id', version: 4 }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.deleteProductTypeById({ id: ' ', version: 1 })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + + describe('deleteProductTypeByKey', () => { + it('should make a DELETE request to the correct endpoint with the version param', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .delete('/test-project-key/product-types/key=my-product-type-key') + .query({ version: 4 }) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.deleteProductTypeByKey({ key: 'my-product-type-key', version: 4 }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty key is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.deleteProductTypeByKey({ key: ' ', version: 1 })).toThrow( + "The string parameter 'key' cannot be empty", + ) + }) + }) + }) + + describe('States (additional coverage)', () => { + const updateData = { version: 1, actions: [{ action: 'changeKey', key: 'new-key' }] } as any + + describe('updateStateById', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/states/my-state-id', updateData) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.updateStateById({ id: 'my-state-id', data: updateData }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.updateStateById({ id: ' ', data: updateData })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + + describe('updateStateByKey', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/states/key=my-state-key', updateData) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.updateStateByKey({ key: 'my-state-key', data: updateData }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty key is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.updateStateByKey({ key: ' ', data: updateData })).toThrow( + "The string parameter 'key' cannot be empty", + ) + }) + }) + + describe('deleteStateById', () => { + it('should make a DELETE request to the correct endpoint with the version param', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .delete('/test-project-key/states/my-state-id') + .query({ version: 2 }) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.deleteStateById({ id: 'my-state-id', version: 2 }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.deleteStateById({ id: ' ', version: 1 })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + + describe('deleteStateByKey', () => { + it('should make a DELETE request to the correct endpoint with the version param', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .delete('/test-project-key/states/key=my-state-key') + .query({ version: 2 }) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.deleteStateByKey({ key: 'my-state-key', version: 2 }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty key is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.deleteStateByKey({ key: ' ', version: 1 })).toThrow( + "The string parameter 'key' cannot be empty", + ) + }) + }) + }) + + describe('Subscriptions', () => { + const subscriptionDraft = { key: 'my-sub', destination: { type: 'GoogleCloudPubSub' } } as any + const subscriptionUpdate = { version: 1, actions: [{ action: 'setKey', key: 'new-sub-key' }] } as any + + describe('getSubscriptionById', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/subscriptions/my-sub-id') + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.getSubscriptionById({ id: 'my-sub-id' }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.getSubscriptionById({ id: ' ' })).toThrow("The string parameter 'id' cannot be empty") + }) + }) + + describe('getSubscriptionByKey', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/subscriptions/key=my-sub-key') + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.getSubscriptionByKey({ key: 'my-sub-key' }) + + expect(result).toEqual(singleResultItem) + }) + }) + + describe('querySubscriptions', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/subscriptions') + .reply(200, singleItemResponse) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.querySubscriptions() + + expect(result).toEqual(singleItemResponse) + }) + }) + + describe('createSubscription', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/subscriptions', subscriptionDraft) + .reply(201, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.createSubscription({ data: subscriptionDraft }) + + expect(result).toEqual(singleResultItem) + }) + }) + + describe('updateSubscriptionById', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/subscriptions/my-sub-id', subscriptionUpdate) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.updateSubscriptionById({ id: 'my-sub-id', data: subscriptionUpdate }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.updateSubscriptionById({ id: ' ', data: subscriptionUpdate })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + + describe('updateSubscriptionByKey', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/subscriptions/key=my-sub-key', subscriptionUpdate) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.updateSubscriptionByKey({ key: 'my-sub-key', data: subscriptionUpdate }) + + expect(result).toEqual(singleResultItem) + }) + }) + + describe('deleteSubscriptionById', () => { + it('should make a DELETE request to the correct endpoint with the version param', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .delete('/test-project-key/subscriptions/my-sub-id') + .query({ version: 3 }) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.deleteSubscriptionById({ id: 'my-sub-id', version: 3 }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.deleteSubscriptionById({ id: ' ', version: 1 })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + + describe('deleteSubscriptionByKey', () => { + it('should make a DELETE request to the correct endpoint with the version param', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .delete('/test-project-key/subscriptions/key=my-sub-key') + .query({ version: 3 }) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.deleteSubscriptionByKey({ key: 'my-sub-key', version: 3 }) + + expect(result).toEqual(singleResultItem) + }) + }) + }) + + describe('Recurrence policies (additional coverage)', () => { + const recurrencePolicyUpdate = { version: 1, actions: [{ action: 'setKey', key: 'new-key' }] } as any + + describe('getRecurrencePolicyById', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/recurrence-policies/my-policy-id') + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.getRecurrencePolicyById({ id: 'my-policy-id' }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.getRecurrencePolicyById({ id: ' ' })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + + describe('queryRecurrencePolicies', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/recurrence-policies') + .reply(200, singleItemResponse) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.queryRecurrencePolicies() + + expect(result).toEqual(singleItemResponse) + }) + }) + + describe('checkRecurrencePolicyExistsById', () => { + it('should return true on a 200', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurrence-policies/my-policy-id') + .reply(200) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurrencePolicyExistsById({ id: 'my-policy-id' })).resolves.toBe(true) + }) + + it('should return false on a 404', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurrence-policies/my-policy-id') + .reply(404) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurrencePolicyExistsById({ id: 'my-policy-id' })).resolves.toBe(false) + }) + + it('should throw on a non-404 error', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurrence-policies/my-policy-id') + .reply(500) + .persist() + const api = new CommercetoolsApi(defaultConfig) + + await expect(() => api.checkRecurrencePolicyExistsById({ id: 'my-policy-id' })).rejects.toThrow( + 'Request failed with status code 500', + ) + }) + }) + + describe('checkRecurrencePolicyExists', () => { + it('should return true on a 200', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurrence-policies?where=some-predicate') + .reply(200) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurrencePolicyExists({ params: { where: 'some-predicate' } })).resolves.toBe(true) + }) + + it('should return false on a 404', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurrence-policies?where=some-predicate') + .reply(404) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurrencePolicyExists({ params: { where: 'some-predicate' } })).resolves.toBe(false) + }) + + it('should throw on a non-404 error', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurrence-policies?where=some-predicate') + .reply(500) + .persist() + const api = new CommercetoolsApi(defaultConfig) + + await expect(() => api.checkRecurrencePolicyExists({ params: { where: 'some-predicate' } })).rejects.toThrow( + 'Request failed with status code 500', + ) + }) + }) + + describe('updateRecurrencePolicyById', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/recurrence-policies/my-policy-id', recurrencePolicyUpdate) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.updateRecurrencePolicyById({ + id: 'my-policy-id', + data: recurrencePolicyUpdate, + }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.updateRecurrencePolicyById({ id: ' ', data: recurrencePolicyUpdate })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + }) + + describe('Recurring orders (additional coverage)', () => { + const recurringOrderDraft = { cart: { typeId: 'cart', id: 'cart-id' } } as any + const recurringOrderUpdate = { version: 1, actions: [{ action: 'setKey', key: 'new-key' }] } as any + + describe('getRecurringOrderById', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/recurring-orders/my-order-id') + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.getRecurringOrderById({ id: 'my-order-id' }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.getRecurringOrderById({ id: ' ' })).toThrow("The string parameter 'id' cannot be empty") + }) + }) + + describe('queryRecurringOrders', () => { + it('should make a GET request to the correct endpoint', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .get('/test-project-key/recurring-orders') + .reply(200, singleItemResponse) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.queryRecurringOrders() + + expect(result).toEqual(singleItemResponse) + }) + }) + + describe('checkRecurringOrderExistsById', () => { + it('should return true on a 200', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurring-orders/my-order-id') + .reply(200) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurringOrderExistsById({ id: 'my-order-id' })).resolves.toBe(true) + }) + + it('should return false on a 404', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurring-orders/my-order-id') + .reply(404) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurringOrderExistsById({ id: 'my-order-id' })).resolves.toBe(false) + }) + + it('should throw on a non-404 error', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurring-orders/my-order-id') + .reply(500) + .persist() + const api = new CommercetoolsApi(defaultConfig) + + await expect(() => api.checkRecurringOrderExistsById({ id: 'my-order-id' })).rejects.toThrow( + 'Request failed with status code 500', + ) + }) + }) + + describe('checkRecurringOrderExists', () => { + it('should return true on a 200', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurring-orders?where=some-predicate') + .reply(200) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurringOrderExists({ params: { where: 'some-predicate' } })).resolves.toBe(true) + }) + + it('should return false on a 404', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurring-orders?where=some-predicate') + .reply(404) + const api = new CommercetoolsApi(defaultConfig) + + await expect(api.checkRecurringOrderExists({ params: { where: 'some-predicate' } })).resolves.toBe(false) + }) + + it('should throw on a non-404 error', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .head('/test-project-key/recurring-orders?where=some-predicate') + .reply(500) + .persist() + const api = new CommercetoolsApi(defaultConfig) + + await expect(() => api.checkRecurringOrderExists({ params: { where: 'some-predicate' } })).rejects.toThrow( + 'Request failed with status code 500', + ) + }) + }) + + describe('createRecurringOrder', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/recurring-orders', recurringOrderDraft) + .reply(201, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.createRecurringOrder({ data: recurringOrderDraft }) + + expect(result).toEqual(singleResultItem) + }) + }) + + describe('updateRecurringOrderById', () => { + it('should make a POST request to the correct endpoint with the provided data', async () => { + nock('https://api.europe-west1.gcp.commercetools.com') + .post('/test-project-key/recurring-orders/my-order-id', recurringOrderUpdate) + .reply(200, singleResultItem) + const api = new CommercetoolsApi(defaultConfig) + + const result = await api.updateRecurringOrderById({ + id: 'my-order-id', + data: recurringOrderUpdate, + }) + + expect(result).toEqual(singleResultItem) + }) + + it('should throw an error when an empty id is passed', async () => { + const api = new CommercetoolsApi(defaultConfig) + await expect(() => api.updateRecurringOrderById({ id: ' ', data: recurringOrderUpdate })).toThrow( + "The string parameter 'id' cannot be empty", + ) + }) + }) + }) }) diff --git a/src/test/auth/CommercetoolsAuth.test.ts b/src/test/auth/CommercetoolsAuth.test.ts index 8af7fec0a6..5dba6fd54f 100644 --- a/src/test/auth/CommercetoolsAuth.test.ts +++ b/src/test/auth/CommercetoolsAuth.test.ts @@ -693,6 +693,21 @@ describe('CommercetoolsAuth', () => { }) }) + describe('revokeToken', () => { + it('should delegate to the underlying api.revokeToken method', async () => { + // Covers line 275 of CommercetoolsAuth.ts. + const auth = new CommercetoolsAuth(defaultConfig) + const scope = nock('https://auth.us-east-2.aws.commercetools.com') + .post('/oauth/token/revoke', `token=my-access-token&token_type_hint=access_token`) + .reply(200, {}) + + await expect( + auth.revokeToken({ tokenValue: 'my-access-token', tokenType: 'access_token' }), + ).resolves.toBeUndefined() + expect(scope.isDone()).toBe(true) + }) + }) + describe('multiple simultaneous requests', () => { it('should make all requests wait for the pending client credentials', async () => { const scope = nock('https://auth.us-east-2.aws.commercetools.com', { diff --git a/src/test/auth/CommercetoolsAuthApi.test.ts b/src/test/auth/CommercetoolsAuthApi.test.ts index 6daa4916c5..d4dccd2694 100644 --- a/src/test/auth/CommercetoolsAuthApi.test.ts +++ b/src/test/auth/CommercetoolsAuthApi.test.ts @@ -595,4 +595,49 @@ describe('CommercetoolsAuthApi', () => { } }) }) + + describe('getRequestOptions', () => { + it('uses a provided correlationId when passed in', () => { + // Covers lines 177-178 of CommercetoolsAuthApi.ts — the explicit + // `correlationId` branch in `getRequestOptions`. + const api = new CommercetoolsAuthApi(defaultConfig) + + const result = api.getRequestOptions({ + path: '/token', + correlationId: 'my-correlation-id', + body: { grant_type: 'client_credentials' }, + }) + + expect(result.headers?.['X-Correlation-ID']).toBe('my-correlation-id') + expect(result.method).toBe('POST') + expect(result.url).toBe('https://auth.us-east-2.aws.commercetools.com/oauth/token') + }) + + it('generates a UUID correlationId when none is provided', () => { + const api = new CommercetoolsAuthApi(defaultConfig) + + const result = api.getRequestOptions({ + path: '/token', + body: { grant_type: 'client_credentials' }, + }) + + expect(result.headers?.['X-Correlation-ID']).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, + ) + }) + + it('treats an empty string correlationId as missing and generates a UUID', () => { + const api = new CommercetoolsAuthApi(defaultConfig) + + const result = api.getRequestOptions({ + path: '/token', + correlationId: '', + body: { grant_type: 'client_credentials' }, + }) + + expect(result.headers?.['X-Correlation-ID']).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, + ) + }) + }) }) diff --git a/src/test/utils/axios/convert-error.test.ts b/src/test/utils/axios/convert-error.test.ts index e0c7643009..6065a499e1 100644 --- a/src/test/utils/axios/convert-error.test.ts +++ b/src/test/utils/axios/convert-error.test.ts @@ -68,6 +68,14 @@ describe('convertAxiosError', () => { expect(result?.response.tlsVersion).toBeUndefined() }) + it('falls back to an empty string when the request config has no url', () => { + // Covers the `error.config?.url ?? ''` fallback branch on line 15. + const error = buildAxiosError({ config: undefined as unknown as AxiosError['config'] }) + const result = convertAxiosError(error, stats) + expect(result?.request.url).toBe('') + expect(result?.request.method).toBeUndefined() + }) + it('produces the expected full shape including tlsVersion', () => { const error = buildAxiosError({ response: { diff --git a/src/test/utils/axios/convert-response.test.ts b/src/test/utils/axios/convert-response.test.ts index 5e532d87c5..33eb0dc491 100644 --- a/src/test/utils/axios/convert-response.test.ts +++ b/src/test/utils/axios/convert-response.test.ts @@ -57,6 +57,16 @@ describe('convertAxiosResponse', () => { expect(result.response.tlsVersion).toBeUndefined() }) + it('falls back to an empty string when the response config has no url', () => { + // Covers the `response.config?.url ?? ''` fallback branch on line 12. + const result = convertAxiosResponse( + buildResponse({ config: undefined as unknown as AxiosResponse['config'] }), + stats, + ) + expect(result.request.url).toBe('') + expect(result.request.method).toBeUndefined() + }) + it('includes the other response fields alongside tlsVersion', () => { const result = convertAxiosResponse(buildResponse(), stats) expect(result).toEqual({ diff --git a/src/test/utils/axios/extract-headers.test.ts b/src/test/utils/axios/extract-headers.test.ts new file mode 100644 index 0000000000..dd8ac1f8a4 --- /dev/null +++ b/src/test/utils/axios/extract-headers.test.ts @@ -0,0 +1,36 @@ +import { AxiosHeaders } from 'axios' +import { extractAxiosHeaders } from '../../../lib/utils/axios/extract-headers.js' + +describe('extractAxiosHeaders', () => { + it('returns undefined when given non-AxiosHeaders input', () => { + expect(extractAxiosHeaders(undefined)).toBeUndefined() + expect(extractAxiosHeaders(null)).toBeUndefined() + expect(extractAxiosHeaders({ accept: 'application/json' })).toBeUndefined() + }) + + it('returns undefined when given an empty AxiosHeaders instance', () => { + expect(extractAxiosHeaders(new AxiosHeaders())).toBeUndefined() + }) + + it('lowercases header names and stringifies scalar values', () => { + const headers = new AxiosHeaders() + headers.set('Accept', 'application/json') + headers.set('X-Number', 42) + headers.set('X-Boolean', true) + + expect(extractAxiosHeaders(headers)).toEqual({ + accept: 'application/json', + 'x-number': '42', + 'x-boolean': 'true', + }) + }) + + it('joins array header values with a comma and space', () => { + const headers = new AxiosHeaders() + headers.set('Set-Cookie', ['a=1', 'b=2']) + + expect(extractAxiosHeaders(headers)).toEqual({ + 'set-cookie': 'a=1, b=2', + }) + }) +}) diff --git a/src/test/utils/get-socket-stats.test.ts b/src/test/utils/get-socket-stats.test.ts index 6a0e42fc42..687926980d 100644 --- a/src/test/utils/get-socket-stats.test.ts +++ b/src/test/utils/get-socket-stats.test.ts @@ -70,4 +70,30 @@ describe('getSocketStats', () => { queuedRequests: 0, }) }) + + it('treats entries with nullish values as zero', () => { + // Covers the `?.length ?? 0` fallback branches on lines 12/17/22 where + // an enumerated key has an explicitly undefined value. + const mockAgent = { + sockets: { + 'host-a:443': undefined, + 'host-b:443': [1, 2], + }, + freeSockets: { + 'host-a:443': undefined, + 'host-b:443': [3], + }, + requests: { + 'host-a:443': undefined, + 'host-b:443': [4, 5, 6], + }, + } as unknown as https.Agent + + const stats = getSocketStats(mockAgent) + expect(stats).toEqual({ + activeSockets: 2, + freeSocketCount: 1, + queuedRequests: 3, + }) + }) }) diff --git a/src/test/utils/mask.test.ts b/src/test/utils/mask.test.ts index b04273579f..5bd3bb53ff 100644 --- a/src/test/utils/mask.test.ts +++ b/src/test/utils/mask.test.ts @@ -1,4 +1,5 @@ -import { maskSensitiveData } from '../../lib/utils/mask.js' +import { AxiosHeaders } from 'axios' +import { maskSensitiveData, maskSensitiveHeaders } from '../../lib/utils/mask.js' describe('maskSensitiveData', () => { it('should return null if null is passed in', () => { @@ -37,6 +38,22 @@ describe('maskSensitiveData', () => { }) }) + describe('AxiosHeaders input', () => { + it('should serialize an AxiosHeaders instance via toJSON before masking', () => { + // Covers the `data instanceof AxiosHeaders` branch on line 43 of mask.ts. + const headers = new AxiosHeaders() + headers.set('Authorization', 'Bearer secret-token') + headers.set('Accept', 'application/json') + + const result = maskSensitiveHeaders(headers) + + expect(result).toEqual({ + Authorization: '********', + Accept: 'application/json', + }) + }) + }) + describe('deeply nested object', () => { it('should mask all matching field names, regardless of nesting level', () => { const result = maskSensitiveData( diff --git a/src/test/utils/plain-clone.test.ts b/src/test/utils/plain-clone.test.ts index 5d0890dfad..24b465e778 100644 --- a/src/test/utils/plain-clone.test.ts +++ b/src/test/utils/plain-clone.test.ts @@ -103,4 +103,17 @@ describe('plainClone', function () { }, }) }) + + it('should ignore inherited (prototype-chain) properties', () => { + // Covers the `hasOwnProperty` false branch on line 20 — properties that + // come from the prototype chain must not be copied across. + const proto = { inherited: 'should-not-be-copied' } + const source = Object.create(proto) + source.own = 'should-be-copied' + + const result = plainClone(source) + + expect(result).toEqual({ own: 'should-be-copied' }) + expect(result.inherited).toBeUndefined() + }) })