├── .env.sample ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── renovate.json ├── restlet-demo.js ├── src ├── client.ts ├── errors.ts ├── index.ts ├── types.ts └── utils.ts ├── test ├── basic.test.ts ├── query.test.ts ├── request.test.ts └── utils.test.ts ├── tsconfig.json └── vitest.config.ts /.env.sample: -------------------------------------------------------------------------------- 1 | consumer_key="" 2 | consumer_secret_key="" 3 | token="" 4 | token_secret="" 5 | realm="" 6 | base_url="" -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | pull_request: 5 | 6 | env: 7 | CI: true 8 | realm: ${{secrets.realm}} 9 | base_url: ${{secrets.base_url}} 10 | consumer_key: ${{secrets.consumer_key}} 11 | consumer_secret_key: ${{secrets.consumer_secret_key}} 12 | token: ${{secrets.token}} 13 | token_secret: ${{secrets.token_secret}} 14 | 15 | jobs: 16 | test: 17 | name: Node ${{ matrix.node }} 18 | runs-on: ubuntu-latest 19 | 20 | strategy: 21 | matrix: 22 | node: [20] 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | - uses: pnpm/action-setup@v4 27 | with: 28 | version: 9 29 | 30 | - name: Set Node.js version 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: ${{ matrix.node }} 34 | cache: "pnpm" 35 | 36 | - name: Install dependencies 37 | run: pnpm install 38 | 39 | - name: Run tests 40 | run: pnpm coverage 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # tsc output 2 | dist/ 3 | 4 | # node packages 5 | node_modules/ 6 | 7 | # coverage output 8 | coverage/ 9 | 10 | # env files 11 | .env 12 | 13 | # output of yarn pack 14 | *.tgz 15 | 16 | # yarn errors 17 | yarn-error.log 18 | 19 | # IDE files 20 | .vscode/ 21 | .idea/ 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | coverage/ 4 | .github/ 5 | 6 | .env 7 | .env.sample 8 | tsconfig.json 9 | 10 | yarn-error.log 11 | *.tgz -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Julien Bras 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Netsuite API Client 2 | 3 | [![Node.js CI](https://github.com/julbrs/netsuite-api-client/actions/workflows/node.js.yml/badge.svg)](https://github.com/julbrs/netsuite-api-client/actions/workflows/node.js.yml) [![npm version](https://badge.fury.io/js/netsuite-api-client.svg)](https://www.npmjs.com/package/netsuite-api-client) [![downloads](https://img.shields.io/npm/dm/netsuite-api-client.svg)](https://www.npmjs.com/package/netsuite-api-client) 4 | 5 | Run REST calls and SuiteQL queries against NetSuite SuiteTalk WebServices. 6 | 7 | ## Why this new package? 8 | 9 | This package is a fork & merge of popular Netsuite packages [netsuite-rest](https://www.npmjs.com/package/netsuite-rest) & [suiteql](https://www.npmjs.com/package/suiteql), see [here](https://github.com/ehmad11/netsuite-rest/issues/27#issuecomment-1751798730) for the motivation. This package is **ESM only**. It adds **types** thanks to TypeScript, and **more recent dependencies** to handle recent CVEs on `got` package mainly. It meant to be **actively supported** in the future. 10 | 11 | If you need to stay on **CommonJS**, you can still use [netsuite-rest](https://www.npmjs.com/package/netsuite-rest) or [suiteql](https://www.npmjs.com/package/suiteql) by [ehmad11](https://github.com/ehmad11)! 12 | 13 | # Installation 14 | 15 | ``` 16 | npm i netsuite-api-client 17 | ``` 18 | 19 | ## Quick Start 20 | 21 | To set up TBA in Netsuite, see the help topic [Getting Started with Token-based Authentication](https://system.netsuite.com/app/help/helpcenter.nl?fid=section_4247337262.html). 22 | 23 | ```ts 24 | import { NetsuiteApiClient } from "netsuite-api-client"; 25 | const client = new NetsuiteApiClient({ 26 | consumer_key: process.env.consumer_key, 27 | consumer_secret_key: process.env.consumer_secret_key, 28 | token: process.env.token, 29 | token_secret: process.env.token_secret, 30 | realm: process.env.realm, 31 | base_url: process.env.base_url, 32 | }); 33 | ``` 34 | 35 | ## request 36 | 37 | ### Test 38 | 39 | ```ts 40 | client 41 | .request({ 42 | path: "*", 43 | method: "OPTIONS", 44 | }) 45 | .then((response) => console.log(response)) 46 | .catch((err) => console.log(err)); 47 | ``` 48 | 49 | ### GET 50 | 51 | ```ts 52 | client 53 | .request({ 54 | path: "record/v1/customer/", 55 | }) 56 | .then((response) => response.data) 57 | .then((data) => console.log(data.links)) 58 | .catch((err) => console.log(err)); 59 | ``` 60 | 61 | ## query 62 | 63 | ```ts 64 | query(string, (limit = 1000), (offset = 0)); 65 | ``` 66 | 67 | - **string** - Select query to run 68 | - **limit** - Limit number of rows, max is 1000 69 | - **offset** - Rows to start from 70 | 71 | This method returns with the promise support, response will be in JSON format 72 | 73 | ### Example 74 | 75 | ```ts 76 | const transactions = await client.query("select id from transaction", 10, 0); 77 | ``` 78 | 79 | ## queryAll (Stream) 80 | 81 | When working on large number of rows, stream is handy 82 | 83 | ```ts 84 | queryAll(string, (limit = 1000)); 85 | ``` 86 | 87 | - **string** - Select query to run 88 | - **limit** - Limit number of rows, max is 1000 89 | 90 | ### Example 91 | 92 | ```ts 93 | const items = []; 94 | const st = client.queryAll(` 95 | select 96 | tranid, id from transaction 97 | where 98 | rownum <= 30 99 | `); 100 | 101 | st.on("data", (data) => { 102 | items.push(data); 103 | }); 104 | 105 | st.on("end", () => { 106 | console.log("stream ended"); 107 | }); 108 | 109 | st.on("error", (err) => { 110 | console.log(err); 111 | }); 112 | ``` 113 | 114 | ## Response 115 | 116 | Requests are returned with promise support. HTTP response codes other than 2xx will cause the promise to be rejected. 117 | 118 | The Netsuite error will be preserved in the error message, so you don't have to read the got `HttpError` ! For example: 119 | 120 | ``` 121 | Invalid search query. Detailed unprocessed description follows. Search error occurred: Unknown identifier 'not_existing_field'. Available identifiers are: {customer=customer} 122 | ``` 123 | 124 | ## Metadata 125 | 126 | ```ts 127 | client.request({ path: "record/v1/metadata-catalog/" }); 128 | ``` 129 | 130 | Record is the name of the service we are trying to access, v1 is the service version, and metadata-catalog is the sub-resource, that is, the record metadata. The response informs you through HATEOAS links about the possible mediaType flavor in which the response can be obtained. 131 | 132 | ## HATEOAS 133 | 134 | You can navigate to the referenced resources without deeper knowledge of the system. A typical response contains "links" sections for each resource, which can be a sub-resource of a parent resource or any other referenced resource. You can use links to work with those resources. 135 | 136 | ## More Resources 137 | 138 | ### SuiteTalk REST Web Services 139 | 140 | [Overview and Setup](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/chapter_1540391670.html) - Official Documentation 141 | 142 | ### Netsuite Rest API Browser 143 | 144 | [REST API Browser](https://system.netsuite.com/help/helpcenter/en_US/APIs/REST_API_Browser/record/v1/2021.2/index.html) provides a visual overview of the structure and capabilities of the REST web services Record API. The data presented in the REST API Browser is based on OpenAPI 3.0 metadata. 145 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "netsuite-api-client", 3 | "version": "1.0.3", 4 | "description": "Netsuite client for REST interface", 5 | "keywords": [ 6 | "netsuite", 7 | "suitetalk", 8 | "rest", 9 | "web", 10 | "services", 11 | "promise", 12 | "suiteql" 13 | ], 14 | "type": "module", 15 | "author": "Julien Bras ", 16 | "homepage": "https://github.com/julbrs/netsuite-api-client#readme", 17 | "bugs": "https://github.com/julbrs/netsuite-api-client/issues", 18 | "exports": { 19 | ".": "./dist/index.js" 20 | }, 21 | "types": "dist/index.d.ts", 22 | "scripts": { 23 | "build": "tsc --build --clean && tsc", 24 | "test": "vitest", 25 | "coverage": "vitest --coverage", 26 | "prepublishOnly": "yarn build" 27 | }, 28 | "devDependencies": { 29 | "@tsconfig/node20": "^20.1.4", 30 | "@types/got": "^9.6.12", 31 | "@vitest/coverage-v8": "^3.0.0", 32 | "dotenv": "^16.3.1", 33 | "typescript": "^5.2.2", 34 | "vitest": "^3.0.0" 35 | }, 36 | "dependencies": { 37 | "got": "^14.0.0", 38 | "oauth-1.0a": "^2.2.6" 39 | }, 40 | "repository": { 41 | "type": "git", 42 | "url": "https://github.com/julbrs/netsuite-api-client.git" 43 | }, 44 | "license": "MIT" 45 | } 46 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | got: 12 | specifier: ^14.0.0 13 | version: 14.4.6 14 | oauth-1.0a: 15 | specifier: ^2.2.6 16 | version: 2.2.6 17 | devDependencies: 18 | '@tsconfig/node20': 19 | specifier: ^20.1.4 20 | version: 20.1.4 21 | '@types/got': 22 | specifier: ^9.6.12 23 | version: 9.6.12 24 | '@vitest/coverage-v8': 25 | specifier: ^3.0.0 26 | version: 3.0.7(vitest@3.0.7(@types/node@22.8.0)) 27 | dotenv: 28 | specifier: ^16.3.1 29 | version: 16.4.7 30 | typescript: 31 | specifier: ^5.2.2 32 | version: 5.8.2 33 | vitest: 34 | specifier: ^3.0.0 35 | version: 3.0.7(@types/node@22.8.0) 36 | 37 | packages: 38 | 39 | '@ampproject/remapping@2.3.0': 40 | resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 41 | engines: {node: '>=6.0.0'} 42 | 43 | '@babel/helper-string-parser@7.25.9': 44 | resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} 45 | engines: {node: '>=6.9.0'} 46 | 47 | '@babel/helper-validator-identifier@7.25.9': 48 | resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} 49 | engines: {node: '>=6.9.0'} 50 | 51 | '@babel/parser@7.26.9': 52 | resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} 53 | engines: {node: '>=6.0.0'} 54 | hasBin: true 55 | 56 | '@babel/types@7.26.9': 57 | resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} 58 | engines: {node: '>=6.9.0'} 59 | 60 | '@bcoe/v8-coverage@1.0.2': 61 | resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} 62 | engines: {node: '>=18'} 63 | 64 | '@esbuild/aix-ppc64@0.21.5': 65 | resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} 66 | engines: {node: '>=12'} 67 | cpu: [ppc64] 68 | os: [aix] 69 | 70 | '@esbuild/android-arm64@0.21.5': 71 | resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} 72 | engines: {node: '>=12'} 73 | cpu: [arm64] 74 | os: [android] 75 | 76 | '@esbuild/android-arm@0.21.5': 77 | resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} 78 | engines: {node: '>=12'} 79 | cpu: [arm] 80 | os: [android] 81 | 82 | '@esbuild/android-x64@0.21.5': 83 | resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} 84 | engines: {node: '>=12'} 85 | cpu: [x64] 86 | os: [android] 87 | 88 | '@esbuild/darwin-arm64@0.21.5': 89 | resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} 90 | engines: {node: '>=12'} 91 | cpu: [arm64] 92 | os: [darwin] 93 | 94 | '@esbuild/darwin-x64@0.21.5': 95 | resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} 96 | engines: {node: '>=12'} 97 | cpu: [x64] 98 | os: [darwin] 99 | 100 | '@esbuild/freebsd-arm64@0.21.5': 101 | resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} 102 | engines: {node: '>=12'} 103 | cpu: [arm64] 104 | os: [freebsd] 105 | 106 | '@esbuild/freebsd-x64@0.21.5': 107 | resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} 108 | engines: {node: '>=12'} 109 | cpu: [x64] 110 | os: [freebsd] 111 | 112 | '@esbuild/linux-arm64@0.21.5': 113 | resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} 114 | engines: {node: '>=12'} 115 | cpu: [arm64] 116 | os: [linux] 117 | 118 | '@esbuild/linux-arm@0.21.5': 119 | resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} 120 | engines: {node: '>=12'} 121 | cpu: [arm] 122 | os: [linux] 123 | 124 | '@esbuild/linux-ia32@0.21.5': 125 | resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} 126 | engines: {node: '>=12'} 127 | cpu: [ia32] 128 | os: [linux] 129 | 130 | '@esbuild/linux-loong64@0.21.5': 131 | resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} 132 | engines: {node: '>=12'} 133 | cpu: [loong64] 134 | os: [linux] 135 | 136 | '@esbuild/linux-mips64el@0.21.5': 137 | resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} 138 | engines: {node: '>=12'} 139 | cpu: [mips64el] 140 | os: [linux] 141 | 142 | '@esbuild/linux-ppc64@0.21.5': 143 | resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} 144 | engines: {node: '>=12'} 145 | cpu: [ppc64] 146 | os: [linux] 147 | 148 | '@esbuild/linux-riscv64@0.21.5': 149 | resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} 150 | engines: {node: '>=12'} 151 | cpu: [riscv64] 152 | os: [linux] 153 | 154 | '@esbuild/linux-s390x@0.21.5': 155 | resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} 156 | engines: {node: '>=12'} 157 | cpu: [s390x] 158 | os: [linux] 159 | 160 | '@esbuild/linux-x64@0.21.5': 161 | resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} 162 | engines: {node: '>=12'} 163 | cpu: [x64] 164 | os: [linux] 165 | 166 | '@esbuild/netbsd-x64@0.21.5': 167 | resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} 168 | engines: {node: '>=12'} 169 | cpu: [x64] 170 | os: [netbsd] 171 | 172 | '@esbuild/openbsd-x64@0.21.5': 173 | resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} 174 | engines: {node: '>=12'} 175 | cpu: [x64] 176 | os: [openbsd] 177 | 178 | '@esbuild/sunos-x64@0.21.5': 179 | resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} 180 | engines: {node: '>=12'} 181 | cpu: [x64] 182 | os: [sunos] 183 | 184 | '@esbuild/win32-arm64@0.21.5': 185 | resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} 186 | engines: {node: '>=12'} 187 | cpu: [arm64] 188 | os: [win32] 189 | 190 | '@esbuild/win32-ia32@0.21.5': 191 | resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} 192 | engines: {node: '>=12'} 193 | cpu: [ia32] 194 | os: [win32] 195 | 196 | '@esbuild/win32-x64@0.21.5': 197 | resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} 198 | engines: {node: '>=12'} 199 | cpu: [x64] 200 | os: [win32] 201 | 202 | '@isaacs/cliui@8.0.2': 203 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 204 | engines: {node: '>=12'} 205 | 206 | '@istanbuljs/schema@0.1.3': 207 | resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} 208 | engines: {node: '>=8'} 209 | 210 | '@jridgewell/gen-mapping@0.3.8': 211 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 212 | engines: {node: '>=6.0.0'} 213 | 214 | '@jridgewell/resolve-uri@3.1.2': 215 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 216 | engines: {node: '>=6.0.0'} 217 | 218 | '@jridgewell/set-array@1.2.1': 219 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 220 | engines: {node: '>=6.0.0'} 221 | 222 | '@jridgewell/sourcemap-codec@1.5.0': 223 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 224 | 225 | '@jridgewell/trace-mapping@0.3.25': 226 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 227 | 228 | '@pkgjs/parseargs@0.11.0': 229 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 230 | engines: {node: '>=14'} 231 | 232 | '@rollup/rollup-android-arm-eabi@4.34.9': 233 | resolution: {integrity: sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==} 234 | cpu: [arm] 235 | os: [android] 236 | 237 | '@rollup/rollup-android-arm64@4.34.9': 238 | resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} 239 | cpu: [arm64] 240 | os: [android] 241 | 242 | '@rollup/rollup-darwin-arm64@4.34.9': 243 | resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} 244 | cpu: [arm64] 245 | os: [darwin] 246 | 247 | '@rollup/rollup-darwin-x64@4.34.9': 248 | resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} 249 | cpu: [x64] 250 | os: [darwin] 251 | 252 | '@rollup/rollup-freebsd-arm64@4.34.9': 253 | resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} 254 | cpu: [arm64] 255 | os: [freebsd] 256 | 257 | '@rollup/rollup-freebsd-x64@4.34.9': 258 | resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} 259 | cpu: [x64] 260 | os: [freebsd] 261 | 262 | '@rollup/rollup-linux-arm-gnueabihf@4.34.9': 263 | resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} 264 | cpu: [arm] 265 | os: [linux] 266 | 267 | '@rollup/rollup-linux-arm-musleabihf@4.34.9': 268 | resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} 269 | cpu: [arm] 270 | os: [linux] 271 | 272 | '@rollup/rollup-linux-arm64-gnu@4.34.9': 273 | resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} 274 | cpu: [arm64] 275 | os: [linux] 276 | 277 | '@rollup/rollup-linux-arm64-musl@4.34.9': 278 | resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} 279 | cpu: [arm64] 280 | os: [linux] 281 | 282 | '@rollup/rollup-linux-loongarch64-gnu@4.34.9': 283 | resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} 284 | cpu: [loong64] 285 | os: [linux] 286 | 287 | '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': 288 | resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} 289 | cpu: [ppc64] 290 | os: [linux] 291 | 292 | '@rollup/rollup-linux-riscv64-gnu@4.34.9': 293 | resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} 294 | cpu: [riscv64] 295 | os: [linux] 296 | 297 | '@rollup/rollup-linux-s390x-gnu@4.34.9': 298 | resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} 299 | cpu: [s390x] 300 | os: [linux] 301 | 302 | '@rollup/rollup-linux-x64-gnu@4.34.9': 303 | resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} 304 | cpu: [x64] 305 | os: [linux] 306 | 307 | '@rollup/rollup-linux-x64-musl@4.34.9': 308 | resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} 309 | cpu: [x64] 310 | os: [linux] 311 | 312 | '@rollup/rollup-win32-arm64-msvc@4.34.9': 313 | resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} 314 | cpu: [arm64] 315 | os: [win32] 316 | 317 | '@rollup/rollup-win32-ia32-msvc@4.34.9': 318 | resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} 319 | cpu: [ia32] 320 | os: [win32] 321 | 322 | '@rollup/rollup-win32-x64-msvc@4.34.9': 323 | resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} 324 | cpu: [x64] 325 | os: [win32] 326 | 327 | '@sec-ant/readable-stream@0.4.1': 328 | resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} 329 | 330 | '@sindresorhus/is@7.0.1': 331 | resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==} 332 | engines: {node: '>=18'} 333 | 334 | '@szmarczak/http-timer@5.0.1': 335 | resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} 336 | engines: {node: '>=14.16'} 337 | 338 | '@tsconfig/node20@20.1.4': 339 | resolution: {integrity: sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==} 340 | 341 | '@types/estree@1.0.6': 342 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 343 | 344 | '@types/got@9.6.12': 345 | resolution: {integrity: sha512-X4pj/HGHbXVLqTpKjA2ahI4rV/nNBc9mGO2I/0CgAra+F2dKgMXnENv2SRpemScBzBAI4vMelIVYViQxlSE6xA==} 346 | 347 | '@types/http-cache-semantics@4.0.4': 348 | resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} 349 | 350 | '@types/node@22.8.0': 351 | resolution: {integrity: sha512-84rafSBHC/z1i1E3p0cJwKA+CfYDNSXX9WSZBRopjIzLET8oNt6ht2tei4C7izwDeEiLLfdeSVBv1egOH916hg==} 352 | 353 | '@types/tough-cookie@4.0.5': 354 | resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} 355 | 356 | '@vitest/coverage-v8@3.0.7': 357 | resolution: {integrity: sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==} 358 | peerDependencies: 359 | '@vitest/browser': 3.0.7 360 | vitest: 3.0.7 361 | peerDependenciesMeta: 362 | '@vitest/browser': 363 | optional: true 364 | 365 | '@vitest/expect@3.0.7': 366 | resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} 367 | 368 | '@vitest/mocker@3.0.7': 369 | resolution: {integrity: sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==} 370 | peerDependencies: 371 | msw: ^2.4.9 372 | vite: ^5.0.0 || ^6.0.0 373 | peerDependenciesMeta: 374 | msw: 375 | optional: true 376 | vite: 377 | optional: true 378 | 379 | '@vitest/pretty-format@3.0.7': 380 | resolution: {integrity: sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==} 381 | 382 | '@vitest/runner@3.0.7': 383 | resolution: {integrity: sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==} 384 | 385 | '@vitest/snapshot@3.0.7': 386 | resolution: {integrity: sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==} 387 | 388 | '@vitest/spy@3.0.7': 389 | resolution: {integrity: sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==} 390 | 391 | '@vitest/utils@3.0.7': 392 | resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} 393 | 394 | ansi-regex@5.0.1: 395 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 396 | engines: {node: '>=8'} 397 | 398 | ansi-regex@6.1.0: 399 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 400 | engines: {node: '>=12'} 401 | 402 | ansi-styles@4.3.0: 403 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 404 | engines: {node: '>=8'} 405 | 406 | ansi-styles@6.2.1: 407 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 408 | engines: {node: '>=12'} 409 | 410 | assertion-error@2.0.1: 411 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 412 | engines: {node: '>=12'} 413 | 414 | asynckit@0.4.0: 415 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 416 | 417 | balanced-match@1.0.2: 418 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 419 | 420 | brace-expansion@2.0.1: 421 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 422 | 423 | cac@6.7.14: 424 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 425 | engines: {node: '>=8'} 426 | 427 | cacheable-lookup@7.0.0: 428 | resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} 429 | engines: {node: '>=14.16'} 430 | 431 | cacheable-request@12.0.1: 432 | resolution: {integrity: sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==} 433 | engines: {node: '>=18'} 434 | 435 | chai@5.2.0: 436 | resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} 437 | engines: {node: '>=12'} 438 | 439 | check-error@2.1.1: 440 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 441 | engines: {node: '>= 16'} 442 | 443 | color-convert@2.0.1: 444 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 445 | engines: {node: '>=7.0.0'} 446 | 447 | color-name@1.1.4: 448 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 449 | 450 | combined-stream@1.0.8: 451 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 452 | engines: {node: '>= 0.8'} 453 | 454 | cross-spawn@7.0.6: 455 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 456 | engines: {node: '>= 8'} 457 | 458 | debug@4.4.0: 459 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 460 | engines: {node: '>=6.0'} 461 | peerDependencies: 462 | supports-color: '*' 463 | peerDependenciesMeta: 464 | supports-color: 465 | optional: true 466 | 467 | decompress-response@6.0.0: 468 | resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} 469 | engines: {node: '>=10'} 470 | 471 | deep-eql@5.0.2: 472 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 473 | engines: {node: '>=6'} 474 | 475 | defer-to-connect@2.0.1: 476 | resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} 477 | engines: {node: '>=10'} 478 | 479 | delayed-stream@1.0.0: 480 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 481 | engines: {node: '>=0.4.0'} 482 | 483 | dotenv@16.4.7: 484 | resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} 485 | engines: {node: '>=12'} 486 | 487 | eastasianwidth@0.2.0: 488 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 489 | 490 | emoji-regex@8.0.0: 491 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 492 | 493 | emoji-regex@9.2.2: 494 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 495 | 496 | es-module-lexer@1.6.0: 497 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 498 | 499 | esbuild@0.21.5: 500 | resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} 501 | engines: {node: '>=12'} 502 | hasBin: true 503 | 504 | estree-walker@3.0.3: 505 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 506 | 507 | expect-type@1.2.0: 508 | resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==} 509 | engines: {node: '>=12.0.0'} 510 | 511 | foreground-child@3.3.1: 512 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 513 | engines: {node: '>=14'} 514 | 515 | form-data-encoder@4.0.2: 516 | resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==} 517 | engines: {node: '>= 18'} 518 | 519 | form-data@2.5.2: 520 | resolution: {integrity: sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==} 521 | engines: {node: '>= 0.12'} 522 | 523 | fsevents@2.3.3: 524 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 525 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 526 | os: [darwin] 527 | 528 | get-stream@9.0.1: 529 | resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} 530 | engines: {node: '>=18'} 531 | 532 | glob@10.4.5: 533 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 534 | hasBin: true 535 | 536 | got@14.4.6: 537 | resolution: {integrity: sha512-rnhwfM/PhMNJ1i17k3DuDqgj0cKx3IHxBKVv/WX1uDKqrhi2Gv3l7rhPThR/Cc6uU++dD97W9c8Y0qyw9x0jag==} 538 | engines: {node: '>=20'} 539 | 540 | has-flag@4.0.0: 541 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 542 | engines: {node: '>=8'} 543 | 544 | html-escaper@2.0.2: 545 | resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} 546 | 547 | http-cache-semantics@4.1.1: 548 | resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} 549 | 550 | http2-wrapper@2.2.1: 551 | resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} 552 | engines: {node: '>=10.19.0'} 553 | 554 | is-fullwidth-code-point@3.0.0: 555 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 556 | engines: {node: '>=8'} 557 | 558 | is-stream@4.0.1: 559 | resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} 560 | engines: {node: '>=18'} 561 | 562 | isexe@2.0.0: 563 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 564 | 565 | istanbul-lib-coverage@3.2.2: 566 | resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} 567 | engines: {node: '>=8'} 568 | 569 | istanbul-lib-report@3.0.1: 570 | resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} 571 | engines: {node: '>=10'} 572 | 573 | istanbul-lib-source-maps@5.0.6: 574 | resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} 575 | engines: {node: '>=10'} 576 | 577 | istanbul-reports@3.1.7: 578 | resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} 579 | engines: {node: '>=8'} 580 | 581 | jackspeak@3.4.3: 582 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 583 | 584 | json-buffer@3.0.1: 585 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 586 | 587 | keyv@4.5.4: 588 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 589 | 590 | loupe@3.1.3: 591 | resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} 592 | 593 | lowercase-keys@3.0.0: 594 | resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} 595 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 596 | 597 | lru-cache@10.4.3: 598 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 599 | 600 | magic-string@0.30.17: 601 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 602 | 603 | magicast@0.3.5: 604 | resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} 605 | 606 | make-dir@4.0.0: 607 | resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} 608 | engines: {node: '>=10'} 609 | 610 | mime-db@1.52.0: 611 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 612 | engines: {node: '>= 0.6'} 613 | 614 | mime-types@2.1.35: 615 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 616 | engines: {node: '>= 0.6'} 617 | 618 | mimic-response@3.1.0: 619 | resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} 620 | engines: {node: '>=10'} 621 | 622 | mimic-response@4.0.0: 623 | resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} 624 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 625 | 626 | minimatch@9.0.5: 627 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 628 | engines: {node: '>=16 || 14 >=14.17'} 629 | 630 | minipass@7.1.2: 631 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 632 | engines: {node: '>=16 || 14 >=14.17'} 633 | 634 | ms@2.1.3: 635 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 636 | 637 | nanoid@3.3.8: 638 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 639 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 640 | hasBin: true 641 | 642 | normalize-url@8.0.1: 643 | resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} 644 | engines: {node: '>=14.16'} 645 | 646 | oauth-1.0a@2.2.6: 647 | resolution: {integrity: sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==} 648 | 649 | p-cancelable@4.0.1: 650 | resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==} 651 | engines: {node: '>=14.16'} 652 | 653 | package-json-from-dist@1.0.1: 654 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 655 | 656 | path-key@3.1.1: 657 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 658 | engines: {node: '>=8'} 659 | 660 | path-scurry@1.11.1: 661 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 662 | engines: {node: '>=16 || 14 >=14.18'} 663 | 664 | pathe@2.0.3: 665 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 666 | 667 | pathval@2.0.0: 668 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 669 | engines: {node: '>= 14.16'} 670 | 671 | picocolors@1.1.1: 672 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 673 | 674 | postcss@8.5.3: 675 | resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} 676 | engines: {node: ^10 || ^12 || >=14} 677 | 678 | quick-lru@5.1.1: 679 | resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} 680 | engines: {node: '>=10'} 681 | 682 | resolve-alpn@1.2.1: 683 | resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} 684 | 685 | responselike@3.0.0: 686 | resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} 687 | engines: {node: '>=14.16'} 688 | 689 | rollup@4.34.9: 690 | resolution: {integrity: sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==} 691 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 692 | hasBin: true 693 | 694 | safe-buffer@5.2.1: 695 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 696 | 697 | semver@7.7.1: 698 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 699 | engines: {node: '>=10'} 700 | hasBin: true 701 | 702 | shebang-command@2.0.0: 703 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 704 | engines: {node: '>=8'} 705 | 706 | shebang-regex@3.0.0: 707 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 708 | engines: {node: '>=8'} 709 | 710 | siginfo@2.0.0: 711 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 712 | 713 | signal-exit@4.1.0: 714 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 715 | engines: {node: '>=14'} 716 | 717 | source-map-js@1.2.1: 718 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 719 | engines: {node: '>=0.10.0'} 720 | 721 | stackback@0.0.2: 722 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 723 | 724 | std-env@3.8.0: 725 | resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} 726 | 727 | string-width@4.2.3: 728 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 729 | engines: {node: '>=8'} 730 | 731 | string-width@5.1.2: 732 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 733 | engines: {node: '>=12'} 734 | 735 | strip-ansi@6.0.1: 736 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 737 | engines: {node: '>=8'} 738 | 739 | strip-ansi@7.1.0: 740 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 741 | engines: {node: '>=12'} 742 | 743 | supports-color@7.2.0: 744 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 745 | engines: {node: '>=8'} 746 | 747 | test-exclude@7.0.1: 748 | resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} 749 | engines: {node: '>=18'} 750 | 751 | tinybench@2.9.0: 752 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 753 | 754 | tinyexec@0.3.2: 755 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 756 | 757 | tinypool@1.0.2: 758 | resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} 759 | engines: {node: ^18.0.0 || >=20.0.0} 760 | 761 | tinyrainbow@2.0.0: 762 | resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} 763 | engines: {node: '>=14.0.0'} 764 | 765 | tinyspy@3.0.2: 766 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 767 | engines: {node: '>=14.0.0'} 768 | 769 | type-fest@4.36.0: 770 | resolution: {integrity: sha512-3T/PUdKTCnkUmhQU6FFJEHsLwadsRegktX3TNHk+2JJB9HlA8gp1/VXblXVDI93kSnXF2rdPx0GMbHtJIV2LPg==} 771 | engines: {node: '>=16'} 772 | 773 | typescript@5.8.2: 774 | resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} 775 | engines: {node: '>=14.17'} 776 | hasBin: true 777 | 778 | undici-types@6.19.8: 779 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 780 | 781 | vite-node@3.0.7: 782 | resolution: {integrity: sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==} 783 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 784 | hasBin: true 785 | 786 | vite@5.4.14: 787 | resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} 788 | engines: {node: ^18.0.0 || >=20.0.0} 789 | hasBin: true 790 | peerDependencies: 791 | '@types/node': ^18.0.0 || >=20.0.0 792 | less: '*' 793 | lightningcss: ^1.21.0 794 | sass: '*' 795 | sass-embedded: '*' 796 | stylus: '*' 797 | sugarss: '*' 798 | terser: ^5.4.0 799 | peerDependenciesMeta: 800 | '@types/node': 801 | optional: true 802 | less: 803 | optional: true 804 | lightningcss: 805 | optional: true 806 | sass: 807 | optional: true 808 | sass-embedded: 809 | optional: true 810 | stylus: 811 | optional: true 812 | sugarss: 813 | optional: true 814 | terser: 815 | optional: true 816 | 817 | vitest@3.0.7: 818 | resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} 819 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 820 | hasBin: true 821 | peerDependencies: 822 | '@edge-runtime/vm': '*' 823 | '@types/debug': ^4.1.12 824 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 825 | '@vitest/browser': 3.0.7 826 | '@vitest/ui': 3.0.7 827 | happy-dom: '*' 828 | jsdom: '*' 829 | peerDependenciesMeta: 830 | '@edge-runtime/vm': 831 | optional: true 832 | '@types/debug': 833 | optional: true 834 | '@types/node': 835 | optional: true 836 | '@vitest/browser': 837 | optional: true 838 | '@vitest/ui': 839 | optional: true 840 | happy-dom: 841 | optional: true 842 | jsdom: 843 | optional: true 844 | 845 | which@2.0.2: 846 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 847 | engines: {node: '>= 8'} 848 | hasBin: true 849 | 850 | why-is-node-running@2.3.0: 851 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 852 | engines: {node: '>=8'} 853 | hasBin: true 854 | 855 | wrap-ansi@7.0.0: 856 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 857 | engines: {node: '>=10'} 858 | 859 | wrap-ansi@8.1.0: 860 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 861 | engines: {node: '>=12'} 862 | 863 | snapshots: 864 | 865 | '@ampproject/remapping@2.3.0': 866 | dependencies: 867 | '@jridgewell/gen-mapping': 0.3.8 868 | '@jridgewell/trace-mapping': 0.3.25 869 | 870 | '@babel/helper-string-parser@7.25.9': {} 871 | 872 | '@babel/helper-validator-identifier@7.25.9': {} 873 | 874 | '@babel/parser@7.26.9': 875 | dependencies: 876 | '@babel/types': 7.26.9 877 | 878 | '@babel/types@7.26.9': 879 | dependencies: 880 | '@babel/helper-string-parser': 7.25.9 881 | '@babel/helper-validator-identifier': 7.25.9 882 | 883 | '@bcoe/v8-coverage@1.0.2': {} 884 | 885 | '@esbuild/aix-ppc64@0.21.5': 886 | optional: true 887 | 888 | '@esbuild/android-arm64@0.21.5': 889 | optional: true 890 | 891 | '@esbuild/android-arm@0.21.5': 892 | optional: true 893 | 894 | '@esbuild/android-x64@0.21.5': 895 | optional: true 896 | 897 | '@esbuild/darwin-arm64@0.21.5': 898 | optional: true 899 | 900 | '@esbuild/darwin-x64@0.21.5': 901 | optional: true 902 | 903 | '@esbuild/freebsd-arm64@0.21.5': 904 | optional: true 905 | 906 | '@esbuild/freebsd-x64@0.21.5': 907 | optional: true 908 | 909 | '@esbuild/linux-arm64@0.21.5': 910 | optional: true 911 | 912 | '@esbuild/linux-arm@0.21.5': 913 | optional: true 914 | 915 | '@esbuild/linux-ia32@0.21.5': 916 | optional: true 917 | 918 | '@esbuild/linux-loong64@0.21.5': 919 | optional: true 920 | 921 | '@esbuild/linux-mips64el@0.21.5': 922 | optional: true 923 | 924 | '@esbuild/linux-ppc64@0.21.5': 925 | optional: true 926 | 927 | '@esbuild/linux-riscv64@0.21.5': 928 | optional: true 929 | 930 | '@esbuild/linux-s390x@0.21.5': 931 | optional: true 932 | 933 | '@esbuild/linux-x64@0.21.5': 934 | optional: true 935 | 936 | '@esbuild/netbsd-x64@0.21.5': 937 | optional: true 938 | 939 | '@esbuild/openbsd-x64@0.21.5': 940 | optional: true 941 | 942 | '@esbuild/sunos-x64@0.21.5': 943 | optional: true 944 | 945 | '@esbuild/win32-arm64@0.21.5': 946 | optional: true 947 | 948 | '@esbuild/win32-ia32@0.21.5': 949 | optional: true 950 | 951 | '@esbuild/win32-x64@0.21.5': 952 | optional: true 953 | 954 | '@isaacs/cliui@8.0.2': 955 | dependencies: 956 | string-width: 5.1.2 957 | string-width-cjs: string-width@4.2.3 958 | strip-ansi: 7.1.0 959 | strip-ansi-cjs: strip-ansi@6.0.1 960 | wrap-ansi: 8.1.0 961 | wrap-ansi-cjs: wrap-ansi@7.0.0 962 | 963 | '@istanbuljs/schema@0.1.3': {} 964 | 965 | '@jridgewell/gen-mapping@0.3.8': 966 | dependencies: 967 | '@jridgewell/set-array': 1.2.1 968 | '@jridgewell/sourcemap-codec': 1.5.0 969 | '@jridgewell/trace-mapping': 0.3.25 970 | 971 | '@jridgewell/resolve-uri@3.1.2': {} 972 | 973 | '@jridgewell/set-array@1.2.1': {} 974 | 975 | '@jridgewell/sourcemap-codec@1.5.0': {} 976 | 977 | '@jridgewell/trace-mapping@0.3.25': 978 | dependencies: 979 | '@jridgewell/resolve-uri': 3.1.2 980 | '@jridgewell/sourcemap-codec': 1.5.0 981 | 982 | '@pkgjs/parseargs@0.11.0': 983 | optional: true 984 | 985 | '@rollup/rollup-android-arm-eabi@4.34.9': 986 | optional: true 987 | 988 | '@rollup/rollup-android-arm64@4.34.9': 989 | optional: true 990 | 991 | '@rollup/rollup-darwin-arm64@4.34.9': 992 | optional: true 993 | 994 | '@rollup/rollup-darwin-x64@4.34.9': 995 | optional: true 996 | 997 | '@rollup/rollup-freebsd-arm64@4.34.9': 998 | optional: true 999 | 1000 | '@rollup/rollup-freebsd-x64@4.34.9': 1001 | optional: true 1002 | 1003 | '@rollup/rollup-linux-arm-gnueabihf@4.34.9': 1004 | optional: true 1005 | 1006 | '@rollup/rollup-linux-arm-musleabihf@4.34.9': 1007 | optional: true 1008 | 1009 | '@rollup/rollup-linux-arm64-gnu@4.34.9': 1010 | optional: true 1011 | 1012 | '@rollup/rollup-linux-arm64-musl@4.34.9': 1013 | optional: true 1014 | 1015 | '@rollup/rollup-linux-loongarch64-gnu@4.34.9': 1016 | optional: true 1017 | 1018 | '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': 1019 | optional: true 1020 | 1021 | '@rollup/rollup-linux-riscv64-gnu@4.34.9': 1022 | optional: true 1023 | 1024 | '@rollup/rollup-linux-s390x-gnu@4.34.9': 1025 | optional: true 1026 | 1027 | '@rollup/rollup-linux-x64-gnu@4.34.9': 1028 | optional: true 1029 | 1030 | '@rollup/rollup-linux-x64-musl@4.34.9': 1031 | optional: true 1032 | 1033 | '@rollup/rollup-win32-arm64-msvc@4.34.9': 1034 | optional: true 1035 | 1036 | '@rollup/rollup-win32-ia32-msvc@4.34.9': 1037 | optional: true 1038 | 1039 | '@rollup/rollup-win32-x64-msvc@4.34.9': 1040 | optional: true 1041 | 1042 | '@sec-ant/readable-stream@0.4.1': {} 1043 | 1044 | '@sindresorhus/is@7.0.1': {} 1045 | 1046 | '@szmarczak/http-timer@5.0.1': 1047 | dependencies: 1048 | defer-to-connect: 2.0.1 1049 | 1050 | '@tsconfig/node20@20.1.4': {} 1051 | 1052 | '@types/estree@1.0.6': {} 1053 | 1054 | '@types/got@9.6.12': 1055 | dependencies: 1056 | '@types/node': 22.8.0 1057 | '@types/tough-cookie': 4.0.5 1058 | form-data: 2.5.2 1059 | 1060 | '@types/http-cache-semantics@4.0.4': {} 1061 | 1062 | '@types/node@22.8.0': 1063 | dependencies: 1064 | undici-types: 6.19.8 1065 | 1066 | '@types/tough-cookie@4.0.5': {} 1067 | 1068 | '@vitest/coverage-v8@3.0.7(vitest@3.0.7(@types/node@22.8.0))': 1069 | dependencies: 1070 | '@ampproject/remapping': 2.3.0 1071 | '@bcoe/v8-coverage': 1.0.2 1072 | debug: 4.4.0 1073 | istanbul-lib-coverage: 3.2.2 1074 | istanbul-lib-report: 3.0.1 1075 | istanbul-lib-source-maps: 5.0.6 1076 | istanbul-reports: 3.1.7 1077 | magic-string: 0.30.17 1078 | magicast: 0.3.5 1079 | std-env: 3.8.0 1080 | test-exclude: 7.0.1 1081 | tinyrainbow: 2.0.0 1082 | vitest: 3.0.7(@types/node@22.8.0) 1083 | transitivePeerDependencies: 1084 | - supports-color 1085 | 1086 | '@vitest/expect@3.0.7': 1087 | dependencies: 1088 | '@vitest/spy': 3.0.7 1089 | '@vitest/utils': 3.0.7 1090 | chai: 5.2.0 1091 | tinyrainbow: 2.0.0 1092 | 1093 | '@vitest/mocker@3.0.7(vite@5.4.14(@types/node@22.8.0))': 1094 | dependencies: 1095 | '@vitest/spy': 3.0.7 1096 | estree-walker: 3.0.3 1097 | magic-string: 0.30.17 1098 | optionalDependencies: 1099 | vite: 5.4.14(@types/node@22.8.0) 1100 | 1101 | '@vitest/pretty-format@3.0.7': 1102 | dependencies: 1103 | tinyrainbow: 2.0.0 1104 | 1105 | '@vitest/runner@3.0.7': 1106 | dependencies: 1107 | '@vitest/utils': 3.0.7 1108 | pathe: 2.0.3 1109 | 1110 | '@vitest/snapshot@3.0.7': 1111 | dependencies: 1112 | '@vitest/pretty-format': 3.0.7 1113 | magic-string: 0.30.17 1114 | pathe: 2.0.3 1115 | 1116 | '@vitest/spy@3.0.7': 1117 | dependencies: 1118 | tinyspy: 3.0.2 1119 | 1120 | '@vitest/utils@3.0.7': 1121 | dependencies: 1122 | '@vitest/pretty-format': 3.0.7 1123 | loupe: 3.1.3 1124 | tinyrainbow: 2.0.0 1125 | 1126 | ansi-regex@5.0.1: {} 1127 | 1128 | ansi-regex@6.1.0: {} 1129 | 1130 | ansi-styles@4.3.0: 1131 | dependencies: 1132 | color-convert: 2.0.1 1133 | 1134 | ansi-styles@6.2.1: {} 1135 | 1136 | assertion-error@2.0.1: {} 1137 | 1138 | asynckit@0.4.0: {} 1139 | 1140 | balanced-match@1.0.2: {} 1141 | 1142 | brace-expansion@2.0.1: 1143 | dependencies: 1144 | balanced-match: 1.0.2 1145 | 1146 | cac@6.7.14: {} 1147 | 1148 | cacheable-lookup@7.0.0: {} 1149 | 1150 | cacheable-request@12.0.1: 1151 | dependencies: 1152 | '@types/http-cache-semantics': 4.0.4 1153 | get-stream: 9.0.1 1154 | http-cache-semantics: 4.1.1 1155 | keyv: 4.5.4 1156 | mimic-response: 4.0.0 1157 | normalize-url: 8.0.1 1158 | responselike: 3.0.0 1159 | 1160 | chai@5.2.0: 1161 | dependencies: 1162 | assertion-error: 2.0.1 1163 | check-error: 2.1.1 1164 | deep-eql: 5.0.2 1165 | loupe: 3.1.3 1166 | pathval: 2.0.0 1167 | 1168 | check-error@2.1.1: {} 1169 | 1170 | color-convert@2.0.1: 1171 | dependencies: 1172 | color-name: 1.1.4 1173 | 1174 | color-name@1.1.4: {} 1175 | 1176 | combined-stream@1.0.8: 1177 | dependencies: 1178 | delayed-stream: 1.0.0 1179 | 1180 | cross-spawn@7.0.6: 1181 | dependencies: 1182 | path-key: 3.1.1 1183 | shebang-command: 2.0.0 1184 | which: 2.0.2 1185 | 1186 | debug@4.4.0: 1187 | dependencies: 1188 | ms: 2.1.3 1189 | 1190 | decompress-response@6.0.0: 1191 | dependencies: 1192 | mimic-response: 3.1.0 1193 | 1194 | deep-eql@5.0.2: {} 1195 | 1196 | defer-to-connect@2.0.1: {} 1197 | 1198 | delayed-stream@1.0.0: {} 1199 | 1200 | dotenv@16.4.7: {} 1201 | 1202 | eastasianwidth@0.2.0: {} 1203 | 1204 | emoji-regex@8.0.0: {} 1205 | 1206 | emoji-regex@9.2.2: {} 1207 | 1208 | es-module-lexer@1.6.0: {} 1209 | 1210 | esbuild@0.21.5: 1211 | optionalDependencies: 1212 | '@esbuild/aix-ppc64': 0.21.5 1213 | '@esbuild/android-arm': 0.21.5 1214 | '@esbuild/android-arm64': 0.21.5 1215 | '@esbuild/android-x64': 0.21.5 1216 | '@esbuild/darwin-arm64': 0.21.5 1217 | '@esbuild/darwin-x64': 0.21.5 1218 | '@esbuild/freebsd-arm64': 0.21.5 1219 | '@esbuild/freebsd-x64': 0.21.5 1220 | '@esbuild/linux-arm': 0.21.5 1221 | '@esbuild/linux-arm64': 0.21.5 1222 | '@esbuild/linux-ia32': 0.21.5 1223 | '@esbuild/linux-loong64': 0.21.5 1224 | '@esbuild/linux-mips64el': 0.21.5 1225 | '@esbuild/linux-ppc64': 0.21.5 1226 | '@esbuild/linux-riscv64': 0.21.5 1227 | '@esbuild/linux-s390x': 0.21.5 1228 | '@esbuild/linux-x64': 0.21.5 1229 | '@esbuild/netbsd-x64': 0.21.5 1230 | '@esbuild/openbsd-x64': 0.21.5 1231 | '@esbuild/sunos-x64': 0.21.5 1232 | '@esbuild/win32-arm64': 0.21.5 1233 | '@esbuild/win32-ia32': 0.21.5 1234 | '@esbuild/win32-x64': 0.21.5 1235 | 1236 | estree-walker@3.0.3: 1237 | dependencies: 1238 | '@types/estree': 1.0.6 1239 | 1240 | expect-type@1.2.0: {} 1241 | 1242 | foreground-child@3.3.1: 1243 | dependencies: 1244 | cross-spawn: 7.0.6 1245 | signal-exit: 4.1.0 1246 | 1247 | form-data-encoder@4.0.2: {} 1248 | 1249 | form-data@2.5.2: 1250 | dependencies: 1251 | asynckit: 0.4.0 1252 | combined-stream: 1.0.8 1253 | mime-types: 2.1.35 1254 | safe-buffer: 5.2.1 1255 | 1256 | fsevents@2.3.3: 1257 | optional: true 1258 | 1259 | get-stream@9.0.1: 1260 | dependencies: 1261 | '@sec-ant/readable-stream': 0.4.1 1262 | is-stream: 4.0.1 1263 | 1264 | glob@10.4.5: 1265 | dependencies: 1266 | foreground-child: 3.3.1 1267 | jackspeak: 3.4.3 1268 | minimatch: 9.0.5 1269 | minipass: 7.1.2 1270 | package-json-from-dist: 1.0.1 1271 | path-scurry: 1.11.1 1272 | 1273 | got@14.4.6: 1274 | dependencies: 1275 | '@sindresorhus/is': 7.0.1 1276 | '@szmarczak/http-timer': 5.0.1 1277 | cacheable-lookup: 7.0.0 1278 | cacheable-request: 12.0.1 1279 | decompress-response: 6.0.0 1280 | form-data-encoder: 4.0.2 1281 | http2-wrapper: 2.2.1 1282 | lowercase-keys: 3.0.0 1283 | p-cancelable: 4.0.1 1284 | responselike: 3.0.0 1285 | type-fest: 4.36.0 1286 | 1287 | has-flag@4.0.0: {} 1288 | 1289 | html-escaper@2.0.2: {} 1290 | 1291 | http-cache-semantics@4.1.1: {} 1292 | 1293 | http2-wrapper@2.2.1: 1294 | dependencies: 1295 | quick-lru: 5.1.1 1296 | resolve-alpn: 1.2.1 1297 | 1298 | is-fullwidth-code-point@3.0.0: {} 1299 | 1300 | is-stream@4.0.1: {} 1301 | 1302 | isexe@2.0.0: {} 1303 | 1304 | istanbul-lib-coverage@3.2.2: {} 1305 | 1306 | istanbul-lib-report@3.0.1: 1307 | dependencies: 1308 | istanbul-lib-coverage: 3.2.2 1309 | make-dir: 4.0.0 1310 | supports-color: 7.2.0 1311 | 1312 | istanbul-lib-source-maps@5.0.6: 1313 | dependencies: 1314 | '@jridgewell/trace-mapping': 0.3.25 1315 | debug: 4.4.0 1316 | istanbul-lib-coverage: 3.2.2 1317 | transitivePeerDependencies: 1318 | - supports-color 1319 | 1320 | istanbul-reports@3.1.7: 1321 | dependencies: 1322 | html-escaper: 2.0.2 1323 | istanbul-lib-report: 3.0.1 1324 | 1325 | jackspeak@3.4.3: 1326 | dependencies: 1327 | '@isaacs/cliui': 8.0.2 1328 | optionalDependencies: 1329 | '@pkgjs/parseargs': 0.11.0 1330 | 1331 | json-buffer@3.0.1: {} 1332 | 1333 | keyv@4.5.4: 1334 | dependencies: 1335 | json-buffer: 3.0.1 1336 | 1337 | loupe@3.1.3: {} 1338 | 1339 | lowercase-keys@3.0.0: {} 1340 | 1341 | lru-cache@10.4.3: {} 1342 | 1343 | magic-string@0.30.17: 1344 | dependencies: 1345 | '@jridgewell/sourcemap-codec': 1.5.0 1346 | 1347 | magicast@0.3.5: 1348 | dependencies: 1349 | '@babel/parser': 7.26.9 1350 | '@babel/types': 7.26.9 1351 | source-map-js: 1.2.1 1352 | 1353 | make-dir@4.0.0: 1354 | dependencies: 1355 | semver: 7.7.1 1356 | 1357 | mime-db@1.52.0: {} 1358 | 1359 | mime-types@2.1.35: 1360 | dependencies: 1361 | mime-db: 1.52.0 1362 | 1363 | mimic-response@3.1.0: {} 1364 | 1365 | mimic-response@4.0.0: {} 1366 | 1367 | minimatch@9.0.5: 1368 | dependencies: 1369 | brace-expansion: 2.0.1 1370 | 1371 | minipass@7.1.2: {} 1372 | 1373 | ms@2.1.3: {} 1374 | 1375 | nanoid@3.3.8: {} 1376 | 1377 | normalize-url@8.0.1: {} 1378 | 1379 | oauth-1.0a@2.2.6: {} 1380 | 1381 | p-cancelable@4.0.1: {} 1382 | 1383 | package-json-from-dist@1.0.1: {} 1384 | 1385 | path-key@3.1.1: {} 1386 | 1387 | path-scurry@1.11.1: 1388 | dependencies: 1389 | lru-cache: 10.4.3 1390 | minipass: 7.1.2 1391 | 1392 | pathe@2.0.3: {} 1393 | 1394 | pathval@2.0.0: {} 1395 | 1396 | picocolors@1.1.1: {} 1397 | 1398 | postcss@8.5.3: 1399 | dependencies: 1400 | nanoid: 3.3.8 1401 | picocolors: 1.1.1 1402 | source-map-js: 1.2.1 1403 | 1404 | quick-lru@5.1.1: {} 1405 | 1406 | resolve-alpn@1.2.1: {} 1407 | 1408 | responselike@3.0.0: 1409 | dependencies: 1410 | lowercase-keys: 3.0.0 1411 | 1412 | rollup@4.34.9: 1413 | dependencies: 1414 | '@types/estree': 1.0.6 1415 | optionalDependencies: 1416 | '@rollup/rollup-android-arm-eabi': 4.34.9 1417 | '@rollup/rollup-android-arm64': 4.34.9 1418 | '@rollup/rollup-darwin-arm64': 4.34.9 1419 | '@rollup/rollup-darwin-x64': 4.34.9 1420 | '@rollup/rollup-freebsd-arm64': 4.34.9 1421 | '@rollup/rollup-freebsd-x64': 4.34.9 1422 | '@rollup/rollup-linux-arm-gnueabihf': 4.34.9 1423 | '@rollup/rollup-linux-arm-musleabihf': 4.34.9 1424 | '@rollup/rollup-linux-arm64-gnu': 4.34.9 1425 | '@rollup/rollup-linux-arm64-musl': 4.34.9 1426 | '@rollup/rollup-linux-loongarch64-gnu': 4.34.9 1427 | '@rollup/rollup-linux-powerpc64le-gnu': 4.34.9 1428 | '@rollup/rollup-linux-riscv64-gnu': 4.34.9 1429 | '@rollup/rollup-linux-s390x-gnu': 4.34.9 1430 | '@rollup/rollup-linux-x64-gnu': 4.34.9 1431 | '@rollup/rollup-linux-x64-musl': 4.34.9 1432 | '@rollup/rollup-win32-arm64-msvc': 4.34.9 1433 | '@rollup/rollup-win32-ia32-msvc': 4.34.9 1434 | '@rollup/rollup-win32-x64-msvc': 4.34.9 1435 | fsevents: 2.3.3 1436 | 1437 | safe-buffer@5.2.1: {} 1438 | 1439 | semver@7.7.1: {} 1440 | 1441 | shebang-command@2.0.0: 1442 | dependencies: 1443 | shebang-regex: 3.0.0 1444 | 1445 | shebang-regex@3.0.0: {} 1446 | 1447 | siginfo@2.0.0: {} 1448 | 1449 | signal-exit@4.1.0: {} 1450 | 1451 | source-map-js@1.2.1: {} 1452 | 1453 | stackback@0.0.2: {} 1454 | 1455 | std-env@3.8.0: {} 1456 | 1457 | string-width@4.2.3: 1458 | dependencies: 1459 | emoji-regex: 8.0.0 1460 | is-fullwidth-code-point: 3.0.0 1461 | strip-ansi: 6.0.1 1462 | 1463 | string-width@5.1.2: 1464 | dependencies: 1465 | eastasianwidth: 0.2.0 1466 | emoji-regex: 9.2.2 1467 | strip-ansi: 7.1.0 1468 | 1469 | strip-ansi@6.0.1: 1470 | dependencies: 1471 | ansi-regex: 5.0.1 1472 | 1473 | strip-ansi@7.1.0: 1474 | dependencies: 1475 | ansi-regex: 6.1.0 1476 | 1477 | supports-color@7.2.0: 1478 | dependencies: 1479 | has-flag: 4.0.0 1480 | 1481 | test-exclude@7.0.1: 1482 | dependencies: 1483 | '@istanbuljs/schema': 0.1.3 1484 | glob: 10.4.5 1485 | minimatch: 9.0.5 1486 | 1487 | tinybench@2.9.0: {} 1488 | 1489 | tinyexec@0.3.2: {} 1490 | 1491 | tinypool@1.0.2: {} 1492 | 1493 | tinyrainbow@2.0.0: {} 1494 | 1495 | tinyspy@3.0.2: {} 1496 | 1497 | type-fest@4.36.0: {} 1498 | 1499 | typescript@5.8.2: {} 1500 | 1501 | undici-types@6.19.8: {} 1502 | 1503 | vite-node@3.0.7(@types/node@22.8.0): 1504 | dependencies: 1505 | cac: 6.7.14 1506 | debug: 4.4.0 1507 | es-module-lexer: 1.6.0 1508 | pathe: 2.0.3 1509 | vite: 5.4.14(@types/node@22.8.0) 1510 | transitivePeerDependencies: 1511 | - '@types/node' 1512 | - less 1513 | - lightningcss 1514 | - sass 1515 | - sass-embedded 1516 | - stylus 1517 | - sugarss 1518 | - supports-color 1519 | - terser 1520 | 1521 | vite@5.4.14(@types/node@22.8.0): 1522 | dependencies: 1523 | esbuild: 0.21.5 1524 | postcss: 8.5.3 1525 | rollup: 4.34.9 1526 | optionalDependencies: 1527 | '@types/node': 22.8.0 1528 | fsevents: 2.3.3 1529 | 1530 | vitest@3.0.7(@types/node@22.8.0): 1531 | dependencies: 1532 | '@vitest/expect': 3.0.7 1533 | '@vitest/mocker': 3.0.7(vite@5.4.14(@types/node@22.8.0)) 1534 | '@vitest/pretty-format': 3.0.7 1535 | '@vitest/runner': 3.0.7 1536 | '@vitest/snapshot': 3.0.7 1537 | '@vitest/spy': 3.0.7 1538 | '@vitest/utils': 3.0.7 1539 | chai: 5.2.0 1540 | debug: 4.4.0 1541 | expect-type: 1.2.0 1542 | magic-string: 0.30.17 1543 | pathe: 2.0.3 1544 | std-env: 3.8.0 1545 | tinybench: 2.9.0 1546 | tinyexec: 0.3.2 1547 | tinypool: 1.0.2 1548 | tinyrainbow: 2.0.0 1549 | vite: 5.4.14(@types/node@22.8.0) 1550 | vite-node: 3.0.7(@types/node@22.8.0) 1551 | why-is-node-running: 2.3.0 1552 | optionalDependencies: 1553 | '@types/node': 22.8.0 1554 | transitivePeerDependencies: 1555 | - less 1556 | - lightningcss 1557 | - msw 1558 | - sass 1559 | - sass-embedded 1560 | - stylus 1561 | - sugarss 1562 | - supports-color 1563 | - terser 1564 | 1565 | which@2.0.2: 1566 | dependencies: 1567 | isexe: 2.0.0 1568 | 1569 | why-is-node-running@2.3.0: 1570 | dependencies: 1571 | siginfo: 2.0.0 1572 | stackback: 0.0.2 1573 | 1574 | wrap-ansi@7.0.0: 1575 | dependencies: 1576 | ansi-styles: 4.3.0 1577 | string-width: 4.2.3 1578 | strip-ansi: 6.0.1 1579 | 1580 | wrap-ansi@8.1.0: 1581 | dependencies: 1582 | ansi-styles: 6.2.1 1583 | string-width: 5.1.2 1584 | strip-ansi: 7.1.0 1585 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /restlet-demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @NAPIVersion 2.0 3 | * @NScriptType Restlet 4 | */ 5 | 6 | /* 7 | This file was generated via the 8 | headintheclouds [typings-suitescript-2.0[(https://github.com/headintheclouddev/typings-suitescript-2.0) project. 9 | 10 | It can be deployed as a restlet and will return a successful response to any GET request. 11 | */ 12 | 13 | var __importDefault = (this && this.__importDefault) || function (mod) { 14 | return (mod && mod.__esModule) ? mod : { "default": mod }; 15 | }; 16 | define(["N/log"], function (log_1) { 17 | var exports = {}; 18 | Object.defineProperty(exports, "__esModule", { value: true }); 19 | exports.get = void 0; 20 | log_1 = __importDefault(log_1); 21 | var get = function (requestParams) { 22 | log_1.default.debug({ 23 | title: 'GET Request', 24 | details: requestParams, 25 | }); 26 | return { 27 | success: true, 28 | }; 29 | }; 30 | exports.get = get; 31 | return exports; 32 | }); 33 | -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- 1 | import OAuth from "oauth-1.0a"; 2 | import crypto from "crypto"; 3 | import got, { HTTPError, OptionsOfTextResponseBody } from "got"; 4 | import { Readable } from "stream"; 5 | import { 6 | NetsuiteOptions, 7 | NetsuiteQueryResult, 8 | NetsuiteRequestOptions, 9 | NetsuiteResponse, 10 | } from "./types.js"; 11 | import { NetsuiteError } from "./errors.js"; 12 | import {removeLeadingSlash, removeTrailingSlash} from "./utils.js"; 13 | 14 | export default class NetsuiteApiClient { 15 | consumer_key: string; 16 | consumer_secret_key: string; 17 | token: string; 18 | token_secret: string; 19 | version: string; 20 | algorithm: string; 21 | realm: string; 22 | base_url?: string; 23 | 24 | constructor(options: NetsuiteOptions) { 25 | this.consumer_key = options.consumer_key; 26 | this.consumer_secret_key = options.consumer_secret_key; 27 | this.token = options.token; 28 | this.token_secret = options.token_secret; 29 | this.version = "1.0"; 30 | this.algorithm = "HMAC-SHA256"; 31 | this.realm = options.realm; 32 | this.base_url = options.base_url ? removeTrailingSlash(options.base_url) : undefined; 33 | } 34 | 35 | /** 36 | * Retrieve the Authorization Header 37 | * @returns 38 | * @param url 39 | * @param method 40 | */ 41 | getAuthorizationHeader(url: string, method: string): { [key: string]: string } { 42 | const oauth = new OAuth({ 43 | consumer: { 44 | key: this.consumer_key, 45 | secret: this.consumer_secret_key, 46 | }, 47 | realm: this.realm, 48 | signature_method: this.algorithm, 49 | hash_function(base_string, key) { 50 | return crypto.createHmac("sha256", key).update(base_string).digest("base64"); 51 | }, 52 | }); 53 | return oauth.toHeader( 54 | oauth.authorize( 55 | { 56 | url, 57 | method, 58 | }, 59 | { 60 | key: this.token, 61 | secret: this.token_secret, 62 | } 63 | ) 64 | ) as unknown as { [key: string]: string }; 65 | } 66 | 67 | /** 68 | * Run a raw REST API request 69 | * @param opts 70 | * @returns 71 | */ 72 | public async request(opts: NetsuiteRequestOptions) { 73 | const { path = "*", method = "GET", body = "", heads = {}, restletUrl } = opts; 74 | const cleanPath = removeLeadingSlash(path); 75 | // Set up the Request URI 76 | 77 | // as suggested by dylbarne in #15: sanitize url to enhance overall usability 78 | let uri = `https://${this.realm 79 | .toLowerCase() 80 | .replace("_", "-")}.suitetalk.api.netsuite.com/services/rest/${cleanPath}`; 81 | if (this.base_url) { 82 | uri = `${this.base_url}/services/rest/${cleanPath}` 83 | } 84 | // if restletUrl is provided, use it instead of the default uri 85 | if (restletUrl) { 86 | uri = restletUrl; 87 | } 88 | 89 | const options = { 90 | method, 91 | headers: this.getAuthorizationHeader(uri, method), 92 | throwHttpErrors: true, 93 | decompress: true, 94 | } as OptionsOfTextResponseBody; 95 | 96 | if (Object.keys(heads).length > 0) { 97 | options.headers = { ...options.headers, ...heads }; 98 | } 99 | if (body) { 100 | options.body = body; 101 | options.headers!.prefer = "transient"; 102 | } 103 | try { 104 | const response = await got(uri, options); 105 | return { 106 | ...response, 107 | headers: response.headers, 108 | data: response.body ? JSON.parse(response.body) : null, 109 | } as NetsuiteResponse; 110 | } catch (e) { 111 | if (e instanceof HTTPError) { 112 | throw new NetsuiteError(e); 113 | } 114 | throw e; 115 | } 116 | } 117 | 118 | /** 119 | * Connect ! 120 | * @returns 121 | */ 122 | public async connect() { 123 | return await this.request({ 124 | path: "*", 125 | method: "OPTIONS", 126 | }); 127 | } 128 | 129 | /** 130 | * Run SuiteQL query 131 | * @param query 132 | * @param limit 133 | * @param offset 134 | * @returns 135 | */ 136 | public async query(query: string, limit = 1000, offset = 0) { 137 | let queryResult: NetsuiteQueryResult = { items: [], hasMore: false }; 138 | if (limit > 1000) throw new Error("Max limit is 1000"); 139 | // replace all \t with spaces as suggested in #5 140 | query = query.replace(/\t/g, " "); 141 | query = query.replace(/\r?\n|\r/gm, ""); 142 | let bodyContent = `{"q": "${query}" }`; 143 | const response = await this.request({ 144 | path: `query/v1/suiteql?limit=${limit}&offset=${offset}`, 145 | method: "POST", 146 | body: bodyContent, 147 | }); 148 | queryResult.items = response.data.items; 149 | queryResult.hasMore = response.data.hasMore; 150 | return queryResult; 151 | } 152 | 153 | /** 154 | * Run and then combine all pages of a query 155 | * @param query 156 | * @param limit 157 | * @returns 158 | */ 159 | public queryAll(query: string, limit = 1000) { 160 | const stream = new Readable({ 161 | objectMode: true, 162 | read() {}, 163 | }); 164 | let offset = 0; 165 | const getNextPage = async () => { 166 | try { 167 | let hasMore = true; 168 | while (hasMore === true) { 169 | let sqlResult = await this.query(query, limit, offset); 170 | sqlResult.items.forEach((item) => stream.push(item)); 171 | hasMore = sqlResult.hasMore; 172 | offset = offset + limit; 173 | } 174 | stream.push(null); 175 | } catch (error) { 176 | stream.emit('error', error); 177 | } 178 | }; 179 | getNextPage(); 180 | return stream; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | import { HTTPError } from "got"; 2 | 3 | type NetsuiteBodyError = { 4 | type: string; 5 | title: string; 6 | status: number; 7 | "o:errorDetails": { 8 | detail: string; 9 | "o:errorQueryParam": string; 10 | "o:errorCode": string; 11 | }[]; 12 | }; 13 | 14 | export class NetsuiteError extends Error { 15 | constructor(httpError: HTTPError) { 16 | try { 17 | const body = httpError?.response?.body; 18 | const data = JSON.parse(body as string) as NetsuiteBodyError; 19 | const text = data["o:errorDetails"][0].detail; 20 | super(text || httpError.message); 21 | } catch (e) { 22 | super(httpError.message); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import NetsuiteApiClient from "./client.js"; 2 | import { 3 | NetsuiteOptions, 4 | NetsuiteRequestOptions, 5 | NetsuiteResponse, 6 | NetsuiteQueryResult, 7 | } from "./types.js"; 8 | import { NetsuiteError } from "./errors.js"; 9 | 10 | export { 11 | NetsuiteApiClient, 12 | NetsuiteOptions, 13 | NetsuiteRequestOptions, 14 | NetsuiteResponse, 15 | NetsuiteQueryResult, 16 | NetsuiteError, 17 | }; 18 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type {Buffer} from "node:buffer"; 2 | import type {Readable} from "node:stream"; 3 | 4 | export type NetsuiteOptions = { 5 | consumer_key: string; 6 | consumer_secret_key: string; 7 | token: string; 8 | token_secret: string; 9 | realm: string; 10 | base_url?: string; 11 | }; 12 | 13 | type BaseRequestOptions = { 14 | /** 15 | * The HTTP method to use 16 | */ 17 | method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS" | 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options'; 18 | /** 19 | * The body of the request 20 | */ 21 | body?: string; 22 | /** 23 | * Additional headers to send with the request 24 | */ 25 | heads?: any; 26 | }; 27 | 28 | export type NetsuiteRequestOptions = 29 | | (BaseRequestOptions & { path?: string; restletUrl?: never }) 30 | | (BaseRequestOptions & { path?: never; restletUrl?: string }); 31 | 32 | export type NetsuiteResponse = { 33 | statusCode: number; 34 | headers: NodeJS.Dict; 35 | data: any; 36 | }; 37 | 38 | export type NetsuiteQueryResult = { 39 | items: any[]; 40 | hasMore: boolean; 41 | }; 42 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | function removeTrailingSlash(str: string): string { 2 | return str.replace(/\/$/, ''); 3 | } 4 | 5 | function removeLeadingSlash(str: string): string { 6 | return str.replace(/^\//, ''); 7 | } 8 | 9 | export { removeLeadingSlash, removeTrailingSlash }; 10 | -------------------------------------------------------------------------------- /test/basic.test.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import NetsuiteApiClient from "../src/client.js"; 3 | import { beforeAll, describe, expect, it } from "vitest"; 4 | 5 | let client: NetsuiteApiClient; 6 | 7 | describe("Test basic behavior", () => { 8 | beforeAll(() => { 9 | if ( 10 | process.env.consumer_key == undefined || 11 | process.env.consumer_secret_key == undefined || 12 | process.env.token == undefined || 13 | process.env.token_secret == undefined || 14 | process.env.realm == undefined || 15 | process.env.base_url == undefined 16 | ) { 17 | throw new Error("Please create a `.env` file based on `.env.sample`"); 18 | } 19 | }); 20 | 21 | it("it should throw error if config is missing", () => { 22 | const t = () => { 23 | new NetsuiteApiClient(null as any); 24 | }; 25 | expect(t).toThrow(TypeError); 26 | }); 27 | 28 | it("it should not connect to NetSuite because of invalid token", () => { 29 | expect.assertions(1); 30 | 31 | client = new NetsuiteApiClient({ 32 | consumer_key: process.env.consumer_key!, 33 | consumer_secret_key: process.env.consumer_secret_key!, 34 | token: "INVALID TOKEN", 35 | token_secret: process.env.token_secret!, 36 | realm: process.env.realm!, 37 | }); 38 | return expect(client.connect()).rejects.toThrowError( 39 | "Invalid login attempt. For more details, see the Login Audit Trail in the NetSuite UI at Setup > Users/Roles > User Management > View Login Audit Trail." 40 | ); 41 | }); 42 | 43 | it("it should not connect to NetSuite because of invalid realm", () => { 44 | expect.assertions(1); 45 | 46 | client = new NetsuiteApiClient({ 47 | consumer_key: process.env.consumer_key!, 48 | consumer_secret_key: process.env.consumer_secret_key!, 49 | token: process.env.token!, 50 | token_secret: process.env.token_secret!, 51 | realm: "invalid", 52 | }); 53 | return expect(client.connect()).rejects.toThrowError( 54 | "getaddrinfo ENOTFOUND invalid.suitetalk.api.netsuite.co" 55 | ); 56 | }); 57 | 58 | it("it should connect to NetSuite", async () => { 59 | expect.assertions(1); 60 | 61 | client = new NetsuiteApiClient({ 62 | consumer_key: process.env.consumer_key!, 63 | consumer_secret_key: process.env.consumer_secret_key!, 64 | token: process.env.token!, 65 | token_secret: process.env.token_secret!, 66 | realm: process.env.realm!, 67 | base_url: process.env.base_url, 68 | }); 69 | 70 | const response = await client.connect(); 71 | expect(response.statusCode).toEqual(204); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/query.test.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import NetsuiteApiClient from "../src/client.js"; 3 | import { beforeAll, describe, expect, it } from "vitest"; 4 | 5 | let client: NetsuiteApiClient; 6 | 7 | describe("Test query and queryAll method", () => { 8 | beforeAll(() => { 9 | if ( 10 | process.env.consumer_key == undefined || 11 | process.env.consumer_secret_key == undefined || 12 | process.env.token == undefined || 13 | process.env.token_secret == undefined || 14 | process.env.realm == undefined 15 | ) { 16 | throw new Error("Please create a `.env` file based on `.env.sample`"); 17 | } 18 | 19 | client = new NetsuiteApiClient({ 20 | consumer_key: process.env.consumer_key, 21 | consumer_secret_key: process.env.consumer_secret_key, 22 | token: process.env.token, 23 | token_secret: process.env.token_secret, 24 | realm: process.env.realm, 25 | }); 26 | }); 27 | it("it should get 2 records from transaction table ", async () => { 28 | expect.assertions(1); 29 | let transactions = await client.query("select id from transaction", 2); 30 | expect(transactions.items.length).toEqual(2); 31 | }); 32 | 33 | it("it should get 0 records from transaction table ", async () => { 34 | expect.assertions(1); 35 | let transactions = await client.query( 36 | `select id from transaction where id = 1 ` 37 | ); 38 | expect(transactions.items.length).toEqual(0); 39 | }); 40 | 41 | it("it should throw error if query is not string", async () => { 42 | expect.assertions(1); 43 | await expect(client.query(123 as any)).rejects.toThrow(Error); 44 | }); 45 | 46 | it("it should throw meaningful error if not existing table", async () => { 47 | expect.assertions(1); 48 | await expect( 49 | client.query("select id from transactiontablethatdoesnotexist", 2) 50 | ).rejects.toThrowError( 51 | "Invalid search query. Detailed unprocessed description follows. Invalid search type: transactiontablethatdoesnotexist." 52 | ); 53 | }); 54 | 55 | it("it should throw meaningful error if not existing field", async () => { 56 | expect.assertions(1); 57 | await expect( 58 | client.query("select not_existing_field from customer", 2) 59 | ).rejects.toThrowError( 60 | "Invalid search query. Detailed unprocessed description follows. Search error occurred: Unknown identifier 'not_existing_field'. Available identifiers are: {customer=customer}" 61 | ); 62 | }); 63 | 64 | it("it should throw error if limit exceeds 1000", async () => { 65 | expect.assertions(1); 66 | await expect( 67 | client.query("select id from transaction", 1001) 68 | ).rejects.toThrow("Max limit is 1000"); 69 | }); 70 | 71 | it("it should get all 30 records from transaction table using queryAll", () => 72 | new Promise((done) => { 73 | expect.assertions(1); 74 | let items: any[] = []; 75 | let st = client.queryAll( 76 | "select tranid, id from transaction where rownum <= 30" 77 | ); 78 | st.on("data", (data) => { 79 | items.push(data); 80 | }); 81 | st.on("end", () => { 82 | expect(items.length).toEqual(30); 83 | done(); 84 | }); 85 | })); 86 | 87 | it("it should throw error if queryAll result in error", () => 88 | new Promise((done) => { 89 | expect.assertions(1); 90 | let items: any[] = []; 91 | let st = client.queryAll( 92 | "select tranid, id from transactiontablethatdoesnotexist" 93 | ); 94 | st.on("error", (error) => { 95 | expect(error).toBeDefined(); 96 | done(); 97 | }); 98 | })); 99 | }); 100 | -------------------------------------------------------------------------------- /test/request.test.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | import NetsuiteApiClient from "../src/client.js"; 3 | import { describe, expect, test, beforeAll, afterAll, it } from "vitest"; 4 | 5 | describe("Test request method", () => { 6 | let client: NetsuiteApiClient; 7 | let clientWithBaseUrl: NetsuiteApiClient; 8 | 9 | beforeAll(async () => { 10 | if ( 11 | process.env.consumer_key == undefined || 12 | process.env.consumer_secret_key == undefined || 13 | process.env.token == undefined || 14 | process.env.token_secret == undefined || 15 | process.env.realm == undefined || 16 | process.env.base_url == undefined 17 | // || process.env.restlet_url == undefined 18 | ) { 19 | throw new Error("Please create a `.env` file based on `.env.sample`"); 20 | } 21 | 22 | client = new NetsuiteApiClient({ 23 | consumer_key: process.env.consumer_key, 24 | consumer_secret_key: process.env.consumer_secret_key, 25 | token: process.env.token, 26 | token_secret: process.env.token_secret, 27 | realm: process.env.realm, 28 | }); 29 | 30 | // with base_url 31 | clientWithBaseUrl = new NetsuiteApiClient({ 32 | consumer_key: process.env.consumer_key, 33 | consumer_secret_key: process.env.consumer_secret_key, 34 | token: process.env.token, 35 | token_secret: process.env.token_secret, 36 | realm: process.env.realm, 37 | base_url: process.env.base_url, 38 | }); 39 | }); 40 | 41 | it("should make test request", async () => { 42 | expect.assertions(1); 43 | const response = await client.request({ 44 | method: "OPTIONS", 45 | }); 46 | expect(response.statusCode).toEqual(204); 47 | }); 48 | 49 | // it('should make a RESTlet request', async () => { 50 | // expect.assertions(1); 51 | // const response = await client.request({ 52 | // method: 'GET', 53 | // restletUrl: process.env.restlet_url, 54 | // heads: { 55 | // 'Content-Type': 'application/json' 56 | // } 57 | // }) 58 | // expect(response.statusCode).toEqual(200); 59 | // }) 60 | 61 | it("should make GET request - GET Customers", async () => { 62 | expect.assertions(4); 63 | const response = await client.request({ 64 | path: "record/v1/customer/", 65 | }); 66 | expect(response.statusCode).toEqual(200); 67 | expect(response.data).toBeDefined(); 68 | expect(response.data.items).toBeDefined(); 69 | expect(response.data.count).toBeDefined(); 70 | }); 71 | 72 | it("should make POST request - POST Customers", async () => { 73 | const response = await client.request({ 74 | path: "record/v1/customer/", 75 | method: "POST", 76 | body: JSON.stringify({ 77 | entityStatus: 6, 78 | firstName: "firstName", 79 | lastName: "lastName", 80 | subsidiary: "1", 81 | companyName: "netsuite-api-client-integration-test", 82 | }), 83 | }); 84 | 85 | expect(response.statusCode).toEqual(204); 86 | expect(response.data).toBeDefined(); 87 | expect(response.data).toBeNull(); 88 | expect(response.headers.location).toBeDefined(); 89 | 90 | const location = response.headers.location as string; 91 | 92 | // extract id from location string 93 | const id = location.split("/").pop(); 94 | 95 | // delete the created company 96 | const responseDelete = await client.request({ 97 | path: `record/v1/customer/${id}`, 98 | method: "DELETE", 99 | }); 100 | 101 | expect(responseDelete.statusCode).toEqual(204); 102 | }); 103 | 104 | it("should make POST request - SuiteQL Query", async () => { 105 | expect.assertions(4); 106 | const response = await client.request({ 107 | path: "query/v1/suiteql?limit=5", 108 | method: "POST", 109 | body: `{ 110 | "q": "SELECT id, companyName, email, dateCreated FROM customer WHERE dateCreated >= '01/01/2019' AND dateCreated < '01/01/2020'" 111 | }`, 112 | }); 113 | expect(response.statusCode).toEqual(200); 114 | expect(response.data).toBeDefined(); 115 | expect(response.data.items).toBeDefined(); 116 | expect(response.data.count).toBeDefined(); 117 | }); 118 | 119 | it("should work with base_url", async () => { 120 | expect.assertions(2); 121 | expect(process.env.base_url).toBeDefined(); 122 | 123 | const response = await clientWithBaseUrl.request({ 124 | method: "OPTIONS", 125 | }); 126 | expect(response.statusCode).toEqual(204); 127 | }); 128 | 129 | it("should work with additional headers", async () => { 130 | expect.assertions(1); 131 | const response = await client.request({ 132 | method: "OPTIONS", 133 | heads: { 134 | foo: "foo", 135 | }, 136 | }); 137 | expect(response.statusCode).toEqual(204); 138 | }); 139 | 140 | it("should reject with meaningful error if bad url", async () => { 141 | expect.assertions(1); 142 | await expect(() => 143 | client.request({ 144 | path: "record/v1/bad-path", 145 | }) 146 | ).rejects.toThrowError("Record type 'bad-path' does not exist"); 147 | }); 148 | 149 | it("should reject with meaningful error if bad object", async () => { 150 | expect.assertions(1); 151 | await expect(() => 152 | client.request({ 153 | path: "record/v1/customer/-1", 154 | }) 155 | ).rejects.toThrowError( 156 | "The record instance does not exist. Provide a valid record instance ID." 157 | ); 158 | }); 159 | 160 | it("should reject with meaningful error if bad request (wrong body)", async () => { 161 | expect.assertions(1); 162 | await expect(() => 163 | client.request({ 164 | method: "post", 165 | path: "record/v1/customer", 166 | body: "bad body", 167 | }) 168 | ).rejects.toThrowError("Invalid content in the request body"); 169 | }); 170 | 171 | it("should reject with meaningful error if bad request (missing fields)", async () => { 172 | expect.assertions(1); 173 | await expect(() => 174 | client.request({ 175 | method: "post", 176 | path: "record/v1/customer", 177 | body: JSON.stringify({}), 178 | }) 179 | ).rejects.toThrowError("Error while accessing a resource. Please enter value(s) for: "); 180 | }); 181 | }); 182 | -------------------------------------------------------------------------------- /test/utils.test.ts: -------------------------------------------------------------------------------- 1 | import {describe, expect, it} from "vitest"; 2 | import {removeLeadingSlash, removeTrailingSlash} from "../src/utils.js"; 3 | 4 | 5 | describe("Test Utilities", () => { 6 | 7 | it("it should properly remove a trailing slash", () => { 8 | const original = 'https://example.com/'; 9 | const fixed = 'https://example.com'; 10 | expect(removeTrailingSlash(original)).toEqual(fixed); 11 | }); 12 | 13 | it("it should properly remove a leading slash", () => { 14 | const original = '/path/to/something'; 15 | const fixed = 'path/to/something'; 16 | expect(removeLeadingSlash(original)).toEqual(fixed); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./dist" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { defineConfig } from "vitest/config"; 3 | 4 | export default defineConfig({ 5 | test: { 6 | testTimeout: 20000, 7 | }, 8 | }); 9 | --------------------------------------------------------------------------------