├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── RELEASE_NOTES.md ├── __tests__ ├── basics-fragments.test.mjs ├── basics.test.mjs ├── countDepth.test.mjs ├── countOperationDepths.test.mjs ├── defaultOptions.1.graphql ├── directives.test.mjs ├── introspection.test.mjs ├── invalidQueries.test.mjs ├── mutation.test.mjs ├── posts.test.mjs ├── queries │ ├── directives-additional.test.graphql │ ├── directives-fragments.test.graphql │ ├── directives.test.graphql │ ├── mutation-create.test.graphql │ ├── mutation-delete.test.graphql │ ├── mutation-return-types.test.graphql │ ├── mutation-update.test.graphql │ └── posts.test.graphql ├── simple.graphql └── utils.mjs ├── package.json ├── src ├── envelop.ts ├── index.ts └── interfaces.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserOptions: { 3 | sourceType: "module", 4 | ecmaFeatures: { 5 | jsx: true, 6 | }, 7 | }, 8 | extends: [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "plugin:import/errors", 13 | "plugin:import/typescript", 14 | "prettier", 15 | ], 16 | plugins: ["tsdoc", "simple-import-sort", "import"], 17 | env: { 18 | node: true, 19 | es6: true, 20 | }, 21 | rules: { 22 | "@typescript-eslint/ban-ts-comment": "off", 23 | "@typescript-eslint/ban-ts-ignore": "off", 24 | "@typescript-eslint/camelcase": "off", 25 | "@typescript-eslint/no-empty-function": "off", 26 | "@typescript-eslint/no-empty-interface": "off", 27 | "@typescript-eslint/no-namespace": "off", 28 | "@typescript-eslint/no-use-before-define": "off", 29 | "@typescript-eslint/no-var-requires": "off", 30 | "@typescript-eslint/consistent-type-imports": "error", 31 | "no-confusing-arrow": 0, 32 | "no-else-return": 0, 33 | "no-underscore-dangle": 0, 34 | "no-restricted-syntax": 0, 35 | "no-await-in-loop": 0, 36 | "tsdoc/syntax": 2, 37 | 38 | // Rules that we should enable: 39 | "@typescript-eslint/no-inferrable-types": "warn", 40 | "no-inner-declarations": "warn", 41 | 42 | // Rules we've disabled for now because they're so noisy (but we should really address) 43 | "@typescript-eslint/no-explicit-any": "off", 44 | "@typescript-eslint/no-non-null-assertion": "off", 45 | "@typescript-eslint/no-unused-vars": [ 46 | "error", 47 | { 48 | argsIgnorePattern: "^_", 49 | varsIgnorePattern: "^_", 50 | args: "after-used", 51 | ignoreRestSiblings: true, 52 | }, 53 | ], 54 | 55 | /* 56 | * simple-import-sort seems to be the most stable import sorting currently, 57 | * disable others 58 | */ 59 | "simple-import-sort/imports": "error", 60 | "simple-import-sort/exports": "error", 61 | "sort-imports": "off", 62 | "import/order": "off", 63 | 64 | "import/extensions": ["error", "ignorePackages"], 65 | "import/no-deprecated": "warn", 66 | 67 | // Apply has been more optimised than spread, use whatever feels right. 68 | "prefer-spread": "off", 69 | 70 | // note you must disable the base rule as it can report incorrect errors 71 | "no-duplicate-imports": "off", 72 | "import/no-duplicates": "error", 73 | }, 74 | overrides: [ 75 | // Rules for interfaces.ts files 76 | { 77 | files: ["**/interfaces.ts"], 78 | rules: { 79 | "no-restricted-syntax": [ 80 | "error", 81 | { 82 | selector: "TSModuleDeclaration[kind='global']", 83 | message: 84 | "No `declare global` allowed in `interface.ts` files since these type-only files may not be imported by dependents, recommend adding to `index.ts` instead.", 85 | }, 86 | ], 87 | }, 88 | }, 89 | 90 | // Rules for TypeScript only 91 | { 92 | files: ["*.ts", "*.tsx"], 93 | parser: "@typescript-eslint/parser", 94 | rules: { 95 | "no-dupe-class-members": "off", 96 | "no-undef": "off", 97 | // This rule doesn't understand import of './js' 98 | "import/no-unresolved": "off", 99 | }, 100 | }, 101 | 102 | // Rules for JavaScript only 103 | { 104 | files: ["*.js", "*.jsx", "*.mjs", "*.cjs"], 105 | rules: { 106 | "tsdoc/syntax": "off", 107 | "import/extensions": "off", 108 | }, 109 | }, 110 | 111 | // Stricter rules for source code 112 | { 113 | files: ["*/*/src/**/*.ts", "*/*/src/**/*.tsx"], 114 | extends: [ 115 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 116 | ], 117 | parser: "@typescript-eslint/parser", 118 | parserOptions: { 119 | project: true, 120 | }, 121 | rules: {}, 122 | }, 123 | 124 | // Rules for tests only 125 | { 126 | files: ["**/__tests__/**/*.{ts,mts,js,mjs}"], 127 | rules: { 128 | // Disable these to enable faster test writing 129 | "prefer-const": "off", 130 | "@typescript-eslint/no-explicit-any": "off", 131 | "@typescript-eslint/no-unused-vars": "off", 132 | "@typescript-eslint/explicit-function-return-type": "off", 133 | 134 | // We don't normally care about race conditions in tests 135 | "require-atomic-updates": "off", 136 | }, 137 | }, 138 | 139 | // Don't use Node.js builtins 140 | { 141 | files: ["src/**"], 142 | rules: { 143 | "@typescript-eslint/no-restricted-imports": [ 144 | "error", 145 | { 146 | paths: [ 147 | "assert", 148 | "buffer", 149 | "child_process", 150 | "cluster", 151 | "crypto", 152 | "dgram", 153 | "dns", 154 | "domain", 155 | "events", 156 | "freelist", 157 | "fs", 158 | "fs/promises", 159 | { name: "http", allowTypeImports: true }, 160 | "https", 161 | "module", 162 | "net", 163 | "os", 164 | "path", 165 | "punycode", 166 | "querystring", 167 | "readline", 168 | "repl", 169 | "smalloc", 170 | "stream", 171 | "string_decoder", 172 | "sys", 173 | "timers", 174 | "tls", 175 | "tracing", 176 | "tty", 177 | "url", 178 | "util", 179 | "vm", 180 | "zlib", 181 | ], 182 | patterns: ["node:*"], 183 | }, 184 | ], 185 | }, 186 | }, 187 | ], 188 | }; 189 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | cache: "yarn" 24 | 25 | - name: Install dependencies 26 | run: yarn install --frozen-lockfile 27 | 28 | - name: Run linter 29 | run: yarn lint 30 | 31 | - name: Run tests 32 | run: yarn test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /dist 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## The MIT License (MIT) 2 | 3 | Copyright © `2024` Benjie Gillam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the “Software”), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @graphile/depth-limit 2 | 3 | A highly capable depth limiting 4 | [GraphQL.js](https://github.com/graphql/graphql-js) validation rule to help 5 | protect your server from malicious operations. 6 | 7 | - Limits depth of field selections in an operation 8 | - Limits number of times a list may be nested in an operation 9 | - Limits number of times the same field coordinate may be (directly or 10 | indirectly) nested 11 | - Has separate limits for introspection queries versus regular operations 12 | - Can add limits for specific fields to say how many times they may be nested 13 | (e.g. allow "friends of friends" but deny "friends of friends of friends" via 14 | `{'User.friends': 2}`) 15 | - Can be configured such that each fragment reference also increments the depth 16 | (not recommended) 17 | 18 | ## FAQ 19 | 20 | ### Can I trust this module? 21 | 22 | You're right to check! This module is maintained by 23 | [Benjie Gillam](https://github.com/benjie), a member of the 24 | [GraphQL Technical Steering Committee](https://github.com/graphql/graphql-wg/blob/main/GraphQL-TSC.md#tsc-members). 25 | 26 | The module has no dependencies (though it does have a "peer" dependency of 27 | `graphql`, of course!), so auditing should be straightforward. 28 | 29 | The module is MIT licensed, so as it says in the license: THE SOFTWARE IS 30 | PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 31 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 32 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 33 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 34 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 35 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | ### Is this module Graphile-specific? 38 | 39 | No! This rule should work for any server that allows customizing GraphQL.js 40 | validation rules. It's not specific to PostGraphile, Gra*fast*, Grafserv or any 41 | other Graphile software. 42 | 43 | ### Do I need this module? 44 | 45 | GraphQL is a very powerful technology, but it must be handled carefully - it's 46 | relatively easy for a bad actor to write a small GraphQL query that causes the 47 | server to perform a disporportionaly large amount of work. For this reason, it's 48 | highly recommended that you only accept 49 | [trusted documents](https://benjie.dev/graphql/trusted-documents) to your 50 | GraphQL server - like Facebook do internally. 51 | 52 | If your GraphQL API is intended to be consumed by untrusted (or not fully 53 | trusted) third parties then you cannot rely on the security benefits of the 54 | Trusted Documents pattern, and thus you need to protect yourself from malicious 55 | queries. This rule is one very easy to use piece of protection (though you 56 | should note that it is not sufficient on its own, you should still use all the 57 | common patterns of protecting your API: rate limiting, circuit breakers, 58 | execution timeouts, enforcing pagination limits, body size limits, etc etc). 59 | 60 | ### Do I need this if I am using Trusted Documents (aka persisted queries)? 61 | 62 | Even if you are using the 63 | [Trusted Documents](https://benjie.dev/graphql/trusted-documents) (persisted 64 | queries, stored operations, etc) pattern (which you should be if your API is 65 | only intended to be consumed by your own websites, mobile apps and other 66 | first-party software), this is still a useful validation rule. 67 | 68 | Though Trusted Documents means that the concern of malicious operations is moot 69 | (every document is trusted, so none are expected to be malicious), this rule 70 | still helps to prevent your engineers from writing queries that are too complex 71 | and could potentially be too costly for your server. It's reasonable, when using 72 | Trusted Documents, to increase the limits as needed. 73 | 74 | ## Usage 75 | 76 | Install the module (and `graphql`!) with your package manager of choice: 77 | 78 | ``` 79 | yarn add graphql @graphile/depth-limit 80 | ``` 81 | 82 | How you use it then depends on which server software you're using. 83 | 84 | ### graphql-http 85 | 86 | Add `depthLimit()` to the `validationRules` list passed to the `createHandler` 87 | call. Here's an example based on the "With `http`" usage example 88 | [in the graphql-http docs](https://github.com/graphql/graphql-http/tree/9985b4fbb5b2316b3f6987e67f560ef1ce91a7b9?tab=readme-ov-file#with-http): 89 | 90 | ```ts 91 | import http from "http"; 92 | import { createHandler } from "graphql-http/lib/use/http"; 93 | import { depthLimit } from "@graphile/depth-limit"; 94 | import { schema } from "./schema.js"; 95 | 96 | // Create the GraphQL over HTTP handler 97 | export const handler = createHandler({ 98 | schema, 99 | validationRules: [ 100 | depthLimit({ 101 | // Options here 102 | }), 103 | ], 104 | }); 105 | ``` 106 | 107 | ### Envelop 108 | 109 | If you're using the excellent 110 | [envelop plugin system](https://the-guild.dev/graphql/envelop) for GraphQL you 111 | can use our handy `useDepthLimit` export to build an envelop plugin to add the 112 | validation rule: 113 | 114 | ```ts 115 | import * as GraphQLJS from "graphql"; 116 | import { envelop, useEngine } from "@envelop/core"; 117 | import { useDepthLimit } from "@graphile/depth-limit"; 118 | 119 | const getEnveloped = envelop({ 120 | plugins: [ 121 | useEngine(GraphQLJS), 122 | useDepthLimit({ 123 | // Options here 124 | }), 125 | ], 126 | }); 127 | ``` 128 | 129 | ### Yoga 130 | 131 | With GraphQL Yoga, you'll follow a similar setup as with Envelop above; see the 132 | documentation on 133 | [Yoga plugins](https://the-guild.dev/graphql/yoga-server/docs/features/envelop-plugins). 134 | 135 | ```ts 136 | import { createYoga } from "graphql-yoga"; 137 | import { useDepthLimit } from "@graphile/depth-limit"; 138 | 139 | const yoga = createYoga({ 140 | plugins: [ 141 | useDepthLimit({ 142 | // Options here 143 | }), 144 | ], 145 | }); 146 | ``` 147 | 148 | ### Raw GraphQL.js 149 | 150 | If you're using the `graphql` module from npm directly, import the `depthLimit` 151 | factory function and use its result alongside the `specifiedRules` (the rules 152 | from the GraphQL specification) when validating a GraphQL operation: 153 | 154 | ```ts 155 | import { depthLimit } from "@graphile/depth-limit"; 156 | import { validate, specifiedRules, parse, execute } from "graphql"; 157 | 158 | const allValidationRules = [ 159 | // ❗ Don't forget to use the built in GraphQL validation rules! 160 | ...specifiedRules, 161 | 162 | //👇👇👇👇 163 | depthLimit({ 164 | // Options here 165 | }), 166 | //👆👆👆👆 167 | ]; 168 | 169 | // This is just an example (and assumes the schema has already been validated). 170 | // The `validate()` call is the part that would differ from what you would 171 | // normally have; in particular the optional third argument is specified. 172 | export function executeGraphQLRequest( 173 | schema: GraphQLSchema, 174 | source: string, 175 | variableValues?: Record, 176 | ) { 177 | let document; 178 | try { 179 | document = parse(source); 180 | } catch (syntaxError) { 181 | return { 182 | errors: [syntaxError], 183 | }; 184 | } 185 | 186 | // 👇👇👇👇👇👇👇👇👇 187 | const errors = validate(schema, document, allValidationRules); 188 | // 👆👆👆👆👆👆👆👆👆 189 | 190 | if (errors.length > 0) { 191 | return { errors }; 192 | } 193 | 194 | return execute({ schema, document, variableValues }); 195 | } 196 | ``` 197 | 198 | ## Options 199 | 200 | No matter how you load this validation rule, it accepts the same options: 201 | 202 | ````ts 203 | export type Options = { 204 | /** 205 | * How many selection sets deep may the user query? 206 | * 207 | * @defaultValue `12` 208 | */ 209 | maxDepth?: number; 210 | 211 | /** 212 | * How many nested lists deep may the user query? 213 | * 214 | * @defaultValue `4` 215 | */ 216 | maxListDepth?: number; 217 | 218 | /** 219 | * How many times may a field reference itself recursively by default. This 220 | * is to try and block the most common forms of attack automatically, if you 221 | * have legitimate cases where a field should be referenced recursively then 222 | * you may specifically override those via `maxDepthByFieldCoordinates`. 223 | * 224 | * @defaultValue `2` 225 | */ 226 | maxSelfReferentialDepth?: number; 227 | 228 | /** 229 | * How many selection sets deep may the user query in introspection? 230 | * 231 | * @defaultValue `12` 232 | */ 233 | maxIntrospectionDepth?: number; 234 | 235 | /** 236 | * How many nested lists deep may the user query in introspection? 237 | * 238 | * @defaultValue `3` 239 | */ 240 | maxIntrospectionListDepth?: number; 241 | 242 | /** 243 | * How many times may an introspection field reference itself recursively by 244 | * default. This is to try and block the most common forms of attack 245 | * automatically, if you have legitimate cases where a field should be 246 | * referenced recursively then you may specifically override those via 247 | * `maxDepthByFieldCoordinates`. 248 | * 249 | * @defaultValue `2` 250 | */ 251 | maxIntrospectionSelfReferentialDepth?: number; 252 | 253 | /** 254 | * Set `true` if you want fragment spreads to add to the depth. Not 255 | * recommended; fragments are essential to using GraphQL correctly so using 256 | * them should not have a penalty. 257 | * 258 | * Whether this setting is true or false, the fields referenced by the 259 | * fragment will still of course weigh into the depth calculations. 260 | * 261 | * @defaultValue `false` 262 | */ 263 | fragmentsAddToDepth?: boolean; 264 | 265 | /** 266 | * Limits the number of times a particular field coordinate can be nested 267 | * inside itself; for example: 268 | * 269 | * ``` 270 | * maxDepthByFieldCoordinates: { 271 | * 'User.friends': 2, 272 | * } 273 | * ``` 274 | * 275 | * Would allow you to load a users friends, or their friends of friends, but 276 | * not their friends of friends of friends. 277 | */ 278 | maxDepthByFieldCoordinates?: Record; 279 | 280 | /** 281 | * If true, informs the user what the issues are. Setting this to true could 282 | * have security ramifications as it will make it easier for an attacker to 283 | * determine your limits (however, this could be derived with minimal effort 284 | * also, so is potentially not a big deal). 285 | * 286 | * @defaultValue `false` 287 | */ 288 | revealDetails?: boolean; 289 | }; 290 | ```` 291 | 292 | ### How to choose the right options for you 293 | 294 | Firstly, we've set the introspection defaults to allow the most common 295 | introspection queries to pass validation, so you shouldn't need to customize 296 | those unless you're doing something fancy. 297 | 298 | The main options you should look at customizing are `maxListDepth`, `maxDepth` 299 | and `maxSelfReferentialDepth` (in that order). 300 | 301 | If you're starting from scratch you should set your settings low, and work your 302 | way up as you need to. If you have an existing server then it may make sense to 303 | track your queries for a while and figure out the higest values used (see the 304 | `countOperationDepths` function below), and set your limits to that to prevent 305 | more complex queries. 306 | 307 | Ultimately, tuning these parameters is more of an art than a science, and this 308 | is one reason why this validation rule isn't a built in feature of the `graphql` 309 | module or, indeed, the specification. 310 | 311 | #### maxListDepth 312 | 313 | `maxListDepth` you should set to the lowest value you can tolerate; something 314 | like 2, 3 or 4. It's unlikely that your users are successfully paginating 315 | through collections that are deeper than this. Note: `maxListDepth` only counts 316 | selection sets inside of lists, so scalar lists are ignored and do not add to 317 | the count, this is by design. 318 | 319 | #### maxDepth 320 | 321 | `maxDepth` should also be set to a suitably low value, although it's much more 322 | acceptable to grow this one. One of the common introspection queries requires a 323 | depth of 12 (primarily due to `ofType { ofType { ofType { ... } } }` for 324 | determining the type of a field or argument), so this is the default we use even 325 | for non-introspection queries; lower would be better, but higher is also fine if 326 | you need it. Note that if your schema uses the Relay Cursor Connection pattern 327 | it may end up requiring deeper limits than you might realise. 328 | 329 | #### maxSelfReferentialDepth 330 | 331 | `maxSelfReferentialDepth` defaults to just `2` and prevents attackers from 332 | exploiting cycles in your graph (e.g. 333 | `{ friends { friends { friends { ... } } } }`). We recommend that you keep this 334 | limit low, and then add specific overrides via `maxDepthByFieldCoordinates` if a 335 | particular field is expected to commonly be used in a nested fashion. 336 | 337 | #### maxDepthByFieldCoordinates 338 | 339 | If you have a particular cycle in your schema (for example you might have 340 | `User.friends` which returns `[User]`, and thus you could query it as 341 | `{ currentUser { friends { friends { friends { friends { ... } } } } } }`) and 342 | you would like to prevent people nesting it too many times (or, alternatively, 343 | would like to allow them to nest it more than `maxSelfReferentialDepth` times) 344 | then you can set specific limits on the number of times a particular field may 345 | be referenced recursively by specifying a numeric limit for its 346 | [schema coordinate](https://github.com/graphql/graphql-wg/blob/main/rfcs/SchemaCoordinates.md#typeattribute), 347 | for example: 348 | 349 | ```ts 350 | import { depthLimit } from "@graphile/depth-limit"; 351 | 352 | export const rule = depthLimit({ 353 | maxSelfReferentialDepth: 2, 354 | maxDepthByFieldCoordinates: { 355 | // Allow `{ currentUser { friends { friends { friends { name } } } } }` 356 | // But forbid `{ currentUser { friends { friends { friends { friends { name } } } } } }` 357 | "User.friends": 3, 358 | }, 359 | }); 360 | ``` 361 | 362 | ## Community-funded open-source software 363 | 364 | I love GraphQL (hence the name: Graphile!) and I am a firm believer in the 365 | benefits of open source software; but I don't have time to write awesome 366 | software like this _and_ go out and earn a days wages. If you use this 367 | validation rule, please consider 368 | [sponsoring me](https://github.com/sponsors/benjie)\* so I can keep maintaining 369 | and building this kind of critical software, and maybe I'll even get a chance to 370 | [work through my mountain of GraphQL spec PRs](https://github.com/graphql/graphql-spec/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc+author%3Abenjie). 371 | Thanks for considering sponsoring me, there's no way this project would exist 372 | without the support of my sponsors. 373 | 374 | \* _Other sponsorship methods are available, if you're interested drop me an 375 | email on my GitHub handle at graphile.com._ 376 | 377 | ## `countOperationDepths` 378 | 379 | This function returns the depths seen in a given document to help you to figure 380 | out what good values to set for your options are. Most options are ignored here, 381 | the main (only?) one that impacts the result of `depths` is 382 | `fragmentsAddToDepth`. 383 | 384 | Here you can provide both the schema SDL (or schema object) and the document 385 | source text. If you have already parsed the document then you may wish to use 386 | `countDepths` instead as it is more performant. 387 | 388 | ```ts 389 | import { countOperationDepths } from "@graphile/depth-limit"; 390 | 391 | const schema = /* GraphQL */ ` 392 | type Query { 393 | currentUser: User 394 | } 395 | type User { 396 | name: String 397 | friends: [User!]! 398 | } 399 | type Mutation { 400 | addFriend(id: ID!): AddFriendPayload 401 | } 402 | type AddFriendPayload { 403 | query: Query 404 | } 405 | `; 406 | 407 | const source = /* GraphQL */ ` 408 | query FriendsOfFriends { 409 | ...FoF 410 | } 411 | mutation AddFriend($id: ID!) { 412 | addFriend(id: $id) { 413 | query { 414 | ...FoF 415 | } 416 | } 417 | } 418 | fragment FoF on Query { 419 | currentUser { 420 | name 421 | friends { 422 | name 423 | friends { 424 | name 425 | } 426 | } 427 | } 428 | } 429 | `; 430 | 431 | const depthsByOperationName = countOperationDepths(schema, source); 432 | 433 | import assert from "assert"; 434 | assert.deepEqual(depthsByOperationName, { 435 | FriendsOfFriends: { 436 | $$depth: 3, 437 | $$listDepth: 2, 438 | "Query.currentUser": 1, 439 | "User.name": 1, 440 | "User.friends": 2, 441 | }, 442 | AddFriend: { 443 | $$depth: 5, 444 | $$listDepth: 2, 445 | "Mutation.addFriend": 1, 446 | "AddFriendPayload.query": 1, 447 | "Query.currentUser": 1, 448 | "User.name": 1, 449 | "User.friends": 2, 450 | }, 451 | }); 452 | ``` 453 | 454 | ## `countDepth` 455 | 456 | This function returns the depths seen in a given operation to help you to figure 457 | out what good values to set for your options are. Most options are ignored here, 458 | the main (only?) one that impacts the result of `depths` is 459 | `fragmentsAddToDepth`. 460 | 461 | ```ts 462 | import assert from "assert"; 463 | import { countDepth } from "@graphile/depth-limit"; 464 | import { parse, Kind, getOperationAST } from "graphql"; 465 | import { schema } from "./schema.js"; 466 | 467 | const query = /* GraphQL */ ` 468 | query FriendsOfFriends { 469 | currentUser { 470 | name 471 | friends { 472 | name 473 | friends { 474 | name 475 | } 476 | } 477 | } 478 | } 479 | `; 480 | 481 | const document = parse(query); 482 | const operationName = undefined; 483 | const fragments = document.definitions.filter( 484 | /** @type {(d: any) => d is import('graphql').FragmentDefinitionNode} */ 485 | (d) => d.kind === Kind.FRAGMENT_DEFINITION, 486 | ); 487 | const operation = getOperationAST(document, operationName); 488 | const { depths, resolvedOptions } = countDepth(schema, operation, fragments, { 489 | // Options here 490 | }); 491 | assert.deepEqual(depths, { 492 | $$depth: 3, 493 | $$listDepth: 2, 494 | "Query.currentUser": 1, 495 | "User.name": 1, 496 | "User.friends": 2, 497 | }); 498 | ``` 499 | 500 | ## Caveats 501 | 502 | We count fields on their current named type whilst traversing the document; this 503 | type could be an object, interface, or union type. Therefore, a field that 504 | exists on an interface would be counted as a different coordinate depending on 505 | if the user accesses it via the interface directly or one of the 506 | implementations. 507 | 508 | ## See also 509 | 510 | This isn't the only depth limiting validation rule, others to check out are: 511 | 512 | - [`graphql-depth-limit`](https://www.npmjs.com/package/graphql-depth-limit) - 513 | the OG depth limiter, last published 7 years ago and the repository no longer 514 | exists 515 | - [`@escape.tech/graphql-armor-max-depth`](https://github.com/Escape-Technologies/graphql-armor/tree/main/packages/plugins/max-depth) - 516 | a depth limiting plugin for the GraphQL Armor system 517 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | ## v0.3.0 4 | 5 | - Add the `countOperationDepths` helper function 6 | 7 | ## v0.2.0 8 | 9 | - Add the `maxSelfReferentialDepth` option 10 | - Export the `Options` type 11 | - Clarify `fragmentsAddToDepth` setting 12 | 13 | ## v0.1.0 14 | 15 | - Initial release 16 | -------------------------------------------------------------------------------- /__tests__/basics-fragments.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { parseAndValidate, jsonClone } from "./utils.mjs"; 5 | 6 | /** @type {import("../dist/index.js").Options} */ 7 | const options = { 8 | maxDepth: 10, 9 | maxListDepth: 5, 10 | maxSelfReferentialDepth: 20, 11 | revealDetails: true, 12 | }; 13 | 14 | describe("basics-fragments", () => { 15 | it("allows friends^5", () => { 16 | const errors = parseAndValidate( 17 | /* GraphQL */ ` 18 | query FoFoFoFoF { 19 | currentUser { 20 | ...F1 21 | } 22 | } 23 | fragment F1 on User { 24 | friends { 25 | ...F2 26 | } 27 | } 28 | fragment F2 on User { 29 | friends { 30 | ...F3 31 | } 32 | } 33 | fragment F3 on User { 34 | friends { 35 | ...F4 36 | } 37 | } 38 | fragment F4 on User { 39 | friends { 40 | ...F5 41 | } 42 | } 43 | fragment F5 on User { 44 | friends { 45 | name 46 | } 47 | } 48 | `, 49 | options, 50 | ); 51 | assert.deepEqual(jsonClone(errors), []); 52 | }); 53 | 54 | it("rejects friends^6", () => { 55 | const errors = parseAndValidate( 56 | /* GraphQL */ ` 57 | query FoFoFoFoFoF { 58 | currentUser { 59 | ...F1 60 | } 61 | } 62 | fragment F1 on User { 63 | friends { 64 | ...F2 65 | } 66 | } 67 | fragment F2 on User { 68 | friends { 69 | ...F3 70 | } 71 | } 72 | fragment F3 on User { 73 | friends { 74 | ...F4 75 | } 76 | } 77 | fragment F4 on User { 78 | friends { 79 | ...F5 80 | } 81 | } 82 | fragment F5 on User { 83 | friends { 84 | ...F6 85 | } 86 | } 87 | fragment F6 on User { 88 | friends { 89 | name 90 | } 91 | } 92 | `, 93 | options, 94 | ); 95 | assert.deepEqual(jsonClone(errors), [ 96 | { 97 | message: 98 | "'FoFoFoFoFoF' exceeds operation depth limits: " + 99 | "operation list depth 6 exceeds maximum of 5.", 100 | locations: [ 101 | { 102 | line: 2, 103 | column: 9, 104 | }, 105 | ], 106 | }, 107 | ]); 108 | }); 109 | 110 | it("allows self^9", () => { 111 | const errors = parseAndValidate( 112 | /* GraphQL */ ` 113 | query Self9 { 114 | currentUser { 115 | self { 116 | ...F1 117 | } 118 | } 119 | } 120 | fragment F1 on User { 121 | self { 122 | ...F2 123 | } 124 | } 125 | fragment F2 on User { 126 | self { 127 | ...F3 128 | } 129 | } 130 | fragment F3 on User { 131 | self { 132 | ...F4 133 | } 134 | } 135 | fragment F4 on User { 136 | self { 137 | ...F5 138 | } 139 | } 140 | fragment F5 on User { 141 | self { 142 | ...F6 143 | } 144 | } 145 | fragment F6 on User { 146 | self { 147 | ...F7 148 | } 149 | } 150 | fragment F7 on User { 151 | self { 152 | ...F8 153 | } 154 | } 155 | fragment F8 on User { 156 | self { 157 | name 158 | } 159 | } 160 | `, 161 | options, 162 | ); 163 | assert.deepEqual(jsonClone(errors), []); 164 | }); 165 | 166 | it("rejects self^10", () => { 167 | const errors = parseAndValidate( 168 | /* GraphQL */ ` 169 | query Self10 { 170 | currentUser { 171 | self { 172 | ...F1 173 | } 174 | } 175 | } 176 | fragment F1 on User { 177 | self { 178 | ...F2 179 | } 180 | } 181 | fragment F2 on User { 182 | self { 183 | ...F3 184 | } 185 | } 186 | fragment F3 on User { 187 | self { 188 | ...F4 189 | } 190 | } 191 | fragment F4 on User { 192 | self { 193 | ...F5 194 | } 195 | } 196 | fragment F5 on User { 197 | self { 198 | ...F6 199 | } 200 | } 201 | fragment F6 on User { 202 | self { 203 | ...F7 204 | } 205 | } 206 | fragment F7 on User { 207 | self { 208 | ...F8 209 | } 210 | } 211 | fragment F8 on User { 212 | self { 213 | ...F9 214 | } 215 | } 216 | fragment F9 on User { 217 | self { 218 | name 219 | } 220 | } 221 | `, 222 | options, 223 | ); 224 | assert.deepEqual(jsonClone(errors), [ 225 | { 226 | message: 227 | "'Self10' exceeds operation depth limits: " + 228 | "operation depth 11 exceeds maximum of 10.", 229 | locations: [ 230 | { 231 | line: 2, 232 | column: 9, 233 | }, 234 | ], 235 | }, 236 | ]); 237 | }); 238 | 239 | it("supports custom limit", () => { 240 | const errors = parseAndValidate( 241 | /* GraphQL */ ` 242 | query FoFoF { 243 | currentUser { 244 | ...F1 245 | } 246 | } 247 | fragment F1 on User { 248 | friends { 249 | ...F2 250 | } 251 | } 252 | fragment F2 on User { 253 | friends { 254 | ...F3 255 | } 256 | } 257 | fragment F3 on User { 258 | friends { 259 | name 260 | } 261 | } 262 | `, 263 | { 264 | ...options, 265 | maxDepthByFieldCoordinates: { 266 | // Permit friends of friends, but don't allow going any deeper 267 | "User.friends": 2, 268 | }, 269 | }, 270 | ); 271 | assert.deepEqual(jsonClone(errors), [ 272 | { 273 | message: 274 | "'FoFoF' exceeds operation depth limits: " + 275 | "field User.friends nested 3 times which exceeds maximum of 2.", 276 | locations: [ 277 | { 278 | line: 2, 279 | column: 9, 280 | }, 281 | ], 282 | }, 283 | ]); 284 | }); 285 | }); 286 | -------------------------------------------------------------------------------- /__tests__/basics.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { parseAndValidate, jsonClone } from "./utils.mjs"; 5 | 6 | /** @type {import("../dist/index.js").Options} */ 7 | const options = { 8 | maxListDepth: 5, 9 | maxDepth: 10, 10 | maxSelfReferentialDepth: 20, 11 | revealDetails: true, 12 | }; 13 | 14 | describe("basics", () => { 15 | it("allows friends", () => { 16 | const errors = parseAndValidate( 17 | /* GraphQL */ ` 18 | query FoFoFoFoF { 19 | currentUser { 20 | friends { 21 | name 22 | } 23 | } 24 | } 25 | `, 26 | options, 27 | ); 28 | assert.deepEqual(jsonClone(errors), []); 29 | }); 30 | 31 | it("rejects friends^3 due to maxSelfReferentialDepth", () => { 32 | const errors = parseAndValidate( 33 | /* GraphQL */ ` 34 | query FoFoF { 35 | currentUser { 36 | friends { 37 | friends { 38 | friends { 39 | name 40 | } 41 | } 42 | } 43 | } 44 | } 45 | `, 46 | { ...options, maxSelfReferentialDepth: 2 }, 47 | ); 48 | assert.deepEqual(jsonClone(errors), [ 49 | { 50 | message: 51 | "'FoFoF' exceeds operation depth limits: " + 52 | "field User.friends nested 3 times which exceeds self referential maximum of 2.", 53 | locations: [ 54 | { 55 | line: 2, 56 | column: 9, 57 | }, 58 | ], 59 | }, 60 | ]); 61 | }); 62 | 63 | it("allows friends^5", () => { 64 | const errors = parseAndValidate( 65 | /* GraphQL */ ` 66 | query FoFoFoFoF { 67 | currentUser { 68 | friends { 69 | friends { 70 | friends { 71 | friends { 72 | friends { 73 | name 74 | } 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | `, 82 | options, 83 | ); 84 | assert.deepEqual(jsonClone(errors), []); 85 | }); 86 | 87 | it("rejects friends^6", () => { 88 | const errors = parseAndValidate( 89 | /* GraphQL */ ` 90 | query FoFoFoFoFoF { 91 | currentUser { 92 | friends { 93 | friends { 94 | friends { 95 | friends { 96 | friends { 97 | friends { 98 | name 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | `, 108 | options, 109 | ); 110 | assert.deepEqual(jsonClone(errors), [ 111 | { 112 | message: 113 | "'FoFoFoFoFoF' exceeds operation depth limits: " + 114 | "operation list depth 6 exceeds maximum of 5.", 115 | locations: [ 116 | { 117 | line: 2, 118 | column: 9, 119 | }, 120 | ], 121 | }, 122 | ]); 123 | }); 124 | 125 | it("allows self^9", () => { 126 | const errors = parseAndValidate( 127 | /* GraphQL */ ` 128 | query Self9 { 129 | currentUser { 130 | self { 131 | self { 132 | self { 133 | self { 134 | self { 135 | self { 136 | self { 137 | self { 138 | self { 139 | name 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | `, 152 | options, 153 | ); 154 | assert.deepEqual(jsonClone(errors), []); 155 | }); 156 | 157 | it("rejects self^10", () => { 158 | const errors = parseAndValidate( 159 | /* GraphQL */ ` 160 | query Self10 { 161 | currentUser { 162 | self { 163 | self { 164 | self { 165 | self { 166 | self { 167 | self { 168 | self { 169 | self { 170 | self { 171 | self { 172 | name 173 | } 174 | } 175 | } 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | } 183 | } 184 | } 185 | `, 186 | options, 187 | ); 188 | assert.deepEqual(jsonClone(errors), [ 189 | { 190 | message: 191 | "'Self10' exceeds operation depth limits: " + 192 | "operation depth 11 exceeds maximum of 10.", 193 | locations: [ 194 | { 195 | line: 2, 196 | column: 9, 197 | }, 198 | ], 199 | }, 200 | ]); 201 | }); 202 | 203 | it("rejects self>otherSelf^5", () => { 204 | const errors = parseAndValidate( 205 | /* GraphQL */ ` 206 | query SelfOtherSelf5 { 207 | currentUser { 208 | self { 209 | otherSelf { 210 | self { 211 | otherSelf { 212 | self { 213 | otherSelf { 214 | self { 215 | otherSelf { 216 | self { 217 | otherSelf { 218 | name 219 | } 220 | } 221 | } 222 | } 223 | } 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | } 231 | `, 232 | options, 233 | ); 234 | assert.deepEqual(jsonClone(errors), [ 235 | { 236 | message: 237 | "'SelfOtherSelf5' exceeds operation depth limits: " + 238 | "operation depth 11 exceeds maximum of 10.", 239 | locations: [ 240 | { 241 | line: 2, 242 | column: 9, 243 | }, 244 | ], 245 | }, 246 | ]); 247 | }); 248 | 249 | it("supports custom limit", () => { 250 | const errors = parseAndValidate( 251 | /* GraphQL */ ` 252 | query FoFoFoF { 253 | currentUser { 254 | friends { 255 | friends { 256 | friends { 257 | friends { 258 | name 259 | } 260 | } 261 | } 262 | } 263 | } 264 | } 265 | `, 266 | { 267 | ...options, 268 | maxDepthByFieldCoordinates: { 269 | // Permit friends of friends of friends, but don't allow going any deeper 270 | "User.friends": 3, 271 | }, 272 | }, 273 | ); 274 | assert.deepEqual(jsonClone(errors), [ 275 | { 276 | message: 277 | "'FoFoFoF' exceeds operation depth limits: " + 278 | "field User.friends nested 4 times which exceeds maximum of 3.", 279 | locations: [ 280 | { 281 | line: 2, 282 | column: 9, 283 | }, 284 | ], 285 | }, 286 | ]); 287 | }); 288 | }); 289 | -------------------------------------------------------------------------------- /__tests__/countDepth.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { it } from "node:test"; 3 | import assert from "node:assert"; 4 | import { parse, Kind, getOperationAST } from "graphql"; 5 | import { countDepth } from "../dist/index.js"; 6 | import { getSchema } from "./utils.mjs"; 7 | 8 | const query = /* GraphQL */ ` 9 | query FriendsOfFriends { 10 | currentUser { 11 | name 12 | friends { 13 | name 14 | friends { 15 | name 16 | } 17 | } 18 | } 19 | } 20 | `; 21 | 22 | it("works as in README", async () => { 23 | const document = parse(query); 24 | const operationName = undefined; 25 | const fragments = document.definitions.filter( 26 | /** @type {(d: any) => d is import('graphql').FragmentDefinitionNode} */ 27 | (d) => d.kind === Kind.FRAGMENT_DEFINITION, 28 | ); 29 | const operation = getOperationAST(document, operationName); 30 | if (!operation) { 31 | throw new Error( 32 | `Could not determine operation, please check the operationName`, 33 | ); 34 | } 35 | const schema = await getSchema(); 36 | const { depths, resolvedOptions } = countDepth(schema, operation, fragments, { 37 | // Options here 38 | }); 39 | assert.deepEqual(depths, { 40 | $$depth: 3, 41 | $$listDepth: 2, 42 | "Query.currentUser": 1, 43 | "User.name": 1, 44 | "User.friends": 2, 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /__tests__/countOperationDepths.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { it } from "node:test"; 3 | import assert from "node:assert"; 4 | import { countOperationDepths } from "../dist/index.js"; 5 | 6 | it("works as in README", () => { 7 | const schema = /* GraphQL */ ` 8 | type Query { 9 | currentUser: User 10 | } 11 | type User { 12 | name: String 13 | friends: [User!]! 14 | } 15 | type Mutation { 16 | addFriend(id: ID!): AddFriendPayload 17 | } 18 | type AddFriendPayload { 19 | query: Query 20 | } 21 | `; 22 | 23 | const source = /* GraphQL */ ` 24 | query FriendsOfFriends { 25 | ...FoF 26 | } 27 | mutation AddFriend($id: ID!) { 28 | addFriend(id: $id) { 29 | query { 30 | ...FoF 31 | } 32 | } 33 | } 34 | fragment FoF on Query { 35 | currentUser { 36 | name 37 | friends { 38 | name 39 | friends { 40 | name 41 | } 42 | } 43 | } 44 | } 45 | `; 46 | 47 | const depthsByOperationName = countOperationDepths(schema, source); 48 | 49 | assert.deepEqual(depthsByOperationName, { 50 | FriendsOfFriends: { 51 | $$depth: 3, 52 | $$listDepth: 2, 53 | "Query.currentUser": 1, 54 | "User.name": 1, 55 | "User.friends": 2, 56 | }, 57 | AddFriend: { 58 | $$depth: 5, 59 | $$listDepth: 2, 60 | "Mutation.addFriend": 1, 61 | "AddFriendPayload.query": 1, 62 | "Query.currentUser": 1, 63 | "User.name": 1, 64 | "User.friends": 2, 65 | }, 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /__tests__/directives.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { parseAndValidate, jsonClone, __dirname } from "./utils.mjs"; 5 | import { readFileSync } from "node:fs"; 6 | 7 | /** @type {import("../dist/index.js").Options} */ 8 | const options = { 9 | revealDetails: true, 10 | }; 11 | 12 | describe("directives", () => { 13 | it("Works with skip and include", () => { 14 | const errors = parseAndValidate( 15 | readFileSync(`${__dirname}/queries/directives.test.graphql`, "utf8"), 16 | options, 17 | "defaultOptions.1.graphql", 18 | ); 19 | assert.deepEqual(jsonClone(errors), []); 20 | }); 21 | it("Works with skip and include on additional levels", () => { 22 | const errors = parseAndValidate( 23 | readFileSync( 24 | `${__dirname}/queries/directives-additional.test.graphql`, 25 | "utf8", 26 | ), 27 | options, 28 | "defaultOptions.1.graphql", 29 | ); 30 | assert.deepEqual(jsonClone(errors), []); 31 | }); 32 | it("Works with skip and include with fragments", () => { 33 | const errors = parseAndValidate( 34 | readFileSync( 35 | `${__dirname}/queries/directives-fragments.test.graphql`, 36 | "utf8", 37 | ), 38 | options, 39 | "defaultOptions.1.graphql", 40 | ); 41 | assert.deepEqual(jsonClone(errors), []); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /__tests__/introspection.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { getIntrospectionQuery } from "graphql"; 5 | import { parseAndValidate, jsonClone } from "./utils.mjs"; 6 | 7 | describe("introspection", () => { 8 | it("allows default introspection query", () => { 9 | const errors = parseAndValidate(getIntrospectionQuery()); 10 | assert.deepEqual(jsonClone(errors), []); 11 | }); 12 | 13 | it("allows thorough introspection query", () => { 14 | const errors = parseAndValidate( 15 | getIntrospectionQuery({ 16 | descriptions: true, 17 | specifiedByUrl: true, 18 | schemaDescription: true, 19 | directiveIsRepeatable: true, 20 | inputValueDeprecation: true, 21 | }), 22 | ); 23 | assert.deepEqual(jsonClone(errors), []); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /__tests__/invalidQueries.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { parseAndValidate, jsonClone } from "./utils.mjs"; 5 | 6 | /** @type {import("../dist/index.js").Options} */ 7 | const options = { 8 | maxDepth: 10, 9 | maxListDepth: 5, 10 | maxSelfReferentialDepth: 20, 11 | revealDetails: true, 12 | }; 13 | 14 | describe("doesn't die on invalid queries", () => { 15 | it("direct recursive fragments", () => { 16 | const errors = parseAndValidate( 17 | /* GraphQL */ ` 18 | query FoFoFoFoF { 19 | currentUser { 20 | ...F1 21 | } 22 | } 23 | fragment F1 on User { 24 | friends { 25 | ...F1 26 | } 27 | } 28 | `, 29 | options, 30 | "simple.graphql", 31 | true, 32 | ); 33 | assert.deepEqual(jsonClone(errors), []); 34 | }); 35 | it("indirect recursive fragments", () => { 36 | const errors = parseAndValidate( 37 | /* GraphQL */ ` 38 | query FoFoFoFoF { 39 | currentUser { 40 | ...F1 41 | } 42 | } 43 | fragment F1 on User { 44 | friends { 45 | ...F2 46 | } 47 | } 48 | fragment F2 on User { 49 | friends { 50 | ...F3 51 | } 52 | } 53 | fragment F3 on User { 54 | friends { 55 | ...F4 56 | } 57 | } 58 | fragment F4 on User { 59 | friends { 60 | ...F5 61 | } 62 | } 63 | fragment F5 on User { 64 | friends { 65 | ...F1 66 | } 67 | } 68 | `, 69 | options, 70 | "simple.graphql", 71 | true, 72 | ); 73 | assert.deepEqual(jsonClone(errors), []); 74 | }); 75 | 76 | it("non-existent fragments", () => { 77 | const errors = parseAndValidate( 78 | /* GraphQL */ ` 79 | query FoFoFoFoF { 80 | currentUser { 81 | ...F1 82 | } 83 | } 84 | fragment F1 on User { 85 | friends { 86 | ...F2 87 | } 88 | } 89 | fragment F2 on User { 90 | friends { 91 | ...F3 92 | } 93 | } 94 | fragment F3 on User { 95 | friends { 96 | ...F4 97 | } 98 | } 99 | fragment F4 on User { 100 | friends { 101 | ...F5 102 | } 103 | } 104 | fragment F5 on User { 105 | friends { 106 | ...NonExistantFragment 107 | } 108 | } 109 | `, 110 | options, 111 | "simple.graphql", 112 | true, 113 | ); 114 | assert.deepEqual(jsonClone(errors), []); 115 | }); 116 | 117 | it("non-existent fields", () => { 118 | const errors = parseAndValidate( 119 | /* GraphQL */ ` 120 | query FoFoFoFoF { 121 | currentUser { 122 | ...F1 123 | } 124 | } 125 | fragment F1 on User { 126 | friends { 127 | ...F2 128 | } 129 | } 130 | fragment F2 on User { 131 | friends { 132 | ...F3 133 | } 134 | } 135 | fragment F3 on User { 136 | friends { 137 | ...F4 138 | } 139 | } 140 | fragment F4 on User { 141 | nonExistentField { 142 | ...F5 143 | } 144 | } 145 | fragment F5 on User { 146 | friends { 147 | name 148 | } 149 | } 150 | `, 151 | options, 152 | "simple.graphql", 153 | true, 154 | ); 155 | assert.deepEqual(jsonClone(errors), []); 156 | }); 157 | }); 158 | -------------------------------------------------------------------------------- /__tests__/mutation.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { parseAndValidate, jsonClone, __dirname } from "./utils.mjs"; 5 | import { readFileSync } from "node:fs"; 6 | 7 | /** @type {import("../dist/index.js").Options} */ 8 | const options = { 9 | revealDetails: true, 10 | }; 11 | 12 | describe("mutation test", () => { 13 | it("works with create mutations", () => { 14 | const errors = parseAndValidate( 15 | readFileSync(`${__dirname}/queries/mutation-create.test.graphql`, "utf8"), 16 | options, 17 | "defaultOptions.1.graphql", 18 | ); 19 | assert.deepEqual(jsonClone(errors), []); 20 | }); 21 | it("works with delete mutations", () => { 22 | const errors = parseAndValidate( 23 | readFileSync(`${__dirname}/queries/mutation-delete.test.graphql`, "utf8"), 24 | options, 25 | "defaultOptions.1.graphql", 26 | ); 27 | assert.deepEqual(jsonClone(errors), []); 28 | }); 29 | it("works with return type mutations", () => { 30 | const errors = parseAndValidate( 31 | readFileSync( 32 | `${__dirname}/queries/mutation-return-types.test.graphql`, 33 | "utf8", 34 | ), 35 | options, 36 | "defaultOptions.1.graphql", 37 | ); 38 | assert.deepEqual(jsonClone(errors), []); 39 | }); 40 | it("works with update mutations", () => { 41 | const errors = parseAndValidate( 42 | readFileSync(`${__dirname}/queries/mutation-update.test.graphql`, "utf8"), 43 | options, 44 | "defaultOptions.1.graphql", 45 | ); 46 | assert.deepEqual(jsonClone(errors), []); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /__tests__/posts.test.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import assert from "node:assert"; 3 | import { describe, it } from "node:test"; 4 | import { parseAndValidate, jsonClone, __dirname } from "./utils.mjs"; 5 | import { readFileSync } from "node:fs"; 6 | 7 | /** @type {import("../dist/index.js").Options} */ 8 | const options = { 9 | revealDetails: true, 10 | }; 11 | 12 | describe("posts test", () => { 13 | it("Works with fragments and aliases", () => { 14 | const errors = parseAndValidate( 15 | readFileSync(`${__dirname}/queries/posts.test.graphql`, "utf8"), 16 | options, 17 | "defaultOptions.1.graphql", 18 | ); 19 | assert.deepEqual(jsonClone(errors), []); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /__tests__/queries/directives-additional.test.graphql: -------------------------------------------------------------------------------- 1 | query TestDirectivesAdditional($true: Boolean = true, $false: Boolean = false) { 2 | allPosts { 3 | edges { 4 | cursor 5 | node { 6 | author: personByAuthorId { 7 | firstPost @include(if: $true) { 8 | id 9 | headline 10 | } 11 | friends @skip(if: $false) { 12 | nodes { 13 | id 14 | name 15 | firstName 16 | } 17 | } 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /__tests__/queries/directives-fragments.test.graphql: -------------------------------------------------------------------------------- 1 | query TestDirectivesFragments($true: Boolean = true, $false: Boolean = false) { 2 | allPosts { 3 | edges { 4 | cursor 5 | node { 6 | author: personByAuthorId { 7 | firstPost @include(if: $true) { 8 | ...PostDetails 9 | } 10 | friends @skip(if: $false) { 11 | nodes { 12 | ...PersonDetails 13 | } 14 | } 15 | } 16 | } 17 | } 18 | } 19 | } 20 | 21 | fragment PersonDetails on Person { 22 | id 23 | name 24 | firstName 25 | } 26 | 27 | fragment PostDetails on Post { 28 | id 29 | headline 30 | headlineTrimmed 31 | author: personByAuthorId { 32 | ...PersonDetails 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /__tests__/queries/directives.test.graphql: -------------------------------------------------------------------------------- 1 | query TestDirectives($true: Boolean = true, $false: Boolean = false) { 2 | a: nodeId @include(if: $true) 3 | b: nodeId @include(if: $false) 4 | c: nodeId @skip(if: $true) 5 | d: nodeId @skip(if: $false) 6 | } 7 | -------------------------------------------------------------------------------- /__tests__/queries/mutation-create.test.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateMutation($config: KeyValueHash!) { 2 | a: createType( 3 | input: { 4 | type: { 5 | id: 201 6 | smallint: 30 7 | bigint: "467131188225" 8 | numeric: "15.2" 9 | decimal: "15.2" 10 | boolean: false 11 | varchar: "abc" 12 | enum: RED 13 | enumArray: [RED, GREEN] 14 | domain: 6 15 | domain2: 5 16 | textArray: [ 17 | "have" 18 | "you" 19 | "ever" 20 | "been" 21 | "down" 22 | "the" 23 | "water" 24 | "spout" 25 | "?" 26 | ] 27 | json: "{\"x\":1,\"y\":2,\"z\":3}" 28 | jsonb: "{\"a\":1,\"b\":2,\"c\":3}" 29 | numrange: { start: { value: "50", inclusive: true } } 30 | daterange: { 31 | start: { value: "1927-11-05", inclusive: false } 32 | end: { value: "1927-11-07", inclusive: false } 33 | } 34 | anIntRange: { end: { value: 500, inclusive: false } } 35 | timestamp: "2016-10-07 16:12:21.747269" 36 | timestamptz: "2016-10-09 16:12:45.218676-04" 37 | date: "2016-10-15" 38 | time: "19:13:18.625699" 39 | timetz: "13:13:29.585176-04" 40 | interval: { 41 | seconds: 1 42 | minutes: 2 43 | hours: 3 44 | days: 4 45 | months: 5 46 | years: 6 47 | } 48 | intervalArray: [ 49 | { seconds: 2, minutes: 3, hours: 4, days: 5, months: 6, years: 7 } 50 | { seconds: 3, minutes: 4, hours: 5, days: 6, months: 7, years: 8 } 51 | ] 52 | money: 1234567.89 53 | compoundType: { 54 | a: 123 55 | b: "abc" 56 | c: GREEN 57 | d: "ec4a9fae-4ec5-4763-98eb-0327fb2dc9bf" 58 | e: FOO_BAR 59 | f: _EMPTY_ 60 | fooBar: 20 61 | } 62 | nestedCompoundType: { 63 | a: { 64 | a: 456 65 | b: "def" 66 | c: BLUE 67 | d: "79863dcf-0433-4c3d-bc51-978326d4546f" 68 | e: BAR_FOO 69 | f: ONE 70 | fooBar: 42 71 | } 72 | b: { 73 | a: 789 74 | b: "ghi" 75 | c: RED 76 | d: "b687ee42-c515-4544-b742-525e39517e7d" 77 | e: BAZ_QUX 78 | f: _EMPTY_ 79 | fooBar: -8 80 | } 81 | bazBuz: 0 82 | } 83 | point: { x: 1, y: 3 } 84 | cidr: "192.168.0.0/16" 85 | macaddr: "0cafec0ffee0" 86 | textArrayDomain: [ 87 | "TEXT 2098288669218571759" 88 | "TEXT 2098288669218571760" 89 | "TEXT 2098288669218571761" 90 | ] 91 | int8ArrayDomain: [ 92 | "2098288669218571759" 93 | "2098288669218571760" 94 | "2098288669218571761" 95 | ] 96 | } 97 | } 98 | ) { 99 | clientMutationId 100 | type { 101 | nodeId 102 | id 103 | smallint 104 | bigint 105 | numeric 106 | decimal 107 | boolean 108 | varchar 109 | enum 110 | enumArray 111 | domain 112 | domain2 113 | textArray 114 | json 115 | jsonb 116 | numrange { 117 | start { 118 | value 119 | inclusive 120 | } 121 | end { 122 | value 123 | inclusive 124 | } 125 | } 126 | daterange { 127 | start { 128 | value 129 | inclusive 130 | } 131 | end { 132 | value 133 | inclusive 134 | } 135 | } 136 | anIntRange { 137 | start { 138 | value 139 | inclusive 140 | } 141 | end { 142 | value 143 | inclusive 144 | } 145 | } 146 | timestamp 147 | timestamptz 148 | date 149 | time 150 | timetz 151 | interval { 152 | seconds 153 | minutes 154 | hours 155 | days 156 | months 157 | years 158 | } 159 | intervalArray { 160 | seconds 161 | minutes 162 | hours 163 | days 164 | months 165 | years 166 | } 167 | money 168 | compoundType { 169 | a 170 | b 171 | c 172 | d 173 | e 174 | f 175 | fooBar 176 | } 177 | nestedCompoundType { 178 | a { 179 | a 180 | b 181 | c 182 | d 183 | e 184 | f 185 | fooBar 186 | } 187 | b { 188 | a 189 | b 190 | c 191 | d 192 | e 193 | f 194 | fooBar 195 | } 196 | bazBuz 197 | } 198 | point { 199 | x 200 | y 201 | } 202 | nullablePoint { 203 | x 204 | y 205 | } 206 | inet 207 | cidr 208 | macaddr 209 | textArrayDomain 210 | int8ArrayDomain 211 | } 212 | query { 213 | nodeId 214 | } 215 | } 216 | b: createPerson( 217 | input: { 218 | person: { 219 | id: 9000 220 | name: "John Smith Jr." 221 | email: "johnny.boy.smith@email.com" 222 | about: "Son of Sara and John Smith." 223 | config: $config 224 | lastLoginFromIp: "172.16.1.2" 225 | lastLoginFromSubnet: "172.16.0.0/12" 226 | userMac: "00:00:00:00:00:00" 227 | } 228 | } 229 | ) { 230 | ...createPersonPayload 231 | } 232 | c: createPerson( 233 | input: { 234 | clientMutationId: "hello" 235 | person: { 236 | id: 20 237 | name: "Best Pal" 238 | email: "best.pal@email.com" 239 | about: "My archnemisis is Budd Deey." 240 | config: null 241 | lastLoginFromIp: "192.168.0.42" 242 | lastLoginFromSubnet: "192.168.0.0/16" 243 | userMac: "0000.0000.0000" 244 | } 245 | } 246 | ) { 247 | ...createPersonPayload 248 | } 249 | d: createCompoundKey( 250 | input: { 251 | clientMutationId: "world" 252 | compoundKey: { personId1: 9000, personId2: 20, extra: false } 253 | } 254 | ) { 255 | clientMutationId 256 | compoundKey { 257 | nodeId 258 | personId1 259 | personId2 260 | extra 261 | personByPersonId1 { 262 | nodeId 263 | name 264 | } 265 | personByPersonId2 { 266 | nodeId 267 | name 268 | } 269 | } 270 | personByPersonId1 { 271 | nodeId 272 | name 273 | } 274 | personByPersonId2 { 275 | nodeId 276 | name 277 | } 278 | query { 279 | nodeId 280 | } 281 | } 282 | e: createEdgeCase(input: { edgeCase: { notNullHasDefault: true } }) { 283 | edgeCase { 284 | notNullHasDefault 285 | } 286 | query { 287 | nodeId 288 | } 289 | } 290 | f: createEdgeCase(input: { edgeCase: {} }) { 291 | edgeCase { 292 | notNullHasDefault 293 | } 294 | query { 295 | nodeId 296 | } 297 | } 298 | g: createPerson( 299 | input: { 300 | person: { 301 | id: 1998 302 | name: "Budd Deey" 303 | email: "budd.deey.the.second@email.com" 304 | about: null 305 | config: { a: 5, b: 6, actually_null: null, null_string: "null" } 306 | lastLoginFromIp: "10.0.1.42" 307 | lastLoginFromSubnet: "10.0.0.0/8" 308 | userMac: "aa-bb-cc-dd-ee-ff" 309 | } 310 | } 311 | ) { 312 | ...createPersonPayload 313 | } 314 | h: createPerson( 315 | input: { 316 | person: { 317 | id: 1999 318 | name: "Twenty Seven" 319 | email: "graphile-build.issue.27@example.com" 320 | about: null 321 | } 322 | } 323 | ) { 324 | person { 325 | issue27UserExists: exists(email: "graphile-build.issue.27@example.com") 326 | } 327 | } 328 | i: createDefaultValue( 329 | input: { defaultValue: { id: 2000, nullValue: null } } 330 | ) { 331 | defaultValue { 332 | id 333 | nullValue 334 | } 335 | } 336 | j: createPost( 337 | input: { 338 | post: { 339 | headline: "super headline" 340 | comptypes: [ 341 | { schedule: "2009-10-24 10:23:54+02", isOptimised: true } 342 | { schedule: "2008-10-24 10:23:54+02", isOptimised: false } 343 | { schedule: "2007-10-24 10:23:54+02", isOptimised: null } 344 | ] 345 | } 346 | } 347 | ) { 348 | post { 349 | id 350 | headline 351 | comptypes { 352 | schedule 353 | isOptimised 354 | } 355 | } 356 | } 357 | k: createPost( 358 | input: { 359 | post: { 360 | authorId: 1 361 | headline: "super headline 2" 362 | comptypes: [ 363 | { schedule: "2008-10-24 10:17:54+02", isOptimised: true } 364 | { schedule: "2007-10-24 10:17:54+02", isOptimised: false } 365 | { schedule: "2006-10-24 10:17:54+02", isOptimised: null } 366 | ] 367 | } 368 | } 369 | ) { 370 | post { 371 | id 372 | headline 373 | comptypes { 374 | schedule 375 | isOptimised 376 | } 377 | personByAuthorId { 378 | id 379 | } 380 | } 381 | p2: post { 382 | personByAuthorId { 383 | email 384 | } 385 | } 386 | postEdge { 387 | node { 388 | id 389 | headline 390 | comptypes { 391 | schedule 392 | isOptimised 393 | } 394 | personByAuthorId { 395 | name 396 | } 397 | } 398 | } 399 | personByAuthorId { 400 | createdAt 401 | } 402 | } 403 | } 404 | 405 | fragment createPersonPayload on CreatePersonPayload { 406 | clientMutationId 407 | person { 408 | nodeId 409 | id 410 | name 411 | email 412 | about 413 | config 414 | lastLoginFromIp 415 | lastLoginFromSubnet 416 | userMac 417 | issue27UserExists: exists(email: "graphile-build.issue.27@example.com") 418 | } 419 | a: personEdge(orderBy: PRIMARY_KEY_ASC) { 420 | ...peopleEdge 421 | } 422 | b: personEdge(orderBy: PRIMARY_KEY_DESC) { 423 | ...peopleEdge 424 | } 425 | c: personEdge(orderBy: ID_ASC) { 426 | ...peopleEdge 427 | } 428 | d: personEdge(orderBy: ID_DESC) { 429 | ...peopleEdge 430 | } 431 | e: personEdge(orderBy: EMAIL_ASC) { 432 | ...peopleEdge 433 | } 434 | f: personEdge(orderBy: EMAIL_DESC) { 435 | ...peopleEdge 436 | } 437 | g: personEdge(orderBy: NATURAL) { 438 | ...peopleEdge 439 | } 440 | h: personEdge(orderBy: [EMAIL_DESC, ID_DESC]) { 441 | ...peopleEdge 442 | } 443 | query { 444 | nodeId 445 | } 446 | } 447 | 448 | fragment peopleEdge on PeopleEdge { 449 | cursor 450 | node { 451 | nodeId 452 | name 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /__tests__/queries/mutation-delete.test.graphql: -------------------------------------------------------------------------------- 1 | mutation { 2 | a: deletePost(input: { nodeId: "WyJwb3N0cyIsMV0=" }) { 3 | ...deletePostPayload 4 | } 5 | b: deletePost( 6 | input: { nodeId: "WyJwb3N0cyIsMl0=", clientMutationId: "hello" } 7 | ) { 8 | ...deletePostPayload 9 | } 10 | c: deletePost(input: { nodeId: "WyJwb3N0cyIsMjAwMDAwMF0=" }) { 11 | ...deletePostPayload 12 | } 13 | d: deletePost( 14 | input: { nodeId: "WyJwb3N0cyIsM10=", clientMutationId: "world" } 15 | ) { 16 | ...deletePostPayload 17 | } 18 | d2: deleteTypeById(input: { id: 11, clientMutationId: "throw error" }) { 19 | clientMutationId 20 | deletedTypeId 21 | } 22 | e: deletePostById(input: { id: 6 }) { 23 | ...deletePostPayload 24 | } 25 | f: deletePostById(input: { id: 9, clientMutationId: "hello" }) { 26 | ...deletePostPayload 27 | } 28 | g: deletePostById(input: { id: 2000000 }) { 29 | ...deletePostPayload 30 | } 31 | h: deletePostById(input: { id: 11, clientMutationId: "world" }) { 32 | ...deletePostPayload 33 | } 34 | i: deleteCompoundKey(input: { nodeId: "WyJjb21wb3VuZF9rZXlzIiw0LDNd" }) { 35 | ...deleteCompoundKeyPayload 36 | } 37 | j: deleteCompoundKeyByPersonId1AndPersonId2( 38 | input: { personId1: 2, personId2: 3 } 39 | ) { 40 | ...deleteCompoundKeyPayload 41 | } 42 | k: deletePersonByEmail(input: { email: "budd.deey@email.com" }) { 43 | ...deletePersonPayload 44 | } 45 | l: deletePersonByEmail( 46 | input: { email: "graphile-build.issue.27.exists@example.com" } 47 | ) { 48 | ...deletePersonPayload2 49 | } 50 | m: deletePersonById(input: { id: 1 }) { 51 | ...deletePersonPayloadWithEdge 52 | } 53 | } 54 | 55 | fragment deletePostPayload on DeletePostPayload { 56 | clientMutationId 57 | deletedPostId 58 | post { 59 | nodeId 60 | id 61 | headline 62 | authorId 63 | } 64 | query { 65 | nodeId 66 | } 67 | } 68 | 69 | fragment deleteCompoundKeyPayload on DeleteCompoundKeyPayload { 70 | clientMutationId 71 | deletedCompoundKeyId 72 | compoundKey { 73 | nodeId 74 | personId1 75 | personId2 76 | personByPersonId1 { 77 | nodeId 78 | name 79 | } 80 | personByPersonId2 { 81 | nodeId 82 | name 83 | } 84 | } 85 | query { 86 | nodeId 87 | } 88 | } 89 | 90 | fragment deletePersonPayload on DeletePersonPayload { 91 | clientMutationId 92 | deletedPersonId 93 | query { 94 | nodeId 95 | } 96 | } 97 | fragment deletePersonPayload2 on DeletePersonPayload { 98 | clientMutationId 99 | deletedPersonId 100 | person { 101 | nodeId 102 | id 103 | name 104 | email 105 | issue27UserExists: exists( 106 | email: "graphile-build.issue.27.exists@example.com" 107 | ) 108 | } 109 | query { 110 | nodeId 111 | } 112 | } 113 | 114 | fragment deletePersonPayloadWithEdge on DeletePersonPayload { 115 | clientMutationId 116 | deletedPersonId 117 | personEdge(orderBy: FIRST_NAME_ASC) { 118 | cursor 119 | node { 120 | firstName 121 | id 122 | nodeId 123 | email 124 | } 125 | } 126 | query { 127 | nodeId 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /__tests__/queries/mutation-return-types.test.graphql: -------------------------------------------------------------------------------- 1 | mutation { 2 | mutationInInout(input: { i: 10, ino: 5 }) { 3 | ino 4 | } 5 | mutationInOut(input: { i: 10 }) { 6 | o 7 | } 8 | mutationOut(input: {}) { 9 | o 10 | } 11 | mutationOutComplex(input: { a: 1, b: "test" }) { 12 | result { 13 | x 14 | y { 15 | a 16 | b 17 | c 18 | } 19 | z { 20 | nodeId 21 | id 22 | name 23 | postsByAuthorId { 24 | nodes { 25 | nodeId 26 | id 27 | } 28 | } 29 | } 30 | } 31 | } 32 | mutationOutComplexSetof(input: { a: 1, b: "test" }) { 33 | results { 34 | x 35 | y { 36 | a 37 | b 38 | c 39 | } 40 | z { 41 | nodeId 42 | id 43 | name 44 | postsByAuthorId { 45 | nodes { 46 | nodeId 47 | id 48 | } 49 | } 50 | } 51 | } 52 | } 53 | mutationOutOut(input: {}) { 54 | result { 55 | firstOut 56 | secondOut 57 | } 58 | } 59 | mutationOutOutCompoundType(input: { i1: 10 }) { 60 | result { 61 | o1 62 | o2 { 63 | a 64 | b 65 | c 66 | } 67 | } 68 | } 69 | mutationOutOutSetof(input: {}) { 70 | results { 71 | o1 72 | o2 73 | } 74 | } 75 | mutationOutOutUnnamed(input: {}) { 76 | result { 77 | arg1 78 | arg2 79 | } 80 | } 81 | mutationOutSetof(input: {}) { 82 | os 83 | } 84 | mutationOutTable(input: {}) { 85 | person { 86 | nodeId 87 | id 88 | } 89 | } 90 | mutationOutTableSetof(input: {}) { 91 | people { 92 | nodeId 93 | id 94 | } 95 | } 96 | mutationOutUnnamed(input: {}) { 97 | integer 98 | } 99 | mutationOutUnnamedOutOutUnnamed(input: {}) { 100 | result { 101 | arg1 102 | arg3 103 | o2 104 | } 105 | } 106 | mutationReturnsTableMultiCol(input: { i: 20 }) { 107 | results { 108 | col1 109 | col2 110 | } 111 | } 112 | mutationReturnsTableOneCol(input: { i: 20 }) { 113 | col1S 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /__tests__/queries/mutation-update.test.graphql: -------------------------------------------------------------------------------- 1 | mutation { 2 | a: updatePerson( 3 | input: { 4 | nodeId: "WyJwZW9wbGUiLDFd" 5 | personPatch: { name: "John Smith Sr.", about: "An older John Smith" } 6 | } 7 | ) { 8 | ...updatePersonPayload 9 | } 10 | b: updatePerson( 11 | input: { 12 | clientMutationId: "hello" 13 | nodeId: "WyJwZW9wbGUiLDJd" 14 | personPatch: { name: "Sarah Smith", email: "sarah.smith@email.com" } 15 | } 16 | ) { 17 | ...updatePersonPayload 18 | } 19 | c: updatePerson( 20 | input: { 21 | clientMutationId: "world" 22 | nodeId: "WyJwZW9wbGUiLDJd" 23 | personPatch: { about: "Now with an “H.”" } 24 | } 25 | ) { 26 | ...updatePersonPayload 27 | } 28 | i: updatePerson( 29 | input: { nodeId: "WyJwZW9wbGUiLDJd", personPatch: { about: null } } 30 | ) { 31 | ...updatePersonPayload 32 | } 33 | d: updatePersonById( 34 | input: { 35 | id: 3 36 | personPatch: { 37 | name: "Best Pal" 38 | about: "I have taken over Budd’s account. Hehehe." 39 | } 40 | } 41 | ) { 42 | ...updatePersonPayload 43 | } 44 | e: updatePersonByEmail( 45 | input: { 46 | email: "kathryn.ramirez@email.com" 47 | personPatch: { about: "Make art not friends." } 48 | } 49 | ) { 50 | ...updatePersonPayload 51 | } 52 | f: updateCompoundKey( 53 | input: { 54 | nodeId: "WyJjb21wb3VuZF9rZXlzIiwxLDJd" 55 | compoundKeyPatch: { personId1: 2, extra: true } 56 | } 57 | ) { 58 | ...updateCompoundKeyPayload 59 | } 60 | g: updateCompoundKeyByPersonId1AndPersonId2( 61 | input: { 62 | clientMutationId: "hello" 63 | personId1: 2 64 | personId2: 2 65 | compoundKeyPatch: { personId1: 3, extra: false } 66 | } 67 | ) { 68 | ...updateCompoundKeyPayload 69 | } 70 | h: updateCompoundKeyByPersonId1AndPersonId2( 71 | input: { 72 | clientMutationId: "world" 73 | personId1: 4 74 | personId2: 3 75 | compoundKeyPatch: { extra: false } 76 | } 77 | ) { 78 | ...updateCompoundKeyPayload 79 | } 80 | j: updatePersonByEmail( 81 | input: { 82 | email: "graphile-build.issue.27.exists@example.com" 83 | personPatch: { email: "somethingelse@example.com" } 84 | } 85 | ) { 86 | ...updatePersonPayload 87 | } 88 | k: updateDefaultValueById( 89 | input: { id: 1, defaultValuePatch: { nullValue: null } } 90 | ) { 91 | defaultValue { 92 | id 93 | nullValue 94 | } 95 | } 96 | l: updateNoPrimaryKeyById( 97 | input: { id: 1, noPrimaryKeyPatch: { str: "New String" } } 98 | ) { 99 | noPrimaryKey { 100 | id 101 | str 102 | } 103 | } 104 | } 105 | 106 | fragment updatePersonPayload on UpdatePersonPayload { 107 | clientMutationId 108 | person { 109 | nodeId 110 | id 111 | name 112 | email 113 | about 114 | issue27UserExists: exists( 115 | email: "graphile-build.issue.27.exists@example.com" 116 | ) 117 | } 118 | personEdge { 119 | cursor 120 | node { 121 | nodeId 122 | id 123 | } 124 | } 125 | query { 126 | nodeId 127 | } 128 | } 129 | 130 | fragment updateCompoundKeyPayload on UpdateCompoundKeyPayload { 131 | clientMutationId 132 | compoundKey { 133 | nodeId 134 | personId1 135 | personId2 136 | extra 137 | personByPersonId1 { 138 | id 139 | name 140 | } 141 | personByPersonId2 { 142 | id 143 | name 144 | } 145 | } 146 | query { 147 | nodeId 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /__tests__/queries/posts.test.graphql: -------------------------------------------------------------------------------- 1 | { 2 | allPosts { 3 | edges { 4 | cursor 5 | node { 6 | ...PostDetails 7 | author: personByAuthorId { 8 | firstPost { 9 | ...PostDetails 10 | } 11 | friends { 12 | nodes { 13 | ...PersonDetails 14 | } 15 | totalCount 16 | pageInfo { 17 | startCursor 18 | } 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | 26 | fragment PersonDetails on Person { 27 | id 28 | name 29 | firstName 30 | } 31 | 32 | fragment PostDetails on Post { 33 | id 34 | headline 35 | headlineTrimmed 36 | author: personByAuthorId { 37 | ...PersonDetails 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /__tests__/simple.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | currentUser: User 3 | } 4 | type User { 5 | name: String 6 | "Same object again" 7 | self: User 8 | otherSelf: User 9 | friends: [User!]! 10 | friendsConnection: UserConnection! 11 | } 12 | type UserConnection { 13 | edges: [FriendEdge]! 14 | pageInfo: PageInfo 15 | } 16 | type PageInfo { 17 | hasNextPage: Boolean 18 | } 19 | type FriendEdge { 20 | cursor: String! 21 | node: User 22 | } 23 | -------------------------------------------------------------------------------- /__tests__/utils.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { readFileSync } from "node:fs"; 3 | import { dirname } from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | import { buildSchema, parse, validate } from "graphql"; 6 | import { depthLimit } from "../dist/index.js"; 7 | 8 | export const __dirname = dirname(fileURLToPath(import.meta.url)); 9 | 10 | /** @type {Map} */ 11 | const schemaByFilename = new Map(); 12 | export function getSchema(schemaFilename = "simple.graphql") { 13 | if (schemaByFilename.has(schemaFilename)) { 14 | return schemaByFilename.get(schemaFilename); 15 | } 16 | const schemaText = readFileSync(`${__dirname}/${schemaFilename}`, "utf8"); 17 | const schema = buildSchema(schemaText); 18 | schemaByFilename.set(schemaFilename, schema); 19 | return schema; 20 | } 21 | 22 | /** 23 | * @param {string} operationText 24 | * @param {import("../dist/index.js").Options} [options] 25 | * @param {string} [schemaFilename] 26 | * @param {boolean} [allowInvalidQuery] 27 | */ 28 | export function parseAndValidate( 29 | operationText, 30 | options, 31 | schemaFilename, 32 | allowInvalidQuery = false, 33 | ) { 34 | const schema = getSchema(schemaFilename); 35 | const document = parse(operationText); 36 | const rule = depthLimit(options); 37 | if (!allowInvalidQuery) { 38 | const regularErrors = validate(schema, document); 39 | if (regularErrors.length) { 40 | throw new Error( 41 | `Query was invalid, either pass \`true\` to \`parseAndValidate\` to allow invalid queries, or fix the query. Errors:\n${regularErrors.join("\n")}`, 42 | ); 43 | } 44 | } 45 | const errors = validate(schema, document, [rule]); 46 | return errors; 47 | } 48 | 49 | /** 50 | * @param {any} obj 51 | */ 52 | export function jsonClone(obj) { 53 | return JSON.parse(JSON.stringify(obj)); 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@graphile/depth-limit", 3 | "version": "0.3.1", 4 | "description": "A validation rule to apply to GraphQL operations to avoid excessively deep queries.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "prepack": "tsc", 8 | "test": "tsc && node --test", 9 | "lint": "yarn run eslint . && yarn prettier:check", 10 | "lint:fix": "yarn run eslint --fix . && yarn prettier:fix", 11 | "eslint": "eslint --ext .js,.jsx,.ts,.tsx", 12 | "prettier": "prettier --cache --ignore-path ./.eslintignore", 13 | "prettier:all": "yarn prettier '**/*.{json,md,mdx,html,js,jsx,ts,tsx,yml}'", 14 | "prettier:fix": "yarn prettier:all --write", 15 | "prettier:check": "yarn prettier:all --list-different" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https+git://github.com/graphile/depth-limit" 20 | }, 21 | "keywords": [ 22 | "graphql", 23 | "depth", 24 | "limit", 25 | "arrays", 26 | "lists", 27 | "list", 28 | "array", 29 | "graphile", 30 | "postgraphile", 31 | "pro", 32 | "security", 33 | "introspection" 34 | ], 35 | "author": "Benjie Gillam ", 36 | "license": "MIT", 37 | "peerDependencies": { 38 | "graphql": ">=15 <17" 39 | }, 40 | "devDependencies": { 41 | "@envelop/core": "^5.0.0", 42 | "@tsconfig/node18": "^18.2.4", 43 | "@types/node": "^18.0.0", 44 | "@typescript-eslint/eslint-plugin": "^7.7.0", 45 | "@typescript-eslint/parser": "^7.7.0", 46 | "@typescript-eslint/typescript-estree": "^7.7.0", 47 | "eslint": "^8.48.0", 48 | "eslint-config-prettier": "^9.1.0", 49 | "eslint-plugin-import": "^2.28.1", 50 | "eslint-plugin-simple-import-sort": "^12.1.0", 51 | "eslint-plugin-tsdoc": "^0.2.17", 52 | "graphql": "^15.8.0", 53 | "prettier": "^3.2.5", 54 | "typescript": "^5.4.5" 55 | }, 56 | "prettier": { 57 | "proseWrap": "always" 58 | }, 59 | "files": [ 60 | "dist" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /src/envelop.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin } from "@envelop/core"; 2 | 3 | import { depthLimit } from "./index.js"; 4 | import type { Options } from "./interfaces.js"; 5 | 6 | export function useDepthLimit(options: Options): Plugin { 7 | const rule = depthLimit(options); 8 | return { 9 | onValidate({ addValidationRule }) { 10 | addValidationRule(rule); 11 | }, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | FieldNode, 3 | FragmentDefinitionNode, 4 | FragmentSpreadNode, 5 | GraphQLNamedType, 6 | GraphQLObjectType, 7 | GraphQLSchema, 8 | GraphQLType, 9 | InlineFragmentNode, 10 | OperationDefinitionNode, 11 | Source, 12 | ValidationRule, 13 | } from "graphql"; 14 | import { 15 | buildSchema, 16 | getNamedType, 17 | GraphQLError, 18 | isListType, 19 | isNonNullType, 20 | isObjectType, 21 | Kind, 22 | parse, 23 | SchemaMetaFieldDef, 24 | TypeMetaFieldDef, 25 | TypeNameMetaFieldDef, 26 | } from "graphql"; 27 | 28 | import type { DepthByCoordinate } from "./interfaces.js"; 29 | import { 30 | DEPTH, 31 | INTROSPECTION_DEPTH, 32 | INTROSPECTION_LIST_DEPTH, 33 | LIST_DEPTH, 34 | Options, 35 | } from "./interfaces.js"; 36 | 37 | export { useDepthLimit } from "./envelop.js"; 38 | 39 | export { DepthByCoordinate, Options }; 40 | 41 | /** 42 | * Returns a GraphQL validation rule that can be used to limit the depth of a 43 | * GraphQL operation based on a number of settings. 44 | */ 45 | export function depthLimit(options: Options = {}): ValidationRule { 46 | return function (context) { 47 | const schema = context.getSchema(); 48 | const depthByFragment = new Map>(); 49 | return { 50 | // TODO: refactor this to use the existing visitors rather than recursing ourselves 51 | OperationDefinition: { 52 | enter(operation) { 53 | const operationName = operation.name?.value ?? "(anonymous)"; 54 | const resolvedOptions = resolveOptions(options); 55 | const ctx: CountDepthInternalContext = { 56 | schema, 57 | fragments: fragmentsToMap( 58 | context.getRecursivelyReferencedFragments(operation), 59 | ), 60 | options: resolvedOptions, 61 | depthByFragment, 62 | }; 63 | // TODO: is this equivalent to context.getType()? 64 | const operationType = getOperationType(schema, operation); 65 | if (!operationType) { 66 | return; 67 | } 68 | const depths = countDepthInternal( 69 | ctx, 70 | operationType, 71 | operation, 72 | [], 73 | false, 74 | ); 75 | const { 76 | maxDepthByFieldCoordinates, 77 | revealDetails, 78 | maxSelfReferentialDepth, 79 | maxIntrospectionSelfReferentialDepth, 80 | } = resolvedOptions; 81 | const issues: string[] = []; 82 | for (const coordinate of Object.keys(depths)) { 83 | const fallbackMaxScore = coordinate.includes(".") 84 | ? coordinate.startsWith("__") 85 | ? maxIntrospectionSelfReferentialDepth 86 | : maxSelfReferentialDepth 87 | : undefined; 88 | 89 | const score = depths[coordinate]!; 90 | const maxScore = 91 | maxDepthByFieldCoordinates[coordinate] ?? fallbackMaxScore; 92 | const isSelfReferential = 93 | maxDepthByFieldCoordinates[coordinate] === undefined; 94 | if (maxScore !== undefined && score > maxScore) { 95 | if (coordinate.includes(".")) { 96 | issues.push( 97 | `field ${coordinate} nested ${score} times which exceeds ${isSelfReferential ? "self referential " : ""}maximum of ${maxScore}`, 98 | ); 99 | } else { 100 | switch (coordinate) { 101 | case DEPTH: { 102 | issues.push( 103 | `operation depth ${score} exceeds maximum of ${maxScore}`, 104 | ); 105 | break; 106 | } 107 | case INTROSPECTION_DEPTH: { 108 | issues.push( 109 | `operation introspection depth ${score} exceeds maximum of ${maxScore}`, 110 | ); 111 | break; 112 | } 113 | case LIST_DEPTH: { 114 | issues.push( 115 | `operation list depth ${score} exceeds maximum of ${maxScore}`, 116 | ); 117 | break; 118 | } 119 | case INTROSPECTION_LIST_DEPTH: { 120 | issues.push( 121 | `operation introspection list depth ${score} exceeds maximum of ${maxScore}`, 122 | ); 123 | break; 124 | } 125 | default: { 126 | issues.push( 127 | `[internal error] coordinate '${coordinate}' not understood`, 128 | ); 129 | } 130 | } 131 | } 132 | } 133 | } 134 | if (issues.length > 0) { 135 | // TODO: if revealDetails is true, we should indicate the nodes 136 | // where the limit was exceeded. We could do so by building a 137 | // "stack" of nodes adding to the limit, rather than incrementing 138 | // a counter, and thus we can find the nth position in the stack. 139 | return context.reportError( 140 | new GraphQLError( 141 | `'${operationName}' exceeds operation depth limits${revealDetails ? `: ${issues.join(", ")}` : ""}.`, 142 | [operation], 143 | ), 144 | ); 145 | } 146 | }, 147 | }, 148 | }; 149 | }; 150 | } 151 | 152 | function resolveOptions(options: Options = {}): Required { 153 | const { 154 | maxDepth = 12, 155 | maxListDepth = 4, 156 | maxSelfReferentialDepth = 2, 157 | maxIntrospectionDepth = 12, 158 | maxIntrospectionListDepth = 3, 159 | maxIntrospectionSelfReferentialDepth = 2, 160 | maxDepthByFieldCoordinates: userSpecifiedMaxDepthByFieldCoordinates, 161 | revealDetails = false, 162 | fragmentsAddToDepth = false, 163 | } = options; 164 | const maxDepthByFieldCoordinates: DepthByCoordinate = Object.assign( 165 | Object.create(null), 166 | { 167 | "Query.__schema": 1, 168 | "Query.__type": 1, 169 | "__Type.fields": 1, 170 | "__Type.inputFields": 1, 171 | "__Type.interfaces": 1, 172 | "__Type.ofType": 8, 173 | "__Type.possibleTypes": 1, 174 | "__Field.args": 1, 175 | "__Field.type": 1, 176 | ...userSpecifiedMaxDepthByFieldCoordinates, 177 | [DEPTH]: maxDepth, 178 | [INTROSPECTION_DEPTH]: maxIntrospectionDepth, 179 | [LIST_DEPTH]: maxListDepth, 180 | [INTROSPECTION_LIST_DEPTH]: maxIntrospectionListDepth, 181 | }, 182 | ); 183 | const resolvedOptions: Required = { 184 | maxDepth, 185 | maxListDepth, 186 | maxSelfReferentialDepth, 187 | maxIntrospectionDepth, 188 | maxIntrospectionListDepth, 189 | maxIntrospectionSelfReferentialDepth, 190 | maxDepthByFieldCoordinates: maxDepthByFieldCoordinates as Record< 191 | string, 192 | number 193 | >, 194 | revealDetails, 195 | fragmentsAddToDepth, 196 | }; 197 | return resolvedOptions; 198 | } 199 | 200 | function getOperationType( 201 | schema: GraphQLSchema, 202 | operation: OperationDefinitionNode, 203 | ): GraphQLObjectType | null | undefined { 204 | return operation.operation === "query" 205 | ? schema.getQueryType() 206 | : operation.operation === "mutation" 207 | ? schema.getMutationType() 208 | : operation.operation === "subscription" 209 | ? schema.getSubscriptionType() 210 | : null; 211 | } 212 | 213 | function fragmentsToMap(fragments: readonly FragmentDefinitionNode[]) { 214 | const map = new Map(); 215 | for (const def of fragments) { 216 | map.set(def.name.value, def); 217 | } 218 | return map; 219 | } 220 | 221 | export function countDepth( 222 | schema: GraphQLSchema, 223 | operation: OperationDefinitionNode, 224 | fragments: readonly FragmentDefinitionNode[], 225 | options: Options = {}, 226 | ): { 227 | depths: Readonly; 228 | resolvedOptions: Required; 229 | } { 230 | const resolvedOptions = resolveOptions(options); 231 | const operationType = getOperationType(schema, operation); 232 | if (!operationType) { 233 | throw new Error(`Could not determine root type for operation`); 234 | } 235 | const fragmentMap = fragmentsToMap(fragments); 236 | const ctx: CountDepthInternalContext = { 237 | schema, 238 | fragments: { 239 | get(name) { 240 | const frag = fragmentMap.get(name); 241 | if (!frag) { 242 | throw new Error(`Fragment '${name}' not found!`); 243 | } 244 | return frag; 245 | }, 246 | }, 247 | options: resolvedOptions, 248 | depthByFragment: new Map(), 249 | }; 250 | const depths = countDepthInternal(ctx, operationType, operation, [], false); 251 | return { depths, resolvedOptions }; 252 | } 253 | 254 | interface CountDepthInternalContext { 255 | schema: GraphQLSchema; 256 | fragments: Pick, "get">; 257 | options: Required; 258 | depthByFragment: Map>; 259 | } 260 | 261 | function countDepthInternal( 262 | ctx: CountDepthInternalContext, 263 | currentType: GraphQLNamedType, 264 | node: 265 | | OperationDefinitionNode 266 | | FieldNode 267 | | InlineFragmentNode 268 | | FragmentSpreadNode 269 | | FragmentDefinitionNode, 270 | visitedFragments: ReadonlyArray, 271 | isIntrospection: boolean, 272 | ): Readonly { 273 | const { 274 | schema, 275 | fragments, 276 | options: { fragmentsAddToDepth }, 277 | depthByFragment, 278 | } = ctx; 279 | switch (node.kind) { 280 | case Kind.OPERATION_DEFINITION: 281 | case Kind.FIELD: 282 | case Kind.INLINE_FRAGMENT: 283 | case Kind.FRAGMENT_DEFINITION: { 284 | const currentState: DepthByCoordinate = Object.create(null); 285 | const currentFieldCoord = 286 | node.kind === "Field" ? `${currentType.name}.${node.name.value}` : null; 287 | if (currentFieldCoord) { 288 | incr(currentState, currentFieldCoord, 1); 289 | } 290 | 291 | // Fields don't always have a selection set 292 | if (node.selectionSet) { 293 | if (fragmentsAddToDepth || node.kind === "Field") { 294 | incr(currentState, isIntrospection ? INTROSPECTION_DEPTH : DEPTH, 1); 295 | } 296 | const type = (() => { 297 | switch (node.kind) { 298 | case Kind.OPERATION_DEFINITION: { 299 | return currentType; 300 | } 301 | case Kind.FIELD: { 302 | if (isObjectType(currentType)) { 303 | const fieldName = node.name.value; 304 | const field = 305 | fieldName === "__type" 306 | ? TypeMetaFieldDef 307 | : fieldName === "__schema" 308 | ? SchemaMetaFieldDef 309 | : fieldName === "__typename" 310 | ? TypeNameMetaFieldDef 311 | : currentType.getFields()[node.name.value]; 312 | if (field) { 313 | incr( 314 | currentState, 315 | isIntrospection ? INTROSPECTION_LIST_DEPTH : LIST_DEPTH, 316 | listDepth(field.type), 317 | ); 318 | return getNamedType(field.type); 319 | } else { 320 | return null; 321 | } 322 | } 323 | break; 324 | } 325 | case Kind.INLINE_FRAGMENT: { 326 | if (node.typeCondition) { 327 | return schema.getType(node.typeCondition.name.value); 328 | } else { 329 | return currentType; 330 | } 331 | } 332 | case Kind.FRAGMENT_DEFINITION: { 333 | return schema.getType(node.typeCondition.name.value); 334 | } 335 | default: { 336 | const never: never = node; 337 | throw new Error(`Unhandled node kind ${(never as any).kind}`); 338 | } 339 | } 340 | })(); 341 | if (type) { 342 | const baseDepth = currentState[DEPTH] ?? 0; 343 | const baseIntrospectionDepth = currentState[INTROSPECTION_DEPTH] ?? 0; 344 | const baseListDepth = currentState[LIST_DEPTH] ?? 0; 345 | const baseIntrospectionListDepth = 346 | currentState[INTROSPECTION_LIST_DEPTH] ?? 0; 347 | const baseCurrentFieldDepth = currentFieldCoord 348 | ? currentState[currentFieldCoord] ?? 0 349 | : 0; 350 | for (const child of node.selectionSet.selections) { 351 | const isIntrospectionField = 352 | child.kind === Kind.FIELD && child.name.value.startsWith("__"); 353 | const innerDepth = countDepthInternal( 354 | ctx, 355 | type, 356 | child, 357 | visitedFragments, 358 | // Once you go introspection, you can never go back 359 | isIntrospection || isIntrospectionField, 360 | ); 361 | const fieldCoord = 362 | child.kind === Kind.FIELD 363 | ? `${type.name}.${child.name.value}` 364 | : null; 365 | if (fieldCoord && !currentState[fieldCoord]) { 366 | currentState[fieldCoord] = 1; 367 | } 368 | for (const coord of Object.keys(innerDepth)) { 369 | const score = 370 | (innerDepth[coord] ?? 0) + 371 | // Fields automatically add to depth 372 | (coord === currentFieldCoord 373 | ? baseCurrentFieldDepth 374 | : coord === DEPTH 375 | ? baseDepth 376 | : coord === INTROSPECTION_DEPTH 377 | ? baseIntrospectionDepth 378 | : coord === LIST_DEPTH 379 | ? baseListDepth 380 | : coord === INTROSPECTION_LIST_DEPTH 381 | ? baseIntrospectionListDepth 382 | : 0); 383 | // Only overwrite value if new score is higher 384 | if ( 385 | currentState[coord] === undefined || 386 | currentState[coord]! < score 387 | ) { 388 | currentState[coord] = score; 389 | } 390 | } 391 | } 392 | } 393 | } 394 | return currentState; 395 | } 396 | case Kind.FRAGMENT_SPREAD: { 397 | const fragmentName = node.name.value; 398 | const existing = depthByFragment.get(fragmentName); 399 | if (existing) { 400 | return existing; 401 | } 402 | if (visitedFragments.includes(fragmentName)) { 403 | // Recursion detected; this should be handled by a different 404 | // validation rule. 405 | return {}; 406 | } 407 | const fragment = fragments.get(fragmentName); 408 | if (!fragment) { 409 | // Non-existant fragment detected; this should be handled by a 410 | // different validation rule. 411 | return {}; 412 | } 413 | const fragmentDepth = countDepthInternal( 414 | ctx, 415 | currentType, 416 | fragment, 417 | [...visitedFragments, fragmentName], 418 | isIntrospection, 419 | ); 420 | depthByFragment.set(fragmentName, fragmentDepth); 421 | return fragmentDepth; 422 | } 423 | default: { 424 | const never: never = node; 425 | throw new Error(`Unexpected node type ${(never as any).kind}`); 426 | } 427 | } 428 | } 429 | 430 | function incr( 431 | mutableState: DepthByCoordinate, 432 | coordinate: string, 433 | by = 1, 434 | ): void { 435 | if (mutableState[coordinate] !== undefined) { 436 | mutableState[coordinate]! += by; 437 | } else { 438 | mutableState[coordinate] = by; 439 | } 440 | } 441 | 442 | function listDepth(type: GraphQLType) { 443 | let d = 0; 444 | let t = type; 445 | do { 446 | if (isListType(t)) { 447 | d++; 448 | t = t.ofType; 449 | } else if (isNonNullType(t)) { 450 | t = t.ofType; 451 | } else { 452 | break; 453 | } 454 | } while (t && d < 100); 455 | return d; 456 | } 457 | 458 | export function countOperationDepths( 459 | schema: string | GraphQLSchema, 460 | source: string | Source, 461 | options: Options = {}, 462 | ) { 463 | const actualSchema = 464 | typeof schema === "string" ? buildSchema(schema) : schema; 465 | const document = parse(source); 466 | const operations = document.definitions.filter( 467 | (d): d is OperationDefinitionNode => d.kind === Kind.OPERATION_DEFINITION, 468 | ); 469 | const fragments = document.definitions.filter( 470 | (d): d is FragmentDefinitionNode => d.kind === Kind.FRAGMENT_DEFINITION, 471 | ); 472 | const result: { 473 | [operationName: string]: Readonly; 474 | } = Object.create(null); 475 | for (const operation of operations) { 476 | const operationName = operation.name?.value ?? ""; 477 | const { depths } = countDepth(actualSchema, operation, fragments, options); 478 | result[operationName] = depths; 479 | } 480 | return result; 481 | } 482 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export const DEPTH = "$$depth"; 2 | export const INTROSPECTION_DEPTH = "$$introspectionDepth"; 3 | export const LIST_DEPTH = "$$listDepth"; 4 | export const INTROSPECTION_LIST_DEPTH = "$$introspectionListDepth"; 5 | 6 | export interface DepthByCoordinate { 7 | [DEPTH]?: number; 8 | [INTROSPECTION_DEPTH]?: number; 9 | [LIST_DEPTH]?: number; 10 | [INTROSPECTION_LIST_DEPTH]?: number; 11 | "Query.__schema"?: number; 12 | "Query.__type"?: number; 13 | "__Type.fields"?: number; 14 | "__Type.inputFields"?: number; 15 | "__Type.interfaces"?: number; 16 | "__Type.ofType"?: number; 17 | "__Type.possibleTypes"?: number; 18 | "__Field.args"?: number; 19 | "__Field.type"?: number; 20 | [coordinate: string]: number | undefined; 21 | } 22 | 23 | export type Options = { 24 | /** 25 | * How many selection sets deep may the user query? 26 | * 27 | * @defaultValue `12` 28 | */ 29 | maxDepth?: number; 30 | 31 | /** 32 | * How many nested lists deep may the user query? 33 | * 34 | * @defaultValue `4` 35 | */ 36 | maxListDepth?: number; 37 | 38 | /** 39 | * How many times may a field reference itself recursively by default. This 40 | * is to try and block the most common forms of attack automatically, if you 41 | * have legitimate cases where a field should be referenced recursively then 42 | * you may specifically override those via `maxDepthByFieldCoordinates`. 43 | * 44 | * @defaultValue `2` 45 | */ 46 | maxSelfReferentialDepth?: number; 47 | 48 | /** 49 | * How many selection sets deep may the user query in introspection? 50 | * 51 | * @defaultValue `12` 52 | */ 53 | maxIntrospectionDepth?: number; 54 | 55 | /** 56 | * How many nested lists deep may the user query in introspection? 57 | * 58 | * @defaultValue `3` 59 | */ 60 | maxIntrospectionListDepth?: number; 61 | 62 | /** 63 | * How many times may an introspection field reference itself recursively by 64 | * default. This is to try and block the most common forms of attack 65 | * automatically, if you have legitimate cases where a field should be 66 | * referenced recursively then you may specifically override those via 67 | * `maxDepthByFieldCoordinates`. 68 | * 69 | * @defaultValue `2` 70 | */ 71 | maxIntrospectionSelfReferentialDepth?: number; 72 | 73 | /** 74 | * Set `true` if you want fragment spreads to add to the depth. Not 75 | * recommended; fragments are essential to using GraphQL correctly so using 76 | * them should not have a penalty. 77 | * 78 | * Whether this setting is true or false, the fields referenced by the 79 | * fragment will still of course weigh into the depth calculations. 80 | * 81 | * @defaultValue `false` 82 | */ 83 | fragmentsAddToDepth?: boolean; 84 | 85 | /** 86 | * Limits the number of times a particular field coordinate can be nested 87 | * inside itself; for example: 88 | * 89 | * ``` 90 | * maxDepthByFieldCoordinates: { 91 | * 'User.friends': 2, 92 | * } 93 | * ``` 94 | * 95 | * Would allow you to load a users friends, or their friends of friends, but 96 | * not their friends of friends of friends. 97 | */ 98 | maxDepthByFieldCoordinates?: Record; 99 | 100 | /** 101 | * If true, informs the user what the issues are. Setting this to true could 102 | * have security ramifications as it will make it easier for an attacker to 103 | * determine your limits (however, this could be derived with minimal effort 104 | * also, so is potentially not a big deal). 105 | * 106 | * @defaultValue `false` 107 | */ 108 | revealDetails?: boolean; 109 | }; 110 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "rootDir": "src", 6 | "declarationDir": "dist", 7 | "outDir": "dist" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aashutoshrathi/word-wrap@^1.2.3": 6 | version "1.2.6" 7 | resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" 8 | integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== 9 | 10 | "@envelop/core@^5.0.0": 11 | version "5.0.0" 12 | resolved "https://registry.yarnpkg.com/@envelop/core/-/core-5.0.0.tgz#131616ad9685599e501b14fdf1a983cfd3b72020" 13 | integrity sha512-aJdnH/ptv+cvwfvciCBe7TSvccBwo9g0S5f6u35TBVzRVqIGkK03lFlIL+x1cnfZgN9EfR2b1PH2galrT1CdCQ== 14 | dependencies: 15 | "@envelop/types" "5.0.0" 16 | tslib "^2.5.0" 17 | 18 | "@envelop/types@5.0.0": 19 | version "5.0.0" 20 | resolved "https://registry.yarnpkg.com/@envelop/types/-/types-5.0.0.tgz#3ae59b50ec31d4bdcc7bd0b47e9c8cf2ac44b0ff" 21 | integrity sha512-IPjmgSc4KpQRlO4qbEDnBEixvtb06WDmjKfi/7fkZaryh5HuOmTtixe1EupQI5XfXO8joc3d27uUZ0QdC++euA== 22 | dependencies: 23 | tslib "^2.5.0" 24 | 25 | "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": 26 | version "4.4.0" 27 | resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" 28 | integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== 29 | dependencies: 30 | eslint-visitor-keys "^3.3.0" 31 | 32 | "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": 33 | version "4.10.0" 34 | resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" 35 | integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== 36 | 37 | "@eslint/eslintrc@^2.1.4": 38 | version "2.1.4" 39 | resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" 40 | integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== 41 | dependencies: 42 | ajv "^6.12.4" 43 | debug "^4.3.2" 44 | espree "^9.6.0" 45 | globals "^13.19.0" 46 | ignore "^5.2.0" 47 | import-fresh "^3.2.1" 48 | js-yaml "^4.1.0" 49 | minimatch "^3.1.2" 50 | strip-json-comments "^3.1.1" 51 | 52 | "@eslint/js@8.57.0": 53 | version "8.57.0" 54 | resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" 55 | integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== 56 | 57 | "@humanwhocodes/config-array@^0.11.14": 58 | version "0.11.14" 59 | resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" 60 | integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== 61 | dependencies: 62 | "@humanwhocodes/object-schema" "^2.0.2" 63 | debug "^4.3.1" 64 | minimatch "^3.0.5" 65 | 66 | "@humanwhocodes/module-importer@^1.0.1": 67 | version "1.0.1" 68 | resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" 69 | integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== 70 | 71 | "@humanwhocodes/object-schema@^2.0.2": 72 | version "2.0.3" 73 | resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" 74 | integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== 75 | 76 | "@microsoft/tsdoc-config@0.16.2": 77 | version "0.16.2" 78 | resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf" 79 | integrity sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw== 80 | dependencies: 81 | "@microsoft/tsdoc" "0.14.2" 82 | ajv "~6.12.6" 83 | jju "~1.4.0" 84 | resolve "~1.19.0" 85 | 86 | "@microsoft/tsdoc@0.14.2": 87 | version "0.14.2" 88 | resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" 89 | integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== 90 | 91 | "@nodelib/fs.scandir@2.1.5": 92 | version "2.1.5" 93 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" 94 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 95 | dependencies: 96 | "@nodelib/fs.stat" "2.0.5" 97 | run-parallel "^1.1.9" 98 | 99 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 100 | version "2.0.5" 101 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" 102 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 103 | 104 | "@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": 105 | version "1.2.8" 106 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" 107 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 108 | dependencies: 109 | "@nodelib/fs.scandir" "2.1.5" 110 | fastq "^1.6.0" 111 | 112 | "@tsconfig/node18@^18.2.4": 113 | version "18.2.4" 114 | resolved "https://registry.yarnpkg.com/@tsconfig/node18/-/node18-18.2.4.tgz#094efbdd70f697d37c09f34067bf41bc4a828ae3" 115 | integrity sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ== 116 | 117 | "@types/json-schema@^7.0.15": 118 | version "7.0.15" 119 | resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" 120 | integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== 121 | 122 | "@types/json5@^0.0.29": 123 | version "0.0.29" 124 | resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" 125 | integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== 126 | 127 | "@types/node@^18.0.0": 128 | version "18.19.31" 129 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd" 130 | integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA== 131 | dependencies: 132 | undici-types "~5.26.4" 133 | 134 | "@types/semver@^7.5.8": 135 | version "7.5.8" 136 | resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" 137 | integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== 138 | 139 | "@typescript-eslint/eslint-plugin@^7.7.0": 140 | version "7.7.0" 141 | resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz#bf34a02f221811505b8bf2f31060c8560c1bb0a3" 142 | integrity sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ== 143 | dependencies: 144 | "@eslint-community/regexpp" "^4.10.0" 145 | "@typescript-eslint/scope-manager" "7.7.0" 146 | "@typescript-eslint/type-utils" "7.7.0" 147 | "@typescript-eslint/utils" "7.7.0" 148 | "@typescript-eslint/visitor-keys" "7.7.0" 149 | debug "^4.3.4" 150 | graphemer "^1.4.0" 151 | ignore "^5.3.1" 152 | natural-compare "^1.4.0" 153 | semver "^7.6.0" 154 | ts-api-utils "^1.3.0" 155 | 156 | "@typescript-eslint/parser@^7.7.0": 157 | version "7.7.0" 158 | resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.7.0.tgz#6b1b3ce76c5de002c43af8ae933613b0f2b4bcc6" 159 | integrity sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg== 160 | dependencies: 161 | "@typescript-eslint/scope-manager" "7.7.0" 162 | "@typescript-eslint/types" "7.7.0" 163 | "@typescript-eslint/typescript-estree" "7.7.0" 164 | "@typescript-eslint/visitor-keys" "7.7.0" 165 | debug "^4.3.4" 166 | 167 | "@typescript-eslint/scope-manager@7.7.0": 168 | version "7.7.0" 169 | resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz#3f0db079b275bb8b0cb5be7613fb3130cfb5de77" 170 | integrity sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw== 171 | dependencies: 172 | "@typescript-eslint/types" "7.7.0" 173 | "@typescript-eslint/visitor-keys" "7.7.0" 174 | 175 | "@typescript-eslint/type-utils@7.7.0": 176 | version "7.7.0" 177 | resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz#36792ff4209a781b058de61631a48df17bdefbc5" 178 | integrity sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg== 179 | dependencies: 180 | "@typescript-eslint/typescript-estree" "7.7.0" 181 | "@typescript-eslint/utils" "7.7.0" 182 | debug "^4.3.4" 183 | ts-api-utils "^1.3.0" 184 | 185 | "@typescript-eslint/types@7.7.0": 186 | version "7.7.0" 187 | resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.7.0.tgz#23af4d24bf9ce15d8d301236e3e3014143604f27" 188 | integrity sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w== 189 | 190 | "@typescript-eslint/typescript-estree@7.7.0", "@typescript-eslint/typescript-estree@^7.7.0": 191 | version "7.7.0" 192 | resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz#b5dd6383b4c6a852d7b256a37af971e8982be97f" 193 | integrity sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ== 194 | dependencies: 195 | "@typescript-eslint/types" "7.7.0" 196 | "@typescript-eslint/visitor-keys" "7.7.0" 197 | debug "^4.3.4" 198 | globby "^11.1.0" 199 | is-glob "^4.0.3" 200 | minimatch "^9.0.4" 201 | semver "^7.6.0" 202 | ts-api-utils "^1.3.0" 203 | 204 | "@typescript-eslint/utils@7.7.0": 205 | version "7.7.0" 206 | resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.7.0.tgz#3d2b6606a60ac34f3c625facfb3b3ab7e126f58d" 207 | integrity sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig== 208 | dependencies: 209 | "@eslint-community/eslint-utils" "^4.4.0" 210 | "@types/json-schema" "^7.0.15" 211 | "@types/semver" "^7.5.8" 212 | "@typescript-eslint/scope-manager" "7.7.0" 213 | "@typescript-eslint/types" "7.7.0" 214 | "@typescript-eslint/typescript-estree" "7.7.0" 215 | semver "^7.6.0" 216 | 217 | "@typescript-eslint/visitor-keys@7.7.0": 218 | version "7.7.0" 219 | resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz#950148cf1ac11562a2d903fdf7acf76714a2dc9e" 220 | integrity sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA== 221 | dependencies: 222 | "@typescript-eslint/types" "7.7.0" 223 | eslint-visitor-keys "^3.4.3" 224 | 225 | "@ungap/structured-clone@^1.2.0": 226 | version "1.2.0" 227 | resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" 228 | integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== 229 | 230 | acorn-jsx@^5.3.2: 231 | version "5.3.2" 232 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" 233 | integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== 234 | 235 | acorn@^8.9.0: 236 | version "8.11.3" 237 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" 238 | integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== 239 | 240 | ajv@^6.12.4, ajv@~6.12.6: 241 | version "6.12.6" 242 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" 243 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 244 | dependencies: 245 | fast-deep-equal "^3.1.1" 246 | fast-json-stable-stringify "^2.0.0" 247 | json-schema-traverse "^0.4.1" 248 | uri-js "^4.2.2" 249 | 250 | ansi-regex@^5.0.1: 251 | version "5.0.1" 252 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 253 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 254 | 255 | ansi-styles@^4.1.0: 256 | version "4.3.0" 257 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 258 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 259 | dependencies: 260 | color-convert "^2.0.1" 261 | 262 | argparse@^2.0.1: 263 | version "2.0.1" 264 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 265 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 266 | 267 | array-buffer-byte-length@^1.0.1: 268 | version "1.0.1" 269 | resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" 270 | integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== 271 | dependencies: 272 | call-bind "^1.0.5" 273 | is-array-buffer "^3.0.4" 274 | 275 | array-includes@^3.1.7: 276 | version "3.1.8" 277 | resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" 278 | integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== 279 | dependencies: 280 | call-bind "^1.0.7" 281 | define-properties "^1.2.1" 282 | es-abstract "^1.23.2" 283 | es-object-atoms "^1.0.0" 284 | get-intrinsic "^1.2.4" 285 | is-string "^1.0.7" 286 | 287 | array-union@^2.1.0: 288 | version "2.1.0" 289 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" 290 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== 291 | 292 | array.prototype.findlastindex@^1.2.3: 293 | version "1.2.5" 294 | resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" 295 | integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== 296 | dependencies: 297 | call-bind "^1.0.7" 298 | define-properties "^1.2.1" 299 | es-abstract "^1.23.2" 300 | es-errors "^1.3.0" 301 | es-object-atoms "^1.0.0" 302 | es-shim-unscopables "^1.0.2" 303 | 304 | array.prototype.flat@^1.3.2: 305 | version "1.3.2" 306 | resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" 307 | integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== 308 | dependencies: 309 | call-bind "^1.0.2" 310 | define-properties "^1.2.0" 311 | es-abstract "^1.22.1" 312 | es-shim-unscopables "^1.0.0" 313 | 314 | array.prototype.flatmap@^1.3.2: 315 | version "1.3.2" 316 | resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" 317 | integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== 318 | dependencies: 319 | call-bind "^1.0.2" 320 | define-properties "^1.2.0" 321 | es-abstract "^1.22.1" 322 | es-shim-unscopables "^1.0.0" 323 | 324 | arraybuffer.prototype.slice@^1.0.3: 325 | version "1.0.3" 326 | resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" 327 | integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== 328 | dependencies: 329 | array-buffer-byte-length "^1.0.1" 330 | call-bind "^1.0.5" 331 | define-properties "^1.2.1" 332 | es-abstract "^1.22.3" 333 | es-errors "^1.2.1" 334 | get-intrinsic "^1.2.3" 335 | is-array-buffer "^3.0.4" 336 | is-shared-array-buffer "^1.0.2" 337 | 338 | available-typed-arrays@^1.0.7: 339 | version "1.0.7" 340 | resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" 341 | integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== 342 | dependencies: 343 | possible-typed-array-names "^1.0.0" 344 | 345 | balanced-match@^1.0.0: 346 | version "1.0.2" 347 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 348 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 349 | 350 | brace-expansion@^1.1.7: 351 | version "1.1.11" 352 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 353 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 354 | dependencies: 355 | balanced-match "^1.0.0" 356 | concat-map "0.0.1" 357 | 358 | brace-expansion@^2.0.1: 359 | version "2.0.1" 360 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 361 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 362 | dependencies: 363 | balanced-match "^1.0.0" 364 | 365 | braces@^3.0.2: 366 | version "3.0.2" 367 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 368 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 369 | dependencies: 370 | fill-range "^7.0.1" 371 | 372 | call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: 373 | version "1.0.7" 374 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" 375 | integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== 376 | dependencies: 377 | es-define-property "^1.0.0" 378 | es-errors "^1.3.0" 379 | function-bind "^1.1.2" 380 | get-intrinsic "^1.2.4" 381 | set-function-length "^1.2.1" 382 | 383 | callsites@^3.0.0: 384 | version "3.1.0" 385 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 386 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 387 | 388 | chalk@^4.0.0: 389 | version "4.1.2" 390 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 391 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 392 | dependencies: 393 | ansi-styles "^4.1.0" 394 | supports-color "^7.1.0" 395 | 396 | color-convert@^2.0.1: 397 | version "2.0.1" 398 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 399 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 400 | dependencies: 401 | color-name "~1.1.4" 402 | 403 | color-name@~1.1.4: 404 | version "1.1.4" 405 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 406 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 407 | 408 | concat-map@0.0.1: 409 | version "0.0.1" 410 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 411 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 412 | 413 | cross-spawn@^7.0.2: 414 | version "7.0.3" 415 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 416 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 417 | dependencies: 418 | path-key "^3.1.0" 419 | shebang-command "^2.0.0" 420 | which "^2.0.1" 421 | 422 | data-view-buffer@^1.0.1: 423 | version "1.0.1" 424 | resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" 425 | integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== 426 | dependencies: 427 | call-bind "^1.0.6" 428 | es-errors "^1.3.0" 429 | is-data-view "^1.0.1" 430 | 431 | data-view-byte-length@^1.0.1: 432 | version "1.0.1" 433 | resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" 434 | integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== 435 | dependencies: 436 | call-bind "^1.0.7" 437 | es-errors "^1.3.0" 438 | is-data-view "^1.0.1" 439 | 440 | data-view-byte-offset@^1.0.0: 441 | version "1.0.0" 442 | resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" 443 | integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== 444 | dependencies: 445 | call-bind "^1.0.6" 446 | es-errors "^1.3.0" 447 | is-data-view "^1.0.1" 448 | 449 | debug@^3.2.7: 450 | version "3.2.7" 451 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" 452 | integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== 453 | dependencies: 454 | ms "^2.1.1" 455 | 456 | debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: 457 | version "4.3.4" 458 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 459 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 460 | dependencies: 461 | ms "2.1.2" 462 | 463 | deep-is@^0.1.3: 464 | version "0.1.4" 465 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" 466 | integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== 467 | 468 | define-data-property@^1.0.1, define-data-property@^1.1.4: 469 | version "1.1.4" 470 | resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" 471 | integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== 472 | dependencies: 473 | es-define-property "^1.0.0" 474 | es-errors "^1.3.0" 475 | gopd "^1.0.1" 476 | 477 | define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: 478 | version "1.2.1" 479 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" 480 | integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== 481 | dependencies: 482 | define-data-property "^1.0.1" 483 | has-property-descriptors "^1.0.0" 484 | object-keys "^1.1.1" 485 | 486 | dir-glob@^3.0.1: 487 | version "3.0.1" 488 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 489 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 490 | dependencies: 491 | path-type "^4.0.0" 492 | 493 | doctrine@^2.1.0: 494 | version "2.1.0" 495 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" 496 | integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== 497 | dependencies: 498 | esutils "^2.0.2" 499 | 500 | doctrine@^3.0.0: 501 | version "3.0.0" 502 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 503 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 504 | dependencies: 505 | esutils "^2.0.2" 506 | 507 | es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: 508 | version "1.23.3" 509 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" 510 | integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== 511 | dependencies: 512 | array-buffer-byte-length "^1.0.1" 513 | arraybuffer.prototype.slice "^1.0.3" 514 | available-typed-arrays "^1.0.7" 515 | call-bind "^1.0.7" 516 | data-view-buffer "^1.0.1" 517 | data-view-byte-length "^1.0.1" 518 | data-view-byte-offset "^1.0.0" 519 | es-define-property "^1.0.0" 520 | es-errors "^1.3.0" 521 | es-object-atoms "^1.0.0" 522 | es-set-tostringtag "^2.0.3" 523 | es-to-primitive "^1.2.1" 524 | function.prototype.name "^1.1.6" 525 | get-intrinsic "^1.2.4" 526 | get-symbol-description "^1.0.2" 527 | globalthis "^1.0.3" 528 | gopd "^1.0.1" 529 | has-property-descriptors "^1.0.2" 530 | has-proto "^1.0.3" 531 | has-symbols "^1.0.3" 532 | hasown "^2.0.2" 533 | internal-slot "^1.0.7" 534 | is-array-buffer "^3.0.4" 535 | is-callable "^1.2.7" 536 | is-data-view "^1.0.1" 537 | is-negative-zero "^2.0.3" 538 | is-regex "^1.1.4" 539 | is-shared-array-buffer "^1.0.3" 540 | is-string "^1.0.7" 541 | is-typed-array "^1.1.13" 542 | is-weakref "^1.0.2" 543 | object-inspect "^1.13.1" 544 | object-keys "^1.1.1" 545 | object.assign "^4.1.5" 546 | regexp.prototype.flags "^1.5.2" 547 | safe-array-concat "^1.1.2" 548 | safe-regex-test "^1.0.3" 549 | string.prototype.trim "^1.2.9" 550 | string.prototype.trimend "^1.0.8" 551 | string.prototype.trimstart "^1.0.8" 552 | typed-array-buffer "^1.0.2" 553 | typed-array-byte-length "^1.0.1" 554 | typed-array-byte-offset "^1.0.2" 555 | typed-array-length "^1.0.6" 556 | unbox-primitive "^1.0.2" 557 | which-typed-array "^1.1.15" 558 | 559 | es-define-property@^1.0.0: 560 | version "1.0.0" 561 | resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" 562 | integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== 563 | dependencies: 564 | get-intrinsic "^1.2.4" 565 | 566 | es-errors@^1.2.1, es-errors@^1.3.0: 567 | version "1.3.0" 568 | resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" 569 | integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== 570 | 571 | es-object-atoms@^1.0.0: 572 | version "1.0.0" 573 | resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" 574 | integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== 575 | dependencies: 576 | es-errors "^1.3.0" 577 | 578 | es-set-tostringtag@^2.0.3: 579 | version "2.0.3" 580 | resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" 581 | integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== 582 | dependencies: 583 | get-intrinsic "^1.2.4" 584 | has-tostringtag "^1.0.2" 585 | hasown "^2.0.1" 586 | 587 | es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: 588 | version "1.0.2" 589 | resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" 590 | integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== 591 | dependencies: 592 | hasown "^2.0.0" 593 | 594 | es-to-primitive@^1.2.1: 595 | version "1.2.1" 596 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" 597 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 598 | dependencies: 599 | is-callable "^1.1.4" 600 | is-date-object "^1.0.1" 601 | is-symbol "^1.0.2" 602 | 603 | escape-string-regexp@^4.0.0: 604 | version "4.0.0" 605 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 606 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 607 | 608 | eslint-config-prettier@^9.1.0: 609 | version "9.1.0" 610 | resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" 611 | integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== 612 | 613 | eslint-import-resolver-node@^0.3.9: 614 | version "0.3.9" 615 | resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" 616 | integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== 617 | dependencies: 618 | debug "^3.2.7" 619 | is-core-module "^2.13.0" 620 | resolve "^1.22.4" 621 | 622 | eslint-module-utils@^2.8.0: 623 | version "2.8.1" 624 | resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" 625 | integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== 626 | dependencies: 627 | debug "^3.2.7" 628 | 629 | eslint-plugin-import@^2.28.1: 630 | version "2.29.1" 631 | resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" 632 | integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== 633 | dependencies: 634 | array-includes "^3.1.7" 635 | array.prototype.findlastindex "^1.2.3" 636 | array.prototype.flat "^1.3.2" 637 | array.prototype.flatmap "^1.3.2" 638 | debug "^3.2.7" 639 | doctrine "^2.1.0" 640 | eslint-import-resolver-node "^0.3.9" 641 | eslint-module-utils "^2.8.0" 642 | hasown "^2.0.0" 643 | is-core-module "^2.13.1" 644 | is-glob "^4.0.3" 645 | minimatch "^3.1.2" 646 | object.fromentries "^2.0.7" 647 | object.groupby "^1.0.1" 648 | object.values "^1.1.7" 649 | semver "^6.3.1" 650 | tsconfig-paths "^3.15.0" 651 | 652 | eslint-plugin-simple-import-sort@^12.1.0: 653 | version "12.1.0" 654 | resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.0.tgz#8186ad55474d2f5c986a2f1bf70625a981e30d05" 655 | integrity sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig== 656 | 657 | eslint-plugin-tsdoc@^0.2.17: 658 | version "0.2.17" 659 | resolved "https://registry.yarnpkg.com/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz#27789495bbd8778abbf92db1707fec2ed3dfe281" 660 | integrity sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA== 661 | dependencies: 662 | "@microsoft/tsdoc" "0.14.2" 663 | "@microsoft/tsdoc-config" "0.16.2" 664 | 665 | eslint-scope@^7.2.2: 666 | version "7.2.2" 667 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" 668 | integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== 669 | dependencies: 670 | esrecurse "^4.3.0" 671 | estraverse "^5.2.0" 672 | 673 | eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: 674 | version "3.4.3" 675 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" 676 | integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== 677 | 678 | eslint@^8.48.0: 679 | version "8.57.0" 680 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" 681 | integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== 682 | dependencies: 683 | "@eslint-community/eslint-utils" "^4.2.0" 684 | "@eslint-community/regexpp" "^4.6.1" 685 | "@eslint/eslintrc" "^2.1.4" 686 | "@eslint/js" "8.57.0" 687 | "@humanwhocodes/config-array" "^0.11.14" 688 | "@humanwhocodes/module-importer" "^1.0.1" 689 | "@nodelib/fs.walk" "^1.2.8" 690 | "@ungap/structured-clone" "^1.2.0" 691 | ajv "^6.12.4" 692 | chalk "^4.0.0" 693 | cross-spawn "^7.0.2" 694 | debug "^4.3.2" 695 | doctrine "^3.0.0" 696 | escape-string-regexp "^4.0.0" 697 | eslint-scope "^7.2.2" 698 | eslint-visitor-keys "^3.4.3" 699 | espree "^9.6.1" 700 | esquery "^1.4.2" 701 | esutils "^2.0.2" 702 | fast-deep-equal "^3.1.3" 703 | file-entry-cache "^6.0.1" 704 | find-up "^5.0.0" 705 | glob-parent "^6.0.2" 706 | globals "^13.19.0" 707 | graphemer "^1.4.0" 708 | ignore "^5.2.0" 709 | imurmurhash "^0.1.4" 710 | is-glob "^4.0.0" 711 | is-path-inside "^3.0.3" 712 | js-yaml "^4.1.0" 713 | json-stable-stringify-without-jsonify "^1.0.1" 714 | levn "^0.4.1" 715 | lodash.merge "^4.6.2" 716 | minimatch "^3.1.2" 717 | natural-compare "^1.4.0" 718 | optionator "^0.9.3" 719 | strip-ansi "^6.0.1" 720 | text-table "^0.2.0" 721 | 722 | espree@^9.6.0, espree@^9.6.1: 723 | version "9.6.1" 724 | resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" 725 | integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== 726 | dependencies: 727 | acorn "^8.9.0" 728 | acorn-jsx "^5.3.2" 729 | eslint-visitor-keys "^3.4.1" 730 | 731 | esquery@^1.4.2: 732 | version "1.5.0" 733 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" 734 | integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== 735 | dependencies: 736 | estraverse "^5.1.0" 737 | 738 | esrecurse@^4.3.0: 739 | version "4.3.0" 740 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" 741 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 742 | dependencies: 743 | estraverse "^5.2.0" 744 | 745 | estraverse@^5.1.0, estraverse@^5.2.0: 746 | version "5.3.0" 747 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" 748 | integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== 749 | 750 | esutils@^2.0.2: 751 | version "2.0.3" 752 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 753 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 754 | 755 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 756 | version "3.1.3" 757 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 758 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 759 | 760 | fast-glob@^3.2.9: 761 | version "3.3.2" 762 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" 763 | integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== 764 | dependencies: 765 | "@nodelib/fs.stat" "^2.0.2" 766 | "@nodelib/fs.walk" "^1.2.3" 767 | glob-parent "^5.1.2" 768 | merge2 "^1.3.0" 769 | micromatch "^4.0.4" 770 | 771 | fast-json-stable-stringify@^2.0.0: 772 | version "2.1.0" 773 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 774 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 775 | 776 | fast-levenshtein@^2.0.6: 777 | version "2.0.6" 778 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 779 | integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== 780 | 781 | fastq@^1.6.0: 782 | version "1.17.1" 783 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" 784 | integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== 785 | dependencies: 786 | reusify "^1.0.4" 787 | 788 | file-entry-cache@^6.0.1: 789 | version "6.0.1" 790 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" 791 | integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== 792 | dependencies: 793 | flat-cache "^3.0.4" 794 | 795 | fill-range@^7.0.1: 796 | version "7.0.1" 797 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 798 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 799 | dependencies: 800 | to-regex-range "^5.0.1" 801 | 802 | find-up@^5.0.0: 803 | version "5.0.0" 804 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 805 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 806 | dependencies: 807 | locate-path "^6.0.0" 808 | path-exists "^4.0.0" 809 | 810 | flat-cache@^3.0.4: 811 | version "3.2.0" 812 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" 813 | integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== 814 | dependencies: 815 | flatted "^3.2.9" 816 | keyv "^4.5.3" 817 | rimraf "^3.0.2" 818 | 819 | flatted@^3.2.9: 820 | version "3.3.1" 821 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" 822 | integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== 823 | 824 | for-each@^0.3.3: 825 | version "0.3.3" 826 | resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" 827 | integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== 828 | dependencies: 829 | is-callable "^1.1.3" 830 | 831 | fs.realpath@^1.0.0: 832 | version "1.0.0" 833 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 834 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 835 | 836 | function-bind@^1.1.2: 837 | version "1.1.2" 838 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" 839 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 840 | 841 | function.prototype.name@^1.1.6: 842 | version "1.1.6" 843 | resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" 844 | integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== 845 | dependencies: 846 | call-bind "^1.0.2" 847 | define-properties "^1.2.0" 848 | es-abstract "^1.22.1" 849 | functions-have-names "^1.2.3" 850 | 851 | functions-have-names@^1.2.3: 852 | version "1.2.3" 853 | resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" 854 | integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== 855 | 856 | get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: 857 | version "1.2.4" 858 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" 859 | integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== 860 | dependencies: 861 | es-errors "^1.3.0" 862 | function-bind "^1.1.2" 863 | has-proto "^1.0.1" 864 | has-symbols "^1.0.3" 865 | hasown "^2.0.0" 866 | 867 | get-symbol-description@^1.0.2: 868 | version "1.0.2" 869 | resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" 870 | integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== 871 | dependencies: 872 | call-bind "^1.0.5" 873 | es-errors "^1.3.0" 874 | get-intrinsic "^1.2.4" 875 | 876 | glob-parent@^5.1.2: 877 | version "5.1.2" 878 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 879 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 880 | dependencies: 881 | is-glob "^4.0.1" 882 | 883 | glob-parent@^6.0.2: 884 | version "6.0.2" 885 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" 886 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 887 | dependencies: 888 | is-glob "^4.0.3" 889 | 890 | glob@^7.1.3: 891 | version "7.2.3" 892 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 893 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 894 | dependencies: 895 | fs.realpath "^1.0.0" 896 | inflight "^1.0.4" 897 | inherits "2" 898 | minimatch "^3.1.1" 899 | once "^1.3.0" 900 | path-is-absolute "^1.0.0" 901 | 902 | globals@^13.19.0: 903 | version "13.24.0" 904 | resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" 905 | integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== 906 | dependencies: 907 | type-fest "^0.20.2" 908 | 909 | globalthis@^1.0.3: 910 | version "1.0.3" 911 | resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" 912 | integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== 913 | dependencies: 914 | define-properties "^1.1.3" 915 | 916 | globby@^11.1.0: 917 | version "11.1.0" 918 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" 919 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== 920 | dependencies: 921 | array-union "^2.1.0" 922 | dir-glob "^3.0.1" 923 | fast-glob "^3.2.9" 924 | ignore "^5.2.0" 925 | merge2 "^1.4.1" 926 | slash "^3.0.0" 927 | 928 | gopd@^1.0.1: 929 | version "1.0.1" 930 | resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" 931 | integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== 932 | dependencies: 933 | get-intrinsic "^1.1.3" 934 | 935 | graphemer@^1.4.0: 936 | version "1.4.0" 937 | resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" 938 | integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== 939 | 940 | graphql@^15.8.0: 941 | version "15.8.0" 942 | resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" 943 | integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== 944 | 945 | has-bigints@^1.0.1, has-bigints@^1.0.2: 946 | version "1.0.2" 947 | resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" 948 | integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== 949 | 950 | has-flag@^4.0.0: 951 | version "4.0.0" 952 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 953 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 954 | 955 | has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: 956 | version "1.0.2" 957 | resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" 958 | integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== 959 | dependencies: 960 | es-define-property "^1.0.0" 961 | 962 | has-proto@^1.0.1, has-proto@^1.0.3: 963 | version "1.0.3" 964 | resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" 965 | integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== 966 | 967 | has-symbols@^1.0.2, has-symbols@^1.0.3: 968 | version "1.0.3" 969 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" 970 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 971 | 972 | has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: 973 | version "1.0.2" 974 | resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" 975 | integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== 976 | dependencies: 977 | has-symbols "^1.0.3" 978 | 979 | hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: 980 | version "2.0.2" 981 | resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" 982 | integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== 983 | dependencies: 984 | function-bind "^1.1.2" 985 | 986 | ignore@^5.2.0, ignore@^5.3.1: 987 | version "5.3.1" 988 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" 989 | integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== 990 | 991 | import-fresh@^3.2.1: 992 | version "3.3.0" 993 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" 994 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 995 | dependencies: 996 | parent-module "^1.0.0" 997 | resolve-from "^4.0.0" 998 | 999 | imurmurhash@^0.1.4: 1000 | version "0.1.4" 1001 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 1002 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 1003 | 1004 | inflight@^1.0.4: 1005 | version "1.0.6" 1006 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1007 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 1008 | dependencies: 1009 | once "^1.3.0" 1010 | wrappy "1" 1011 | 1012 | inherits@2: 1013 | version "2.0.4" 1014 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 1015 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 1016 | 1017 | internal-slot@^1.0.7: 1018 | version "1.0.7" 1019 | resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" 1020 | integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== 1021 | dependencies: 1022 | es-errors "^1.3.0" 1023 | hasown "^2.0.0" 1024 | side-channel "^1.0.4" 1025 | 1026 | is-array-buffer@^3.0.4: 1027 | version "3.0.4" 1028 | resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" 1029 | integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== 1030 | dependencies: 1031 | call-bind "^1.0.2" 1032 | get-intrinsic "^1.2.1" 1033 | 1034 | is-bigint@^1.0.1: 1035 | version "1.0.4" 1036 | resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" 1037 | integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== 1038 | dependencies: 1039 | has-bigints "^1.0.1" 1040 | 1041 | is-boolean-object@^1.1.0: 1042 | version "1.1.2" 1043 | resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" 1044 | integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== 1045 | dependencies: 1046 | call-bind "^1.0.2" 1047 | has-tostringtag "^1.0.0" 1048 | 1049 | is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: 1050 | version "1.2.7" 1051 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" 1052 | integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== 1053 | 1054 | is-core-module@^2.1.0, is-core-module@^2.13.0, is-core-module@^2.13.1: 1055 | version "2.13.1" 1056 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" 1057 | integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== 1058 | dependencies: 1059 | hasown "^2.0.0" 1060 | 1061 | is-data-view@^1.0.1: 1062 | version "1.0.1" 1063 | resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" 1064 | integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== 1065 | dependencies: 1066 | is-typed-array "^1.1.13" 1067 | 1068 | is-date-object@^1.0.1: 1069 | version "1.0.5" 1070 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" 1071 | integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== 1072 | dependencies: 1073 | has-tostringtag "^1.0.0" 1074 | 1075 | is-extglob@^2.1.1: 1076 | version "2.1.1" 1077 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 1078 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 1079 | 1080 | is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: 1081 | version "4.0.3" 1082 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 1083 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 1084 | dependencies: 1085 | is-extglob "^2.1.1" 1086 | 1087 | is-negative-zero@^2.0.3: 1088 | version "2.0.3" 1089 | resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" 1090 | integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== 1091 | 1092 | is-number-object@^1.0.4: 1093 | version "1.0.7" 1094 | resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" 1095 | integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== 1096 | dependencies: 1097 | has-tostringtag "^1.0.0" 1098 | 1099 | is-number@^7.0.0: 1100 | version "7.0.0" 1101 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 1102 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1103 | 1104 | is-path-inside@^3.0.3: 1105 | version "3.0.3" 1106 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" 1107 | integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== 1108 | 1109 | is-regex@^1.1.4: 1110 | version "1.1.4" 1111 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" 1112 | integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== 1113 | dependencies: 1114 | call-bind "^1.0.2" 1115 | has-tostringtag "^1.0.0" 1116 | 1117 | is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: 1118 | version "1.0.3" 1119 | resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" 1120 | integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== 1121 | dependencies: 1122 | call-bind "^1.0.7" 1123 | 1124 | is-string@^1.0.5, is-string@^1.0.7: 1125 | version "1.0.7" 1126 | resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" 1127 | integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== 1128 | dependencies: 1129 | has-tostringtag "^1.0.0" 1130 | 1131 | is-symbol@^1.0.2, is-symbol@^1.0.3: 1132 | version "1.0.4" 1133 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" 1134 | integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== 1135 | dependencies: 1136 | has-symbols "^1.0.2" 1137 | 1138 | is-typed-array@^1.1.13: 1139 | version "1.1.13" 1140 | resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" 1141 | integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== 1142 | dependencies: 1143 | which-typed-array "^1.1.14" 1144 | 1145 | is-weakref@^1.0.2: 1146 | version "1.0.2" 1147 | resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" 1148 | integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== 1149 | dependencies: 1150 | call-bind "^1.0.2" 1151 | 1152 | isarray@^2.0.5: 1153 | version "2.0.5" 1154 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" 1155 | integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== 1156 | 1157 | isexe@^2.0.0: 1158 | version "2.0.0" 1159 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 1160 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 1161 | 1162 | jju@~1.4.0: 1163 | version "1.4.0" 1164 | resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" 1165 | integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== 1166 | 1167 | js-yaml@^4.1.0: 1168 | version "4.1.0" 1169 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 1170 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 1171 | dependencies: 1172 | argparse "^2.0.1" 1173 | 1174 | json-buffer@3.0.1: 1175 | version "3.0.1" 1176 | resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" 1177 | integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== 1178 | 1179 | json-schema-traverse@^0.4.1: 1180 | version "0.4.1" 1181 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 1182 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 1183 | 1184 | json-stable-stringify-without-jsonify@^1.0.1: 1185 | version "1.0.1" 1186 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 1187 | integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== 1188 | 1189 | json5@^1.0.2: 1190 | version "1.0.2" 1191 | resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" 1192 | integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== 1193 | dependencies: 1194 | minimist "^1.2.0" 1195 | 1196 | keyv@^4.5.3: 1197 | version "4.5.4" 1198 | resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" 1199 | integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== 1200 | dependencies: 1201 | json-buffer "3.0.1" 1202 | 1203 | levn@^0.4.1: 1204 | version "0.4.1" 1205 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" 1206 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 1207 | dependencies: 1208 | prelude-ls "^1.2.1" 1209 | type-check "~0.4.0" 1210 | 1211 | locate-path@^6.0.0: 1212 | version "6.0.0" 1213 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 1214 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 1215 | dependencies: 1216 | p-locate "^5.0.0" 1217 | 1218 | lodash.merge@^4.6.2: 1219 | version "4.6.2" 1220 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 1221 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 1222 | 1223 | lru-cache@^6.0.0: 1224 | version "6.0.0" 1225 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 1226 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1227 | dependencies: 1228 | yallist "^4.0.0" 1229 | 1230 | merge2@^1.3.0, merge2@^1.4.1: 1231 | version "1.4.1" 1232 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 1233 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 1234 | 1235 | micromatch@^4.0.4: 1236 | version "4.0.5" 1237 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" 1238 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 1239 | dependencies: 1240 | braces "^3.0.2" 1241 | picomatch "^2.3.1" 1242 | 1243 | minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: 1244 | version "3.1.2" 1245 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 1246 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 1247 | dependencies: 1248 | brace-expansion "^1.1.7" 1249 | 1250 | minimatch@^9.0.4: 1251 | version "9.0.4" 1252 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" 1253 | integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== 1254 | dependencies: 1255 | brace-expansion "^2.0.1" 1256 | 1257 | minimist@^1.2.0, minimist@^1.2.6: 1258 | version "1.2.8" 1259 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" 1260 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 1261 | 1262 | ms@2.1.2: 1263 | version "2.1.2" 1264 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1265 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1266 | 1267 | ms@^2.1.1: 1268 | version "2.1.3" 1269 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 1270 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 1271 | 1272 | natural-compare@^1.4.0: 1273 | version "1.4.0" 1274 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 1275 | integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== 1276 | 1277 | object-inspect@^1.13.1: 1278 | version "1.13.1" 1279 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" 1280 | integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== 1281 | 1282 | object-keys@^1.1.1: 1283 | version "1.1.1" 1284 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 1285 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 1286 | 1287 | object.assign@^4.1.5: 1288 | version "4.1.5" 1289 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" 1290 | integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== 1291 | dependencies: 1292 | call-bind "^1.0.5" 1293 | define-properties "^1.2.1" 1294 | has-symbols "^1.0.3" 1295 | object-keys "^1.1.1" 1296 | 1297 | object.fromentries@^2.0.7: 1298 | version "2.0.8" 1299 | resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" 1300 | integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== 1301 | dependencies: 1302 | call-bind "^1.0.7" 1303 | define-properties "^1.2.1" 1304 | es-abstract "^1.23.2" 1305 | es-object-atoms "^1.0.0" 1306 | 1307 | object.groupby@^1.0.1: 1308 | version "1.0.3" 1309 | resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" 1310 | integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== 1311 | dependencies: 1312 | call-bind "^1.0.7" 1313 | define-properties "^1.2.1" 1314 | es-abstract "^1.23.2" 1315 | 1316 | object.values@^1.1.7: 1317 | version "1.2.0" 1318 | resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" 1319 | integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== 1320 | dependencies: 1321 | call-bind "^1.0.7" 1322 | define-properties "^1.2.1" 1323 | es-object-atoms "^1.0.0" 1324 | 1325 | once@^1.3.0: 1326 | version "1.4.0" 1327 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1328 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 1329 | dependencies: 1330 | wrappy "1" 1331 | 1332 | optionator@^0.9.3: 1333 | version "0.9.3" 1334 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" 1335 | integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== 1336 | dependencies: 1337 | "@aashutoshrathi/word-wrap" "^1.2.3" 1338 | deep-is "^0.1.3" 1339 | fast-levenshtein "^2.0.6" 1340 | levn "^0.4.1" 1341 | prelude-ls "^1.2.1" 1342 | type-check "^0.4.0" 1343 | 1344 | p-limit@^3.0.2: 1345 | version "3.1.0" 1346 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 1347 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 1348 | dependencies: 1349 | yocto-queue "^0.1.0" 1350 | 1351 | p-locate@^5.0.0: 1352 | version "5.0.0" 1353 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 1354 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 1355 | dependencies: 1356 | p-limit "^3.0.2" 1357 | 1358 | parent-module@^1.0.0: 1359 | version "1.0.1" 1360 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 1361 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1362 | dependencies: 1363 | callsites "^3.0.0" 1364 | 1365 | path-exists@^4.0.0: 1366 | version "4.0.0" 1367 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 1368 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1369 | 1370 | path-is-absolute@^1.0.0: 1371 | version "1.0.1" 1372 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1373 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 1374 | 1375 | path-key@^3.1.0: 1376 | version "3.1.1" 1377 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 1378 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1379 | 1380 | path-parse@^1.0.6, path-parse@^1.0.7: 1381 | version "1.0.7" 1382 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 1383 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 1384 | 1385 | path-type@^4.0.0: 1386 | version "4.0.0" 1387 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 1388 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 1389 | 1390 | picomatch@^2.3.1: 1391 | version "2.3.1" 1392 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 1393 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1394 | 1395 | possible-typed-array-names@^1.0.0: 1396 | version "1.0.0" 1397 | resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" 1398 | integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== 1399 | 1400 | prelude-ls@^1.2.1: 1401 | version "1.2.1" 1402 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" 1403 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 1404 | 1405 | prettier@^3.2.5: 1406 | version "3.2.5" 1407 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" 1408 | integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== 1409 | 1410 | punycode@^2.1.0: 1411 | version "2.3.1" 1412 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" 1413 | integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== 1414 | 1415 | queue-microtask@^1.2.2: 1416 | version "1.2.3" 1417 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 1418 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1419 | 1420 | regexp.prototype.flags@^1.5.2: 1421 | version "1.5.2" 1422 | resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" 1423 | integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== 1424 | dependencies: 1425 | call-bind "^1.0.6" 1426 | define-properties "^1.2.1" 1427 | es-errors "^1.3.0" 1428 | set-function-name "^2.0.1" 1429 | 1430 | resolve-from@^4.0.0: 1431 | version "4.0.0" 1432 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 1433 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1434 | 1435 | resolve@^1.22.4: 1436 | version "1.22.8" 1437 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" 1438 | integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== 1439 | dependencies: 1440 | is-core-module "^2.13.0" 1441 | path-parse "^1.0.7" 1442 | supports-preserve-symlinks-flag "^1.0.0" 1443 | 1444 | resolve@~1.19.0: 1445 | version "1.19.0" 1446 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" 1447 | integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== 1448 | dependencies: 1449 | is-core-module "^2.1.0" 1450 | path-parse "^1.0.6" 1451 | 1452 | reusify@^1.0.4: 1453 | version "1.0.4" 1454 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 1455 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1456 | 1457 | rimraf@^3.0.2: 1458 | version "3.0.2" 1459 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 1460 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 1461 | dependencies: 1462 | glob "^7.1.3" 1463 | 1464 | run-parallel@^1.1.9: 1465 | version "1.2.0" 1466 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 1467 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1468 | dependencies: 1469 | queue-microtask "^1.2.2" 1470 | 1471 | safe-array-concat@^1.1.2: 1472 | version "1.1.2" 1473 | resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" 1474 | integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== 1475 | dependencies: 1476 | call-bind "^1.0.7" 1477 | get-intrinsic "^1.2.4" 1478 | has-symbols "^1.0.3" 1479 | isarray "^2.0.5" 1480 | 1481 | safe-regex-test@^1.0.3: 1482 | version "1.0.3" 1483 | resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" 1484 | integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== 1485 | dependencies: 1486 | call-bind "^1.0.6" 1487 | es-errors "^1.3.0" 1488 | is-regex "^1.1.4" 1489 | 1490 | semver@^6.3.1: 1491 | version "6.3.1" 1492 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" 1493 | integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== 1494 | 1495 | semver@^7.6.0: 1496 | version "7.6.0" 1497 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" 1498 | integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== 1499 | dependencies: 1500 | lru-cache "^6.0.0" 1501 | 1502 | set-function-length@^1.2.1: 1503 | version "1.2.2" 1504 | resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" 1505 | integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== 1506 | dependencies: 1507 | define-data-property "^1.1.4" 1508 | es-errors "^1.3.0" 1509 | function-bind "^1.1.2" 1510 | get-intrinsic "^1.2.4" 1511 | gopd "^1.0.1" 1512 | has-property-descriptors "^1.0.2" 1513 | 1514 | set-function-name@^2.0.1: 1515 | version "2.0.2" 1516 | resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" 1517 | integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== 1518 | dependencies: 1519 | define-data-property "^1.1.4" 1520 | es-errors "^1.3.0" 1521 | functions-have-names "^1.2.3" 1522 | has-property-descriptors "^1.0.2" 1523 | 1524 | shebang-command@^2.0.0: 1525 | version "2.0.0" 1526 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 1527 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1528 | dependencies: 1529 | shebang-regex "^3.0.0" 1530 | 1531 | shebang-regex@^3.0.0: 1532 | version "3.0.0" 1533 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 1534 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1535 | 1536 | side-channel@^1.0.4: 1537 | version "1.0.6" 1538 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" 1539 | integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== 1540 | dependencies: 1541 | call-bind "^1.0.7" 1542 | es-errors "^1.3.0" 1543 | get-intrinsic "^1.2.4" 1544 | object-inspect "^1.13.1" 1545 | 1546 | slash@^3.0.0: 1547 | version "3.0.0" 1548 | resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 1549 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 1550 | 1551 | string.prototype.trim@^1.2.9: 1552 | version "1.2.9" 1553 | resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" 1554 | integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== 1555 | dependencies: 1556 | call-bind "^1.0.7" 1557 | define-properties "^1.2.1" 1558 | es-abstract "^1.23.0" 1559 | es-object-atoms "^1.0.0" 1560 | 1561 | string.prototype.trimend@^1.0.8: 1562 | version "1.0.8" 1563 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" 1564 | integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== 1565 | dependencies: 1566 | call-bind "^1.0.7" 1567 | define-properties "^1.2.1" 1568 | es-object-atoms "^1.0.0" 1569 | 1570 | string.prototype.trimstart@^1.0.8: 1571 | version "1.0.8" 1572 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" 1573 | integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== 1574 | dependencies: 1575 | call-bind "^1.0.7" 1576 | define-properties "^1.2.1" 1577 | es-object-atoms "^1.0.0" 1578 | 1579 | strip-ansi@^6.0.1: 1580 | version "6.0.1" 1581 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1582 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1583 | dependencies: 1584 | ansi-regex "^5.0.1" 1585 | 1586 | strip-bom@^3.0.0: 1587 | version "3.0.0" 1588 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1589 | integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== 1590 | 1591 | strip-json-comments@^3.1.1: 1592 | version "3.1.1" 1593 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 1594 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 1595 | 1596 | supports-color@^7.1.0: 1597 | version "7.2.0" 1598 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 1599 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1600 | dependencies: 1601 | has-flag "^4.0.0" 1602 | 1603 | supports-preserve-symlinks-flag@^1.0.0: 1604 | version "1.0.0" 1605 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1606 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1607 | 1608 | text-table@^0.2.0: 1609 | version "0.2.0" 1610 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1611 | integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== 1612 | 1613 | to-regex-range@^5.0.1: 1614 | version "5.0.1" 1615 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1616 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1617 | dependencies: 1618 | is-number "^7.0.0" 1619 | 1620 | ts-api-utils@^1.3.0: 1621 | version "1.3.0" 1622 | resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" 1623 | integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== 1624 | 1625 | tsconfig-paths@^3.15.0: 1626 | version "3.15.0" 1627 | resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" 1628 | integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== 1629 | dependencies: 1630 | "@types/json5" "^0.0.29" 1631 | json5 "^1.0.2" 1632 | minimist "^1.2.6" 1633 | strip-bom "^3.0.0" 1634 | 1635 | tslib@^2.5.0: 1636 | version "2.6.2" 1637 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" 1638 | integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== 1639 | 1640 | type-check@^0.4.0, type-check@~0.4.0: 1641 | version "0.4.0" 1642 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" 1643 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 1644 | dependencies: 1645 | prelude-ls "^1.2.1" 1646 | 1647 | type-fest@^0.20.2: 1648 | version "0.20.2" 1649 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" 1650 | integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== 1651 | 1652 | typed-array-buffer@^1.0.2: 1653 | version "1.0.2" 1654 | resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" 1655 | integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== 1656 | dependencies: 1657 | call-bind "^1.0.7" 1658 | es-errors "^1.3.0" 1659 | is-typed-array "^1.1.13" 1660 | 1661 | typed-array-byte-length@^1.0.1: 1662 | version "1.0.1" 1663 | resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" 1664 | integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== 1665 | dependencies: 1666 | call-bind "^1.0.7" 1667 | for-each "^0.3.3" 1668 | gopd "^1.0.1" 1669 | has-proto "^1.0.3" 1670 | is-typed-array "^1.1.13" 1671 | 1672 | typed-array-byte-offset@^1.0.2: 1673 | version "1.0.2" 1674 | resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" 1675 | integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== 1676 | dependencies: 1677 | available-typed-arrays "^1.0.7" 1678 | call-bind "^1.0.7" 1679 | for-each "^0.3.3" 1680 | gopd "^1.0.1" 1681 | has-proto "^1.0.3" 1682 | is-typed-array "^1.1.13" 1683 | 1684 | typed-array-length@^1.0.6: 1685 | version "1.0.6" 1686 | resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" 1687 | integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== 1688 | dependencies: 1689 | call-bind "^1.0.7" 1690 | for-each "^0.3.3" 1691 | gopd "^1.0.1" 1692 | has-proto "^1.0.3" 1693 | is-typed-array "^1.1.13" 1694 | possible-typed-array-names "^1.0.0" 1695 | 1696 | typescript@^5.4.5: 1697 | version "5.4.5" 1698 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" 1699 | integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== 1700 | 1701 | unbox-primitive@^1.0.2: 1702 | version "1.0.2" 1703 | resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" 1704 | integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== 1705 | dependencies: 1706 | call-bind "^1.0.2" 1707 | has-bigints "^1.0.2" 1708 | has-symbols "^1.0.3" 1709 | which-boxed-primitive "^1.0.2" 1710 | 1711 | undici-types@~5.26.4: 1712 | version "5.26.5" 1713 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" 1714 | integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== 1715 | 1716 | uri-js@^4.2.2: 1717 | version "4.4.1" 1718 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" 1719 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 1720 | dependencies: 1721 | punycode "^2.1.0" 1722 | 1723 | which-boxed-primitive@^1.0.2: 1724 | version "1.0.2" 1725 | resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" 1726 | integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== 1727 | dependencies: 1728 | is-bigint "^1.0.1" 1729 | is-boolean-object "^1.1.0" 1730 | is-number-object "^1.0.4" 1731 | is-string "^1.0.5" 1732 | is-symbol "^1.0.3" 1733 | 1734 | which-typed-array@^1.1.14, which-typed-array@^1.1.15: 1735 | version "1.1.15" 1736 | resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" 1737 | integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== 1738 | dependencies: 1739 | available-typed-arrays "^1.0.7" 1740 | call-bind "^1.0.7" 1741 | for-each "^0.3.3" 1742 | gopd "^1.0.1" 1743 | has-tostringtag "^1.0.2" 1744 | 1745 | which@^2.0.1: 1746 | version "2.0.2" 1747 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1748 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1749 | dependencies: 1750 | isexe "^2.0.0" 1751 | 1752 | wrappy@1: 1753 | version "1.0.2" 1754 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1755 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1756 | 1757 | yallist@^4.0.0: 1758 | version "4.0.0" 1759 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1760 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1761 | 1762 | yocto-queue@^0.1.0: 1763 | version "0.1.0" 1764 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 1765 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 1766 | --------------------------------------------------------------------------------