From ac9558cc9e7c15249eec445b6c9ff3927430b44f Mon Sep 17 00:00:00 2001 From: djmadeira Date: Mon, 6 May 2024 12:13:44 -0400 Subject: [PATCH] add rollup API spec additional paths Fixed spec (#246) Signed-off-by: saimedhi Fixed search_pipeline spec (#253) Signed-off-by: saimedhi Updated API name: search_pipeline.create to search_pipeline.put (#254) Signed-off-by: saimedhi Fixed search_pipeline.get spec (#255) Signed-off-by: saimedhi Filled in Missing Defaults (#249) * Filled in Missing Defaults Signed-off-by: Theo Truong * # Wordings Signed-off-by: Theo Truong --------- Signed-off-by: Theo Truong Replace pull_request_target workflow with two workflows that upload/download an artifact. (#251) * Fix: replace pull_request_target with a download/upload artifact. Signed-off-by: dblock * Use upload/download-artifact@v4. Signed-off-by: dblock --------- Signed-off-by: dblock Fix: var -> const. (#258) Signed-off-by: dblock Adds tools linter. (#260) Signed-off-by: dblock Fix: '@typescript-eslint/indent': 'warn'. (#262) Signed-off-by: dblock Removed default values from param description (#264) Signed-off-by: saimedhi Generate _opendistro endpoints through merger tool (#257) * Generate _opendistro endpoints through merger tool Signed-off-by: Theo Truong * # Renamed `replaced` with `superseded` Signed-off-by: Theo Truong * # Rebased DEVELOPER_GUIDE.md Signed-off-by: Theo Truong * # Set Tabsize from 4 to 2 Signed-off-by: Theo Truong --------- Signed-off-by: Theo Truong Add _plugins/_notifications/channels API spec (#256) * Add _plugins/_notifications/channels API Signed-off-by: Sokratis Papadopoulos Fix obvious lints. (#265) * Fix cosmetic autoformat lints. * Fixed @typescript-eslint/no-unused-vars. * Fixed eqeqeq. * Fixed @typescript-eslint/consistent-type-imports. * Fixed no-useless-return. * Fixed @typescript-eslint/array-type. * Rebased with changes on main. Signed-off-by: dblock Update list notification channels url for externalDocs (#267) Signed-off-by: Sokratis Papadopoulos Co-authored-by: Sokratis Papadopoulos Updated/Corrected Docs (#270) * Updated/Corrected Docs - README.md - CLIENT_GENERATOR_GUIDE.md - DEVELOPER_GUIDE.md - ./tools/README.md Signed-off-by: Theo Truong * # minor corrections Signed-off-by: Theo Truong * # minor corrections Signed-off-by: Theo Truong --------- Signed-off-by: Theo Truong Add lychee github action for links checking (#269) Corrected content type for bulk operations (#275) * Corrected content type for bulk operations Signed-off-by: Theo Truong * # linting Signed-off-by: Theo Truong --------- Signed-off-by: Theo Truong Validate _superseded_operations.yaml against its JSON schema (#276) * Validate _superseded_operations.yaml against its JSON schema Signed-off-by: Theo Truong * # lint Signed-off-by: Theo Truong * # lint Signed-off-by: Theo Truong --------- Signed-off-by: Theo Truong Fixed Linting for Tools (#278) Signed-off-by: Theo Truong Removed Root file since it's not needed anymore (#279) Signed-off-by: Theo Truong Fixed missing global params (#280) Signed-off-by: Theo Truong Added validation for _info.yaml (#281) DRY'ed JSON schema validation logic Signed-off-by: Theo Truong Implement inline object schema validator (#282) * Implement inline object schema validator and underlying visitor pattern Signed-off-by: Thomas Farr * Fix spec lint error Signed-off-by: Thomas Farr --------- Signed-off-by: Thomas Farr oops fix lint --- .github/workflows/build.yml | 2 +- .github/workflows/coverage-comment.yml | 36 + .../{coverage.yml => coverage-gather.yml} | 27 +- .github/workflows/links.yml | 17 + .github/workflows/lint.yml | 3 +- .github/workflows/tools.yml | 1 + .lycheeignore | 1 + CLIENT_GENERATOR_GUIDE.md | 19 +- DEVELOPER_GUIDE.md | 65 +- README.md | 12 +- _plugins/openapi.rb | 2 +- coverage/README.md | 2 +- json_schemas/_info.schema.yaml | 44 + .../_superseded_operations.schema.yaml | 21 + spec/_global_parameters.yaml | 43 + spec/_info.yaml | 4 + spec/_superseded_operations.yaml | 568 +++ spec/namespaces/_core.yaml | 95 +- spec/namespaces/cat.yaml | 8 + spec/namespaces/cluster.yaml | 22 +- spec/namespaces/indices.yaml | 56 +- spec/namespaces/ingest.yaml | 1 + spec/namespaces/nodes.yaml | 2 + spec/namespaces/notifications.yaml | 30 + spec/namespaces/rollups.yaml | 178 + spec/namespaces/search_pipeline.yaml | 102 +- spec/namespaces/security.yaml | 26 +- spec/namespaces/snapshot.yaml | 7 +- spec/namespaces/tasks.yaml | 4 + spec/opensearch-openapi.yaml | 544 --- spec/schemas/notifications._common.yaml | 13 + spec/schemas/rollups._common.yaml | 195 + tools/README.md | 38 +- tools/eslint.config.mjs | 57 + tools/helpers.ts | 114 +- tools/jest.config.js | 4 +- tools/linter/InlineObjectSchemaValidator.ts | 44 + tools/linter/PathRefsValidator.ts | 76 - tools/linter/SchemaRefsValidator.ts | 178 +- tools/linter/SpecValidator.ts | 64 +- tools/linter/components/InfoFile.ts | 5 + tools/linter/components/NamespaceFile.ts | 176 +- tools/linter/components/NamespacesFolder.ts | 45 +- tools/linter/components/Operation.ts | 194 +- tools/linter/components/OperationGroup.ts | 117 +- tools/linter/components/RootFile.ts | 34 - tools/linter/components/Schema.ts | 37 +- tools/linter/components/SchemaFile.ts | 67 +- tools/linter/components/SchemasFolder.ts | 20 +- .../components/SupersededOperationsFile.ts | 5 + tools/linter/components/base/FileValidator.ts | 87 +- .../linter/components/base/FolderValidator.ts | 49 +- tools/linter/components/base/ValidatorBase.ts | 28 +- tools/linter/lint.ts | 27 +- tools/linter/utils/SpecificationVisitor.ts | 117 + tools/linter/utils/index.ts | 62 + tools/merger/GlobalParamsGenerator.ts | 36 + tools/merger/OpenApiMerger.ts | 233 +- tools/merger/OpenDistro.ts | 23 + tools/merger/SupersededOpsGenerator.ts | 48 + tools/merger/merge.ts | 9 +- tools/package-lock.json | 3809 +++++++++++++++-- tools/package.json | 16 +- tools/test/linter/InfoFile.test.ts | 12 + tools/test/linter/NamespaceFile.test.ts | 162 +- tools/test/linter/NamespacesFolder.test.ts | 98 +- tools/test/linter/Operation.test.ts | 265 +- tools/test/linter/OperationGroup.test.ts | 147 +- tools/test/linter/PathRefsValidator.test.ts | 31 - tools/test/linter/RootFile.test.ts | 27 - tools/test/linter/Schema.test.ts | 23 +- tools/test/linter/SchemaFile.test.ts | 48 +- tools/test/linter/SchemaRefsValidator.test.ts | 56 +- tools/test/linter/SpecValidator.test.ts | 34 +- .../linter/SupersededOperationsFile.test.ts | 11 + tools/test/linter/factories/namespace_file.ts | 80 +- tools/test/linter/factories/operation.ts | 75 +- .../test/linter/factories/operation_group.ts | 66 +- tools/test/linter/factories/schema.ts | 34 +- tools/test/linter/factories/schema_file.ts | 41 +- tools/test/linter/fixtures/_info.yaml | 2 + .../fixtures/_superseded_operations.yaml | 7 + tools/test/linter/fixtures/empty/_info.yaml | 4 + .../empty/_superseded_operations.yaml | 1 + .../namespaces/{ => invalid_files}/cat.yaml | 0 .../{ => invalid_files}/dup_path_a.yaml | 0 .../{ => invalid_files}/dup_path_b.yaml | 0 .../{ => invalid_files}/dup_path_c.yaml | 0 .../{ => invalid_files}/indices.txt | 0 .../{ => invalid_files}/invalid_spec.yaml | 0 .../{ => invalid_files}/invalid_yaml.yaml | 0 .../namespaces/invalid_folder/cat.yaml | 27 + .../namespaces/invalid_folder/dup_path_a.yaml | 4 + .../namespaces/invalid_folder/dup_path_b.yaml | 3 + .../namespaces/invalid_folder/dup_path_c.yaml | 3 + .../path_refs_validator/namespaces/cat.yaml | 3 - .../namespaces/indices.yaml | 3 - .../namespaces/missing.yaml | 3 - .../opensearch-openapi.yaml | 13 - tools/test/linter/fixtures/root.yaml | 32 - tools/test/merger/OpenApiMerger.test.ts | 18 +- tools/test/merger/fixtures/expected.yaml | 36 +- ...h-openapi.yaml => _global_parameters.yaml} | 11 +- tools/test/merger/fixtures/spec/_info.yaml | 5 + .../fixtures/spec/_superseded_operations.yaml | 10 + .../namespaces/{ignored.yaml => indices.yaml} | 0 .../fixtures/spec/namespaces/shelter.yaml | 11 +- tools/types.ts | 42 +- 108 files changed, 6781 insertions(+), 2636 deletions(-) create mode 100644 .github/workflows/coverage-comment.yml rename .github/workflows/{coverage.yml => coverage-gather.yml} (76%) create mode 100644 .github/workflows/links.yml create mode 100644 .lycheeignore create mode 100644 json_schemas/_info.schema.yaml create mode 100644 json_schemas/_superseded_operations.schema.yaml create mode 100644 spec/_global_parameters.yaml create mode 100644 spec/_info.yaml create mode 100644 spec/_superseded_operations.yaml create mode 100644 spec/namespaces/rollups.yaml delete mode 100644 spec/opensearch-openapi.yaml create mode 100644 spec/schemas/rollups._common.yaml create mode 100644 tools/eslint.config.mjs create mode 100644 tools/linter/InlineObjectSchemaValidator.ts delete mode 100644 tools/linter/PathRefsValidator.ts create mode 100644 tools/linter/components/InfoFile.ts delete mode 100644 tools/linter/components/RootFile.ts create mode 100644 tools/linter/components/SupersededOperationsFile.ts create mode 100644 tools/linter/utils/SpecificationVisitor.ts create mode 100644 tools/linter/utils/index.ts create mode 100644 tools/merger/GlobalParamsGenerator.ts create mode 100644 tools/merger/OpenDistro.ts create mode 100644 tools/merger/SupersededOpsGenerator.ts create mode 100644 tools/test/linter/InfoFile.test.ts delete mode 100644 tools/test/linter/PathRefsValidator.test.ts delete mode 100644 tools/test/linter/RootFile.test.ts create mode 100644 tools/test/linter/SupersededOperationsFile.test.ts create mode 100644 tools/test/linter/fixtures/_info.yaml create mode 100644 tools/test/linter/fixtures/_superseded_operations.yaml create mode 100644 tools/test/linter/fixtures/empty/_info.yaml create mode 100644 tools/test/linter/fixtures/empty/_superseded_operations.yaml rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/cat.yaml (100%) rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/dup_path_a.yaml (100%) rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/dup_path_b.yaml (100%) rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/dup_path_c.yaml (100%) rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/indices.txt (100%) rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/invalid_spec.yaml (100%) rename tools/test/linter/fixtures/folder_validators/namespaces/{ => invalid_files}/invalid_yaml.yaml (100%) create mode 100644 tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/cat.yaml create mode 100644 tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_a.yaml create mode 100644 tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_b.yaml create mode 100644 tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_c.yaml delete mode 100644 tools/test/linter/fixtures/path_refs_validator/namespaces/cat.yaml delete mode 100644 tools/test/linter/fixtures/path_refs_validator/namespaces/indices.yaml delete mode 100644 tools/test/linter/fixtures/path_refs_validator/namespaces/missing.yaml delete mode 100644 tools/test/linter/fixtures/path_refs_validator/opensearch-openapi.yaml delete mode 100644 tools/test/linter/fixtures/root.yaml rename tools/test/merger/fixtures/spec/{opensearch-openapi.yaml => _global_parameters.yaml} (50%) create mode 100644 tools/test/merger/fixtures/spec/_info.yaml create mode 100644 tools/test/merger/fixtures/spec/_superseded_operations.yaml rename tools/test/merger/fixtures/spec/namespaces/{ignored.yaml => indices.yaml} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ceb549a69..a325840b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: run: |- mkdir -p ../build npm install - export ROOT_PATH=../spec/opensearch-openapi.yaml + export ROOT_PATH=../spec export OUTPUT_PATH=../build/opensearch-openapi.yaml npm run merge -- $ROOT_PATH $OUTPUT_PATH diff --git a/.github/workflows/coverage-comment.yml b/.github/workflows/coverage-comment.yml new file mode 100644 index 000000000..959d7a328 --- /dev/null +++ b/.github/workflows/coverage-comment.yml @@ -0,0 +1,36 @@ +name: Comment with API Coverage + +on: + workflow_run: + workflows: ["Gather API Coverage"] + types: + - completed + +jobs: + comment: + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: Download Coverage Report + uses: actions/download-artifact@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + name: coverage + run-id: ${{ github.event.workflow_run.id }} + + - name: 'Comment on PR' + uses: actions/github-script@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const data = JSON.parse(fs.readFileSync('./coverage.json')); + console.log(data); + await github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: data.pull_request, + body: `API specs implemented for ${data.current}/${data.total} (${data.percent}%) APIs.` + }); \ No newline at end of file diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage-gather.yml similarity index 76% rename from .github/workflows/coverage.yml rename to .github/workflows/coverage-gather.yml index 4ad04baf0..d3b378b90 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage-gather.yml @@ -1,6 +1,6 @@ -name: API Coverage +name: Gather API Coverage -on: [push, pull_request_target] +on: [push, pull_request] env: JAVA_VERSION: 11 @@ -14,14 +14,12 @@ jobs: steps: - name: Checkout Repo uses: actions/checkout@v2 - with: - ref: ${{ github.event.pull_request.head.sha }} - name: Build Spec working-directory: ./tools run: |- mkdir -p ../build npm install - export ROOT_PATH=../spec/opensearch-openapi.yaml + export ROOT_PATH=../spec export OUTPUT_PATH=../build/opensearch-openapi.yaml npm run merge -- $ROOT_PATH $OUTPUT_PATH - name: Build and Run Docker Container @@ -50,14 +48,15 @@ jobs: total=`jq -r '.paths | keys | length' build/local-openapi.json` percent=$((current * 100 / total)) echo "API specs implemented for $current/$total ($percent%) APIs." - cat >>"$GITHUB_OUTPUT" <>"coverage.json" <` category holds the common schemas of a specific sub_category. (e.g. `_common.mapping`) - `._common` category holds the common schemas of a specific namespace. (e.g. `cat._common`, `_core._common`) - `.` category holds the schemas of a specific sub_category of a namespace. (e.g. `cat.aliases`, `_core.search`) +## Superseded Operations + +When an operation is superseded by another operation with **identical functionality**, that is a rename or a change in the URL, it should be listed in [_superseded_operations.yaml](./spec/_superseded_operations.yaml) file. The merger tool will automatically generate the superseded operation in the OpenAPI spec. The superseded operation will have `deprecated: true` and `x-ignorable: true` properties to indicate that it should be ignored by the client generator. + +For example, if the `_superseded_operations.yaml` file contains the following entry: +```yaml +/_opendistro/_anomaly_detection/{nodeId}/stats/{stat}: + superseded_by: /_plugins/_anomaly_detection/{nodeId}/stats/{stat} + operations: + - GET + - POST +``` +Then, the merger tool will generate 2 superseded operations: +- `GET /_opendistro/_anomaly_detection/{nodeId}/stats/{stat}` +- `POST /_opendistro/_anomaly_detection/{nodeId}/stats/{stat}` + +from their respective superseding operations: + +- `GET /_plugins/_anomaly_detection/{nodeId}/stats/{stat}` +- `POST /_plugins/_anomaly_detection/{nodeId}/stats/{stat}` + +if and only if the superseding operations exist in the spec. A warning will be printed on the console if they do not. + +Note that the path parameter names do not need to match. So, if the actual superseding operations have path of `/_plugins/_anomaly_detection/{node_id}/stats/{stat_id}`, the merger tool will recognize that it is the same as `/_plugins/_anomaly_detection/{nodeId}/stats/{stat}` and generate the superseded operations accordingly with the correct path parameter names. + +## Global Parameters +Certain query parameters are global, and they are accepted by every operation. These parameters are listed in the [spec/_global_parameters.yaml](spec/_global_parameters.yaml). The merger tool will automatically add these parameters to all operations. + ## OpenAPI Extensions -This repository includes several penAPI Specification Extensions to fill in any metadata not directly supported OpenAPI: +This repository includes several OpenAPI Specification Extensions to fill in any metadata not natively supported by OpenAPI: - `x-operation-group`: Used to group operations into API actions. - `x-version-added`: OpenSearch version when the operation/parameter was added. @@ -81,8 +113,17 @@ This repository includes several penAPI Specification Extensions to fill in any - `x-version-removed`: OpenSearch version when the operation/parameter was removed. - `x-deprecation-message`: Reason for deprecation and guidance on how to prepare for the next major version. - `x-ignorable`: Denotes that the operation should be ignored by the client generator. This is used in operation groups where some operations have been replaced by newer ones, but we still keep them in the specs because the server still supports them. -- `x-global`: Denotes that the parameter is a global parameter that is included in every operation. These parameters are listed in the [root file](spec/opensearch-openapi.yaml). +- `x-global`: Denotes that the parameter is a global parameter that is included in every operation. These parameters are listed in the [spec/_global_parameters.yaml](spec/_global_parameters.yaml). +- `x-default`: Contains the default value of a parameter. This is often used to override the default value specified in the schema, or to avoid accidentally changing the default value when updating a shared schema. + +## Tools + +We authored a number of tools to merge and lint specs that live in [tools](tools). All tools have tests (run with `npm run test`) and a linter (run with `npm run lint`). + +### Merger + +The spec merger "builds", aka combines all `.yaml` files in a spec folder into a complete OpenAPI spec. A [workflow](./.github/workflows/build.yml) performs this task on the [spec folder](spec) of this repo then publishes the output into [releases](https://github.com/opensearch-project/opensearch-api-specification/releases). -## Linting -We have a linter that validates every `yaml` file in the `./spec` folder to assure that they follow the guidelines we have set. Check out the [Linter](tools/README.md#linter) tool for more information on how to run it locally. Make sure to run the linter before submitting a PR. +### Linter +The spec linter that validates every `.yaml` file in the `./spec` folder to assure that they follow the guidelines we have set. Check out the [Linter README](tools/README.md#spec-linter) for more information on how to run it locally. Make sure to run the linter before submitting a PR. diff --git a/README.md b/README.md index fbc654b9f..79a796175 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ - [Project Resources](#project-resources) - [Code of Conduct](#code-of-conduct) - [Developer Guide](#developer-guide) -- [OpenSearch API Specs](#opensearch-api-specs) +- [Client Generator Guide](#client-generator-guide) +- [Published Spec](#published-spec) - [Security](#security) - [License](#license) - [Copyright](#copyright) @@ -38,10 +39,17 @@ This project has adopted the [Amazon Open Source Code of Conduct](CODE_OF_CONDUC See [DEVELOPER_GUIDE](DEVELOPER_GUIDE.md). -## OpenSearch API Specs +## Client Generator Guide + +See [CLIENT_GENERATOR_GUIDE](CLIENT_GENERATOR_GUIDE.md). + +## Published Spec OpenSearch API Specs are hosted at https://opensearch-project.github.io/opensearch-api-specification/. See [PUBLISHING_SPECS](PUBLISHING_SPECS.md) for more information. +Click [here](https://github.com/opensearch-project/opensearch-api-specification/releases/download/main/opensearch-openapi.yaml) to download the latest OpenSearch OpenAPI yaml file. + + ## Security If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. Please do **not** create a public GitHub issue. diff --git a/_plugins/openapi.rb b/_plugins/openapi.rb index 82bae554f..23a612b27 100644 --- a/_plugins/openapi.rb +++ b/_plugins/openapi.rb @@ -4,7 +4,7 @@ def self.generate(_site, _payload) Dir.chdir('tools') do system 'npm install' - system 'npm run merge -- ../spec/opensearch-openapi.yaml ../_site/opensearch-openapi.yaml' + system 'npm run merge -- ../spec ../_site/opensearch-openapi.yaml' end @generated = true diff --git a/coverage/README.md b/coverage/README.md index 53babbecb..89ae4e76b 100644 --- a/coverage/README.md +++ b/coverage/README.md @@ -2,4 +2,4 @@ Builds the OpenAPI spec, and uses the [opensearch-api plugin](https://github.com/dblock/opensearch-api) and [openapi-diff](https://github.com/OpenAPITools/openapi-diff) to show the differences. -API coverage is run on all pull requests via the [coverage workflow](../.github/workflows/coverage.yml). +API coverage is run on all pull requests via the [coverage workflow](../.github/workflows/coverage-gather.yml). diff --git a/json_schemas/_info.schema.yaml b/json_schemas/_info.schema.yaml new file mode 100644 index 000000000..77a2257b7 --- /dev/null +++ b/json_schemas/_info.schema.yaml @@ -0,0 +1,44 @@ +$schema: http://json-schema.org/draft-07/schema# + +type: object +properties: + title: + type: string + summary: + type: string + description: + type: string + termsOfService: + type: string + format: uri + contact: + $comment: https://spec.openapis.org/oas/v3.1.0#contact-object + type: object + properties: + name: + type: string + url: + type: string + format: uri + email: + type: string + format: email + license: + $comment: https://spec.openapis.org/oas/v3.1.0#license-object + type: object + properties: + name: + type: string + identifier: + type: string + url: + type: string + format: uri + required: + - name + version: + type: string +required: + - title + - version + - $schema \ No newline at end of file diff --git a/json_schemas/_superseded_operations.schema.yaml b/json_schemas/_superseded_operations.schema.yaml new file mode 100644 index 000000000..a49f6671e --- /dev/null +++ b/json_schemas/_superseded_operations.schema.yaml @@ -0,0 +1,21 @@ +$schema: http://json-schema.org/draft-07/schema# + +type: object +patternProperties: + ^\$schema$: + type: string + ^/: + type: object + properties: + superseded_by: + type: string + pattern: ^/ + operations: + type: array + items: + type: string + enum: [GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH] + required: [superseded_by, operations] + additionalProperties: false +required: [$schema] +additionalProperties: false \ No newline at end of file diff --git a/spec/_global_parameters.yaml b/spec/_global_parameters.yaml new file mode 100644 index 000000000..e1f1b5eef --- /dev/null +++ b/spec/_global_parameters.yaml @@ -0,0 +1,43 @@ +openapi: 3.1.0 +info: + title: '' + version: '' +components: + parameters: + pretty: + name: pretty + in: query + description: Whether to pretty format the returned JSON response. + schema: + type: boolean + default: false + human: + name: human + in: query + description: Whether to return human readable values for statistics. + schema: + type: boolean + default: true + error_trace: + name: error_trace + in: query + description: Whether to include the stack trace of returned errors. + schema: + type: boolean + default: false + source: + name: source + in: query + description: The URL-encoded request definition. Useful for libraries that do not accept a request body for non-POST requests. + schema: + type: string + filter_path: + name: filter_path + in: query + description: Comma-separated list of filters used to reduce the response. + schema: + oneOf: + - type: string + - type: array + items: + type: string \ No newline at end of file diff --git a/spec/_info.yaml b/spec/_info.yaml new file mode 100644 index 000000000..f71d35dd0 --- /dev/null +++ b/spec/_info.yaml @@ -0,0 +1,4 @@ +$schema: ../json_schemas/_info.schema.yaml + +title: OpenSearch API Specification +version: 1.0.0 \ No newline at end of file diff --git a/spec/_superseded_operations.yaml b/spec/_superseded_operations.yaml new file mode 100644 index 000000000..c5180f076 --- /dev/null +++ b/spec/_superseded_operations.yaml @@ -0,0 +1,568 @@ +$schema: ../json_schemas/_superseded_operations.schema.yaml + +/_opendistro/_alerting/destinations: + superseded_by: /_plugins/_alerting/destinations + operations: + - GET +/_opendistro/_alerting/destinations/email_accounts/_search: + superseded_by: /_plugins/_alerting/destinations/email_accounts/_search + operations: + - GET + - POST +/_opendistro/_alerting/destinations/email_accounts/{emailAccountID}: + superseded_by: /_plugins/_alerting/destinations/email_accounts/{emailAccountID} + operations: + - GET + - HEAD +/_opendistro/_alerting/destinations/email_groups/_search: + superseded_by: /_plugins/_alerting/destinations/email_groups/_search + operations: + - GET + - POST +/_opendistro/_alerting/destinations/email_groups/{emailGroupID}: + superseded_by: /_plugins/_alerting/destinations/email_groups/{emailGroupID} + operations: + - GET + - HEAD +/_opendistro/_alerting/destinations/{destinationID}: + superseded_by: /_plugins/_alerting/destinations/{destinationID} + operations: + - GET +/_opendistro/_alerting/monitors: + superseded_by: /_plugins/_alerting/monitors + operations: + - POST +/_opendistro/_alerting/monitors/_execute: + superseded_by: /_plugins/_alerting/monitors/_execute + operations: + - POST +/_opendistro/_alerting/monitors/_search: + superseded_by: /_plugins/_alerting/monitors/_search + operations: + - GET + - POST +/_opendistro/_alerting/monitors/alerts: + superseded_by: /_plugins/_alerting/monitors/alerts + operations: + - GET +/_opendistro/_alerting/monitors/{monitorID}: + superseded_by: /_plugins/_alerting/monitors/{monitorID} + operations: + - GET + - PUT + - DELETE + - HEAD +/_opendistro/_alerting/monitors/{monitorID}/_acknowledge/alerts: + superseded_by: /_plugins/_alerting/monitors/{monitorID}/_acknowledge/alerts + operations: + - POST +/_opendistro/_alerting/monitors/{monitorID}/_execute: + superseded_by: /_plugins/_alerting/monitors/{monitorID}/_execute + operations: + - POST +/_opendistro/_alerting/stats/: + superseded_by: /_plugins/_alerting/stats/ + operations: + - GET +/_opendistro/_alerting/stats/{metric}: + superseded_by: /_plugins/_alerting/stats/{metric} + operations: + - GET +/_opendistro/_alerting/{nodeId}/stats/: + superseded_by: /_plugins/_alerting/{nodeId}/stats/ + operations: + - GET +/_opendistro/_alerting/{nodeId}/stats/{metric}: + superseded_by: /_plugins/_alerting/{nodeId}/stats/{metric} + operations: + - GET +/_opendistro/_anomaly_detection/detectors: + superseded_by: /_plugins/_anomaly_detection/detectors + operations: + - POST +/_opendistro/_anomaly_detection/detectors/_search: + superseded_by: /_plugins/_anomaly_detection/detectors/_search + operations: + - GET + - POST +/_opendistro/_anomaly_detection/detectors/count: + superseded_by: /_plugins/_anomaly_detection/detectors/count + operations: + - GET +/_opendistro/_anomaly_detection/detectors/match: + superseded_by: /_plugins/_anomaly_detection/detectors/match + operations: + - GET +/_opendistro/_anomaly_detection/detectors/results/_search: + superseded_by: /_plugins/_anomaly_detection/detectors/results/_search + operations: + - GET + - POST +/_opendistro/_anomaly_detection/detectors/tasks/_search: + superseded_by: /_plugins/_anomaly_detection/detectors/tasks/_search + operations: + - GET + - POST +/_opendistro/_anomaly_detection/detectors/{detectorID}: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID} + operations: + - GET + - PUT + - DELETE + - HEAD +/_opendistro/_anomaly_detection/detectors/{detectorID}/_preview: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID}/_preview + operations: + - POST +/_opendistro/_anomaly_detection/detectors/{detectorID}/_profile: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID}/_profile + operations: + - GET +/_opendistro/_anomaly_detection/detectors/{detectorID}/_profile/{type}: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID}/_profile/{type} + operations: + - GET +/_opendistro/_anomaly_detection/detectors/{detectorID}/_run: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID}/_run + operations: + - POST +/_opendistro/_anomaly_detection/detectors/{detectorID}/_start: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID}/_start + operations: + - POST +/_opendistro/_anomaly_detection/detectors/{detectorID}/_stop: + superseded_by: /_plugins/_anomaly_detection/detectors/{detectorID}/_stop + operations: + - POST +/_opendistro/_anomaly_detection/stats/: + superseded_by: /_plugins/_anomaly_detection/stats/ + operations: + - GET +/_opendistro/_anomaly_detection/stats/{stat}: + superseded_by: /_plugins/_anomaly_detection/stats/{stat} + operations: + - GET +/_opendistro/_anomaly_detection/{nodeId}/stats/: + superseded_by: /_plugins/_anomaly_detection/{nodeId}/stats/ + operations: + - GET +/_opendistro/_anomaly_detection/{nodeId}/stats/{stat}: + superseded_by: /_plugins/_anomaly_detection/{nodeId}/stats/{stat} + operations: + - GET +/_opendistro/_asynchronous_search: + superseded_by: /_plugins/_asynchronous_search + operations: + - POST +/_opendistro/_asynchronous_search/_nodes/{nodeId}/stats: + superseded_by: /_plugins/_asynchronous_search/_nodes/{nodeId}/stats + operations: + - GET +/_opendistro/_asynchronous_search/stats: + superseded_by: /_plugins/_asynchronous_search/stats + operations: + - GET +/_opendistro/_asynchronous_search/{id}: + superseded_by: /_plugins/_asynchronous_search/{id} + operations: + - GET + - DELETE +/_opendistro/_ism/add: + superseded_by: /_plugins/_ism/add + operations: + - POST +/_opendistro/_ism/add/{index}: + superseded_by: /_plugins/_ism/add/{index} + operations: + - POST +/_opendistro/_ism/change_policy: + superseded_by: /_plugins/_ism/change_policy + operations: + - POST +/_opendistro/_ism/change_policy/{index}: + superseded_by: /_plugins/_ism/change_policy/{index} + operations: + - POST +/_opendistro/_ism/explain: + superseded_by: /_plugins/_ism/explain + operations: + - GET +/_opendistro/_ism/explain/{index}: + superseded_by: /_plugins/_ism/explain/{index} + operations: + - GET +/_opendistro/_ism/policies: + superseded_by: /_plugins/_ism/policies + operations: + - GET + - PUT +/_opendistro/_ism/policies/{policyID}: + superseded_by: /_plugins/_ism/policies/{policyID} + operations: + - GET + - PUT + - DELETE + - HEAD +/_opendistro/_ism/remove: + superseded_by: /_plugins/_ism/remove + operations: + - POST +/_opendistro/_ism/remove/{index}: + superseded_by: /_plugins/_ism/remove/{index} + operations: + - POST +/_opendistro/_ism/retry: + superseded_by: /_plugins/_ism/retry + operations: + - POST +/_opendistro/_ism/retry/{index}: + superseded_by: /_plugins/_ism/retry/{index} + operations: + - POST +/_opendistro/_knn/stats/: + superseded_by: /_plugins/_knn/stats/ + operations: + - GET +/_opendistro/_knn/stats/{stat}: + superseded_by: /_plugins/_knn/stats/{stat} + operations: + - GET +/_opendistro/_knn/warmup/{index}: + superseded_by: /_plugins/_knn/warmup/{index} + operations: + - GET +/_opendistro/_knn/{nodeId}/stats/: + superseded_by: /_plugins/_knn/{nodeId}/stats/ + operations: + - GET +/_opendistro/_knn/{nodeId}/stats/{stat}: + superseded_by: /_plugins/_knn/{nodeId}/stats/{stat} + operations: + - GET +/_opendistro/_performanceanalyzer/_agent/{redirectEndpoint}: + superseded_by: /_plugins/_performanceanalyzer/_agent/{redirectEndpoint} + operations: + - GET +/_opendistro/_performanceanalyzer/batch/cluster/config: + superseded_by: /_plugins/_performanceanalyzer/batch/cluster/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/batch/config: + superseded_by: /_plugins/_performanceanalyzer/batch/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/cluster/config: + superseded_by: /_plugins/_performanceanalyzer/cluster/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/config: + superseded_by: /_plugins/_performanceanalyzer/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/logging/cluster/config: + superseded_by: /_plugins/_performanceanalyzer/logging/cluster/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/logging/config: + superseded_by: /_plugins/_performanceanalyzer/logging/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/override/cluster/config: + superseded_by: /_plugins/_performanceanalyzer/override/cluster/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/rca/cluster/config: + superseded_by: /_plugins/_performanceanalyzer/rca/cluster/config + operations: + - GET + - POST +/_opendistro/_performanceanalyzer/rca/config: + superseded_by: /_plugins/_performanceanalyzer/rca/config + operations: + - GET + - POST +/_opendistro/_ppl: + superseded_by: /_plugins/_ppl + operations: + - POST +/_opendistro/_ppl/_explain: + superseded_by: /_plugins/_ppl/_explain + operations: + - POST +/_opendistro/_ppl/stats: + superseded_by: /_plugins/_ppl/stats + operations: + - GET + - POST +/_opendistro/_refresh_search_analyzers: + superseded_by: /_plugins/_refresh_search_analyzers + operations: + - POST +/_opendistro/_refresh_search_analyzers/{index}: + superseded_by: /_plugins/_refresh_search_analyzers/{index} + operations: + - POST +/_opendistro/_reports/_local/stats: + superseded_by: /_plugins/_reports/_local/stats + operations: + - GET +/_opendistro/_reports/definition: + superseded_by: /_plugins/_reports/definition + operations: + - POST +/_opendistro/_reports/definition/{reportDefinitionId}: + superseded_by: /_plugins/_reports/definition/{reportDefinitionId} + operations: + - GET + - PUT + - DELETE +/_opendistro/_reports/definitions: + superseded_by: /_plugins/_reports/definitions + operations: + - GET +/_opendistro/_reports/instance/{reportInstanceId}: + superseded_by: /_plugins/_reports/instance/{reportInstanceId} + operations: + - GET + - POST +/_opendistro/_reports/instances: + superseded_by: /_plugins/_reports/instances + operations: + - GET +/_opendistro/_reports/on_demand: + superseded_by: /_plugins/_reports/on_demand + operations: + - PUT +/_opendistro/_reports/on_demand/{reportDefinitionId}: + superseded_by: /_plugins/_reports/on_demand/{reportDefinitionId} + operations: + - POST +/_opendistro/_rollup/jobs: + superseded_by: /_plugins/_rollup/jobs + operations: + - GET + - PUT +/_opendistro/_rollup/jobs/{rollupID}: + superseded_by: /_plugins/_rollup/jobs/{rollupID} + operations: + - GET + - PUT + - DELETE + - HEAD +/_opendistro/_rollup/jobs/{rollupID}/_explain: + superseded_by: /_plugins/_rollup/jobs/{rollupID}/_explain + operations: + - GET +/_opendistro/_rollup/jobs/{rollupID}/_start: + superseded_by: /_plugins/_rollup/jobs/{rollupID}/_start + operations: + - POST +/_opendistro/_rollup/jobs/{rollupID}/_stop: + superseded_by: /_plugins/_rollup/jobs/{rollupID}/_stop + operations: + - POST +/_opendistro/_security/api/account: + superseded_by: /_plugins/_security/api/account + operations: + - GET + - PUT +/_opendistro/_security/api/actiongroup/: + superseded_by: /_plugins/_security/api/actiongroup/ + operations: + - GET +/_opendistro/_security/api/actiongroup/{name}: + superseded_by: /_plugins/_security/api/actiongroup/{name} + operations: + - GET + - PUT + - DELETE +/_opendistro/_security/api/actiongroups/: + superseded_by: /_plugins/_security/api/actiongroups/ + operations: + - GET + - PATCH +/_opendistro/_security/api/actiongroups/{name}: + superseded_by: /_plugins/_security/api/actiongroups/{name} + operations: + - GET + - PUT + - DELETE + - PATCH +/_opendistro/_security/api/audit/: + superseded_by: /_plugins/_security/api/audit/ + operations: + - GET + - PATCH +/_opendistro/_security/api/audit/config: + superseded_by: /_plugins/_security/api/audit/config + operations: + - PUT +/_opendistro/_security/api/authtoken: + superseded_by: /_plugins/_security/api/authtoken + operations: + - POST +/_opendistro/_security/api/cache: + superseded_by: /_plugins/_security/api/cache + operations: + - GET + - PUT + - POST + - DELETE +/_opendistro/_security/api/internalusers/: + superseded_by: /_plugins/_security/api/internalusers/ + operations: + - GET + - PATCH +/_opendistro/_security/api/internalusers/{name}: + superseded_by: /_plugins/_security/api/internalusers/{name} + operations: + - GET + - PUT + - DELETE + - PATCH +/_opendistro/_security/api/internalusers/{name}/authtoken: + superseded_by: /_plugins/_security/api/internalusers/{name}/authtoken + operations: + - POST +/_opendistro/_security/api/migrate: + superseded_by: /_plugins/_security/api/migrate + operations: + - POST +/_opendistro/_security/api/permissionsinfo: + superseded_by: /_plugins/_security/api/permissionsinfo + operations: + - GET +/_opendistro/_security/api/roles/: + superseded_by: /_plugins/_security/api/roles/ + operations: + - GET + - PATCH +/_opendistro/_security/api/roles/{name}: + superseded_by: /_plugins/_security/api/roles/{name} + operations: + - GET + - PUT + - DELETE + - PATCH +/_opendistro/_security/api/rolesmapping/: + superseded_by: /_plugins/_security/api/rolesmapping/ + operations: + - GET + - PATCH +/_opendistro/_security/api/rolesmapping/{name}: + superseded_by: /_plugins/_security/api/rolesmapping/{name} + operations: + - GET + - PUT + - DELETE + - PATCH +/_opendistro/_security/api/securityconfig: + superseded_by: /_plugins/_security/api/securityconfig + operations: + - GET + - PATCH +/_opendistro/_security/api/securityconfig/config: + superseded_by: /_plugins/_security/api/securityconfig/config + operations: + - PUT +/_opendistro/_security/api/ssl/certs: + superseded_by: /_plugins/_security/api/ssl/certs + operations: + - GET +/_opendistro/_security/api/ssl/{certType}/reloadcerts/: + superseded_by: /_plugins/_security/api/ssl/{certType}/reloadcerts/ + operations: + - PUT +/_opendistro/_security/api/tenancy/config: + superseded_by: /_plugins/_security/api/tenancy/config + operations: + - GET + - PUT +/_opendistro/_security/api/tenants/: + superseded_by: /_plugins/_security/api/tenants/ + operations: + - GET + - PATCH +/_opendistro/_security/api/tenants/{name}: + superseded_by: /_plugins/_security/api/tenants/{name} + operations: + - GET + - PUT + - DELETE + - PATCH +/_opendistro/_security/api/user/: + superseded_by: /_plugins/_security/api/user/ + operations: + - GET +/_opendistro/_security/api/user/{name}: + superseded_by: /_plugins/_security/api/user/{name} + operations: + - GET + - PUT + - DELETE +/_opendistro/_security/api/user/{name}/authtoken: + superseded_by: /_plugins/_security/api/user/{name}/authtoken + operations: + - POST +/_opendistro/_security/api/validate: + superseded_by: /_plugins/_security/api/validate + operations: + - GET +/_opendistro/_security/api/whitelist: + superseded_by: /_plugins/_security/api/whitelist + operations: + - GET + - PUT + - PATCH +/_opendistro/_security/authinfo: + superseded_by: /_plugins/_security/authinfo + operations: + - GET + - POST +/_opendistro/_security/health: + superseded_by: /_plugins/_security/health + operations: + - GET + - POST +/_opendistro/_security/kibanainfo: + superseded_by: /_plugins/_security/kibanainfo + operations: + - GET + - POST +/_opendistro/_security/sslinfo: + superseded_by: /_plugins/_security/sslinfo + operations: + - GET +/_opendistro/_security/tenantinfo: + superseded_by: /_plugins/_security/tenantinfo + operations: + - GET + - POST +/_opendistro/_sql: + superseded_by: /_plugins/_sql + operations: + - POST +/_opendistro/_sql/_explain: + superseded_by: /_plugins/_sql/_explain + operations: + - POST +/_opendistro/_sql/close: + superseded_by: /_plugins/_sql/close + operations: + - POST +/_opendistro/_sql/settings: + superseded_by: /_plugins/_sql/settings + operations: + - PUT +/_opendistro/_sql/stats: + superseded_by: /_plugins/_sql/stats + operations: + - GET + - POST diff --git a/spec/namespaces/_core.yaml b/spec/namespaces/_core.yaml index f20320723..baa2f43e0 100644 --- a/spec/namespaces/_core.yaml +++ b/spec/namespaces/_core.yaml @@ -2110,7 +2110,7 @@ components: requestBodies: bulk: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -2119,7 +2119,6 @@ components: - $ref: '../schemas/_core.bulk.yaml#/components/schemas/UpdateAction' - type: object description: The operation definition and data (action-data pairs), separated by newlines - x-serialize: bulk required: true clear_scroll: content: @@ -2220,23 +2219,21 @@ components: required: true msearch: content: - application/json: + application/x-ndjson: schema: type: array items: $ref: '../schemas/_core.msearch.yaml#/components/schemas/RequestItem' description: The request definitions (metadata-search request definition pairs), separated by newlines - x-serialize: bulk required: true msearch_template: content: - application/json: + application/x-ndjson: schema: type: array items: $ref: '../schemas/_core.msearch_template.yaml#/components/schemas/RequestItem' description: The request definitions (metadata-search request definition pairs), separated by newlines - x-serialize: bulk required: true mtermvectors: content: @@ -3277,6 +3274,7 @@ components: description: If `true`, the request’s actions must target an index alias. schema: type: boolean + default: false style: form bulk::query.routing: in: query @@ -3308,6 +3306,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' clear_scroll::path.scroll_id: in: path name: scroll_id @@ -3346,6 +3345,7 @@ components: This parameter can only be used when the `q` query string parameter is specified. schema: type: boolean + default: false style: form count::query.analyzer: in: query @@ -3420,6 +3420,7 @@ components: Random by default. schema: type: string + default: random style: form count::query.q: in: query @@ -3522,6 +3523,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' create_pit::path.index: name: index in: path @@ -3648,6 +3650,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' delete_by_query::path.index: in: path name: index @@ -3708,6 +3711,7 @@ components: description: If `true`, wildcard and prefix queries are analyzed. schema: type: boolean + default: false style: form delete_by_query::query.analyzer: in: query @@ -3750,9 +3754,10 @@ components: delete_by_query::query.from: in: query name: from - description: 'Starting offset (default: 0)' + description: Starting offset. schema: type: number + default: 0 style: form delete_by_query::query.ignore_unavailable: in: query @@ -3785,6 +3790,7 @@ components: Random by default. schema: type: string + default: random style: form delete_by_query::query.q: in: query @@ -3815,6 +3821,7 @@ components: description: The throttle for this request in sub-requests per second. schema: type: number + default: 0 style: form delete_by_query::query.routing: in: query @@ -3836,6 +3843,7 @@ components: description: Size of the scroll request that powers the operation. schema: type: number + default: 100 style: form delete_by_query::query.search_timeout: in: query @@ -3870,6 +3878,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/Slices' style: form + x-default: '1' delete_by_query::query.sort: in: query name: sort @@ -3909,6 +3918,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' style: form + x-default: 1m delete_by_query::query.version: in: query name: version @@ -3925,12 +3935,14 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' delete_by_query::query.wait_for_completion: in: query name: wait_for_completion description: If `true`, the request blocks until the operation is complete. schema: type: boolean + default: true style: form delete_by_query_rethrottle::path.task_id: in: path @@ -4030,6 +4042,7 @@ components: Random by default. schema: type: string + default: random style: form exists::query.realtime: in: query @@ -4125,6 +4138,7 @@ components: Random by default. schema: type: string + default: random style: form exists_source::query.realtime: in: query @@ -4208,6 +4222,7 @@ components: description: If `true`, wildcard and prefix queries are analyzed. schema: type: boolean + default: false style: form explain::query.analyzer: in: query @@ -4231,6 +4246,7 @@ components: description: Field to use as default where no field prefix is given in the query string. schema: type: string + default: _all style: form explain::query.lenient: in: query @@ -4247,6 +4263,7 @@ components: Random by default. schema: type: string + default: random style: form explain::query.q: in: query @@ -4314,6 +4331,7 @@ components: description: If true, unmapped fields are included in the response. schema: type: boolean + default: false style: form get::path.id: in: path @@ -4358,6 +4376,7 @@ components: description: Specifies the node or shard the operation should be performed on. Random by default. schema: type: string + default: random style: form get::query.realtime: in: query @@ -4472,6 +4491,7 @@ components: description: Specifies the node or shard the operation should be performed on. Random by default. schema: type: string + default: random style: form get_source::query.realtime: in: query @@ -4576,6 +4596,7 @@ components: description: If `true`, the destination must be an index alias. schema: type: boolean + default: false style: form index::query.routing: in: query @@ -4616,6 +4637,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' mget::path.index: in: path name: index @@ -4656,6 +4678,7 @@ components: description: Specifies the node or shard the operation should be performed on. Random by default. schema: type: string + default: random style: form mget::query.realtime: in: query @@ -4699,6 +4722,7 @@ components: description: If true, network roundtrips between the coordinating node and remote clusters are minimized for cross-cluster search requests. schema: type: boolean + default: true style: form msearch::query.max_concurrent_searches: in: query @@ -4713,6 +4737,7 @@ components: description: Maximum number of concurrent shard requests that each sub-search request executes per node. schema: type: number + default: 5 style: form msearch::query.pre_filter_shard_size: in: query @@ -4727,6 +4752,7 @@ components: description: If true, hits.total are returned as an integer in the response. Defaults to false, which returns an object. schema: type: boolean + default: false style: form msearch::query.search_type: in: query @@ -4759,6 +4785,7 @@ components: description: If `true`, network round-trips are minimized for cross-cluster search requests. schema: type: boolean + default: true style: form msearch_template::query.max_concurrent_searches: in: query @@ -4775,6 +4802,7 @@ components: If `false`, it returns `hits.total` as an object. schema: type: boolean + default: false style: form msearch_template::query.search_type: in: query @@ -4806,6 +4834,7 @@ components: description: If `true`, the response includes the document count, sum of document frequencies, and sum of total term frequencies. schema: type: boolean + default: true style: form mtermvectors::query.fields: in: query @@ -4831,6 +4860,7 @@ components: description: If `true`, the response includes term offsets. schema: type: boolean + default: true style: form mtermvectors::query.payloads: in: query @@ -4838,6 +4868,7 @@ components: description: If `true`, the response includes term payloads. schema: type: boolean + default: true style: form mtermvectors::query.positions: in: query @@ -4845,6 +4876,7 @@ components: description: If `true`, the response includes term positions. schema: type: boolean + default: true style: form mtermvectors::query.preference: in: query @@ -4854,6 +4886,7 @@ components: Random by default. schema: type: string + default: random style: form mtermvectors::query.realtime: in: query @@ -4861,6 +4894,7 @@ components: description: If true, the request is real-time as opposed to near-real-time. schema: type: boolean + default: true style: form mtermvectors::query.routing: in: query @@ -4875,6 +4909,7 @@ components: description: If true, the response includes term frequency and document frequency. schema: type: boolean + default: false style: form mtermvectors::query.version: in: query @@ -4979,10 +5014,10 @@ components: reindex::query.max_docs: name: max_docs in: query - description: 'Maximum number of documents to process (default: all documents).' + description: Maximum number of documents to process. By default, all documents. schema: type: integer - description: 'Maximum number of documents to process (default: all documents).' + description: Maximum number of documents to process. By default, all documents. format: int32 reindex::query.refresh: in: query @@ -4999,6 +5034,7 @@ components: Defaults to no throttle. schema: type: number + default: 0 style: form reindex::query.scroll: in: query @@ -5016,6 +5052,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/Slices' style: form + x-default: '1' reindex::query.timeout: in: query name: timeout @@ -5023,6 +5060,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' style: form + x-default: 1m reindex::query.wait_for_active_shards: in: query name: wait_for_active_shards @@ -5032,12 +5070,14 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' reindex::query.wait_for_completion: in: query name: wait_for_completion description: If `true`, the request blocks until the operation is complete. schema: type: boolean + default: true style: form reindex_rethrottle::path.task_id: in: path @@ -5079,6 +5119,7 @@ components: description: If true, the API response’s hit.total property is returned as an integer. If false, the API response’s hit.total property is returned as an object. schema: type: boolean + default: false style: form scroll::query.scroll: in: query @@ -5156,6 +5197,7 @@ components: description: If true, returns partial results if there are shard request timeouts or shard failures. If false, returns an error with no partial results. schema: type: boolean + default: true style: form search::query.analyze_wildcard: in: query @@ -5165,6 +5207,7 @@ components: This parameter can only be used when the q query string parameter is specified. schema: type: boolean + default: false style: form search::query.analyzer: in: query @@ -5183,6 +5226,7 @@ components: This value should be used as a protection mechanism to reduce the memory overhead per search request if the potential number of shards in the request can be large. schema: type: number + default: 512 style: form search::query.ccs_minimize_roundtrips: in: query @@ -5190,6 +5234,7 @@ components: description: If true, network round-trips between the coordinating node and the remote clusters are minimized when executing cross-cluster search (CCS) requests. schema: type: boolean + default: true style: form search::query.default_operator: in: query @@ -5243,6 +5288,7 @@ components: To page through more hits, use the `search_after` parameter. schema: type: number + default: 0 style: form search::query.ignore_throttled: in: query @@ -5283,6 +5329,7 @@ components: This value should be used to limit the impact of the search on the cluster in order to limit the number of concurrent shard requests. schema: type: number + default: 5 style: form search::query.pre_filter_shard_size: in: query @@ -5311,6 +5358,7 @@ components: `` (any string that does not start with `_`) to route searches with the same `` to the same shards in the same order. schema: type: string + default: random style: form search::query.q: in: query @@ -5336,6 +5384,7 @@ components: description: Indicates whether `hits.total` should be rendered as an integer or an object in the rest search response. schema: type: boolean + default: false style: form search::query.routing: in: query @@ -5384,6 +5433,7 @@ components: To page through more hits, use the `search_after` parameter. schema: type: number + default: 10 style: form search::query.sort: in: query @@ -5547,6 +5597,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form search_shards::query.preference: in: query @@ -5556,6 +5607,7 @@ components: Random by default. schema: type: string + default: random style: form search_shards::query.routing: in: query @@ -5590,6 +5642,7 @@ components: description: If `true`, network round-trips are minimized for cross-cluster search requests. schema: type: boolean + default: true style: form search_template::query.expand_wildcards: in: query @@ -5631,6 +5684,7 @@ components: Random by default. schema: type: string + default: random style: form search_template::query.profile: in: query @@ -5645,6 +5699,7 @@ components: description: If true, hits.total are rendered as an integer in the response. schema: type: boolean + default: false style: form search_template::query.routing: in: query @@ -5698,6 +5753,7 @@ components: description: If `true`, the response includes the document count, sum of document frequencies, and sum of total term frequencies. schema: type: boolean + default: true style: form termvectors::query.fields: in: query @@ -5714,6 +5770,7 @@ components: description: If `true`, the response includes term offsets. schema: type: boolean + default: true style: form termvectors::query.payloads: in: query @@ -5721,6 +5778,7 @@ components: description: If `true`, the response includes term payloads. schema: type: boolean + default: true style: form termvectors::query.positions: in: query @@ -5728,6 +5786,7 @@ components: description: If `true`, the response includes term positions. schema: type: boolean + default: true style: form termvectors::query.preference: in: query @@ -5737,6 +5796,7 @@ components: Random by default. schema: type: string + default: random style: form termvectors::query.realtime: in: query @@ -5744,6 +5804,7 @@ components: description: If true, the request is real-time as opposed to near-real-time. schema: type: boolean + default: true style: form termvectors::query.routing: in: query @@ -5758,6 +5819,7 @@ components: description: If `true`, the response includes term frequency and document frequency. schema: type: boolean + default: false style: form termvectors::query.version: in: query @@ -5832,6 +5894,7 @@ components: description: The script language. schema: type: string + default: painless style: form update::query.refresh: in: query @@ -5849,6 +5912,7 @@ components: description: If true, the destination must be an index alias. schema: type: boolean + default: false style: form update::query.retry_on_conflict: in: query @@ -5856,6 +5920,7 @@ components: description: Specify how many times should the operation be retried when a conflict occurs. schema: type: number + default: 0 style: form update::query.routing: in: query @@ -5884,6 +5949,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' update_by_query::path.index: in: path name: index @@ -5944,6 +6010,7 @@ components: description: If `true`, wildcard and prefix queries are analyzed. schema: type: boolean + default: false style: form update_by_query::query.analyzer: in: query @@ -5987,9 +6054,10 @@ components: update_by_query::query.from: in: query name: from - description: 'Starting offset (default: 0)' + description: Starting offset. schema: type: number + default: 0 style: form update_by_query::query.ignore_unavailable: in: query @@ -6032,6 +6100,7 @@ components: Random by default. schema: type: string + default: random style: form update_by_query::query.q: name: q @@ -6060,6 +6129,7 @@ components: description: The throttle for this request in sub-requests per second. schema: type: number + default: 0 style: form update_by_query::query.routing: in: query @@ -6081,6 +6151,7 @@ components: description: Size of the scroll request that powers the operation. schema: type: number + default: 100 style: form update_by_query::query.search_timeout: in: query @@ -6111,6 +6182,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/Slices' style: form + x-default: '1' update_by_query::query.sort: in: query name: sort @@ -6150,6 +6222,7 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' style: form + x-default: 1m update_by_query::query.version: in: query name: version @@ -6166,12 +6239,14 @@ components: schema: $ref: '../schemas/_common.yaml#/components/schemas/WaitForActiveShards' style: form + x-default: '1' update_by_query::query.wait_for_completion: in: query name: wait_for_completion description: If `true`, the request blocks until the operation is complete. schema: type: boolean + default: true style: form update_by_query_rethrottle::path.task_id: in: path diff --git a/spec/namespaces/cat.yaml b/spec/namespaces/cat.yaml index 552b32cbc..8c808e543 100644 --- a/spec/namespaces/cat.yaml +++ b/spec/namespaces/cat.yaml @@ -1422,6 +1422,7 @@ components: description: If true, returns `HH:MM:SS` and Unix epoch timestamps. schema: type: boolean + default: true style: form cat.health::query.v: name: v @@ -1520,6 +1521,7 @@ components: description: If true, the response includes information from segments that are not loaded into memory. schema: type: boolean + default: false style: form cat.indices::query.local: name: local @@ -1544,6 +1546,7 @@ components: description: If true, the response only includes information from primary shards. schema: type: boolean + default: false style: form cat.indices::query.s: name: s @@ -1738,6 +1741,7 @@ components: oneOf: - type: boolean - type: string + default: false style: form cat.nodes::query.h: name: h @@ -2014,6 +2018,7 @@ components: description: If `true`, the response only includes ongoing shard recoveries. schema: type: boolean + default: false style: form cat.recovery::query.bytes: in: query @@ -2028,6 +2033,7 @@ components: description: If `true`, the response includes detailed information about shard recoveries. schema: type: boolean + default: false style: form cat.recovery::query.format: name: format @@ -2529,6 +2535,7 @@ components: description: If `true`, the response does not include information from unavailable snapshots. schema: type: boolean + default: false style: form cat.snapshots::query.master_timeout: name: master_timeout @@ -2579,6 +2586,7 @@ components: description: If `true`, the response includes detailed information about shard recoveries. schema: type: boolean + default: false style: form cat.tasks::query.format: name: format diff --git a/spec/namespaces/cluster.yaml b/spec/namespaces/cluster.yaml index b99b49203..128c114d5 100644 --- a/spec/namespaces/cluster.yaml +++ b/spec/namespaces/cluster.yaml @@ -762,6 +762,7 @@ components: description: If true, returns information about disk usage and shard sizes. schema: type: boolean + default: false style: form cluster.allocation_explain::query.include_yes_decisions: in: query @@ -769,6 +770,7 @@ components: description: If true, returns YES decisions in explanation. schema: type: boolean + default: false style: form cluster.delete_component_template::path.name: in: path @@ -820,6 +822,7 @@ components: nodes are still in the cluster. schema: type: boolean + default: true style: form cluster.exists_component_template::path.name: in: path @@ -846,6 +849,7 @@ components: Defaults to false, which means information is retrieved from the master node. schema: type: boolean + default: false style: form cluster.exists_component_template::query.master_timeout: in: query @@ -885,6 +889,7 @@ components: If `false`, information is retrieved from the master node. schema: type: boolean + default: false style: form cluster.get_component_template::query.master_timeout: in: query @@ -920,6 +925,7 @@ components: description: If `true`, returns settings in flat format. schema: type: boolean + default: false style: form cluster.get_settings::query.include_defaults: in: query @@ -927,6 +933,7 @@ components: description: If `true`, returns default cluster settings from the local node. schema: type: boolean + default: false style: form cluster.get_settings::query.master_timeout: in: query @@ -961,7 +968,7 @@ components: cluster.health::path.index: in: path name: index - description: Comma-separated list of data streams, indices, and index aliases used to limit the request. Wildcard expressions (*) are supported. To target all data streams and indices in a cluster, omit this parameter or use _all or *. + description: Comma-separated list of data streams, indices, and index aliases used to limit the request. Wildcard expressions (*) are supported. To target all data streams and indices in a cluster, omit this parameter or use `_all` or `*`. required: true schema: $ref: '../schemas/_common.yaml#/components/schemas/Indices' @@ -1000,6 +1007,7 @@ components: description: If true, the request retrieves information from the local node only. Defaults to false, which means information is retrieved from the master node. schema: type: boolean + default: false style: form cluster.health::query.master_timeout: in: query @@ -1077,6 +1085,7 @@ components: If `false`, information is retrieved from the master node. schema: type: boolean + default: false style: form cluster.pending_tasks::query.master_timeout: in: query @@ -1145,6 +1154,7 @@ components: description: If `true`, this request cannot replace or update existing component templates. schema: type: boolean + default: false style: form cluster.put_component_template::query.master_timeout: in: query @@ -1192,9 +1202,10 @@ components: cluster.put_settings::query.flat_settings: in: query name: flat_settings - description: 'Return settings in flat format (default: false)' + description: Return settings in flat format. schema: type: boolean + default: false style: form cluster.put_settings::query.master_timeout: in: query @@ -1316,9 +1327,10 @@ components: cluster.state::query.flat_settings: in: query name: flat_settings - description: 'Return settings in flat format (default: false)' + description: Return settings in flat format. schema: type: boolean + default: false style: form cluster.state::query.ignore_unavailable: in: query @@ -1330,9 +1342,10 @@ components: cluster.state::query.local: in: query name: local - description: 'Return local information, do not retrieve the state from cluster-manager node (default: false)' + description: Return local information, do not retrieve the state from cluster-manager node. schema: type: boolean + default: false style: form cluster.state::query.master_timeout: in: query @@ -1372,6 +1385,7 @@ components: description: If `true`, returns settings in flat format. schema: type: boolean + default: false style: form cluster.stats::query.timeout: in: query diff --git a/spec/namespaces/indices.yaml b/spec/namespaces/indices.yaml index d5f3824f6..bcdb78eff 100644 --- a/spec/namespaces/indices.yaml +++ b/spec/namespaces/indices.yaml @@ -3011,6 +3011,7 @@ components: This behavior applies even if the request targets other open indices. schema: type: boolean + default: false style: form indices.delete::query.cluster_manager_timeout: name: cluster_manager_timeout @@ -3036,6 +3037,7 @@ components: description: If `false`, the request returns an error if it targets a missing or closed index. schema: type: boolean + default: false style: form indices.delete::query.master_timeout: in: query @@ -3200,6 +3202,7 @@ components: This behavior applies even if the request targets other open indices. schema: type: boolean + default: false style: form indices.exists::query.expand_wildcards: in: query @@ -3218,6 +3221,7 @@ components: description: If `true`, returns settings in flat format. schema: type: boolean + default: false style: form indices.exists::query.ignore_unavailable: in: query @@ -3225,6 +3229,7 @@ components: description: If `false`, the request returns an error if it targets a missing or closed index. schema: type: boolean + default: false style: form indices.exists::query.include_defaults: in: query @@ -3232,6 +3237,7 @@ components: description: If `true`, return all default settings in the response. schema: type: boolean + default: false style: form indices.exists::query.local: in: query @@ -3239,6 +3245,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form indices.exists_alias::path.index: in: path @@ -3291,6 +3298,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form indices.exists_index_template::path.name: in: path @@ -3351,16 +3359,18 @@ components: indices.exists_template::query.flat_settings: in: query name: flat_settings - description: 'Return settings in flat format (default: false)' + description: Return settings in flat format. schema: type: boolean + default: false style: form indices.exists_template::query.local: in: query name: local - description: 'Return local information, do not retrieve the state from cluster-manager node (default: false)' + description: Return local information, do not retrieve the state from cluster-manager node. schema: type: boolean + default: false style: form indices.exists_template::query.master_timeout: in: query @@ -3425,6 +3435,7 @@ components: If `false`, Opensearch returns an error if you request a flush when another flush operation is running. schema: type: boolean + default: true style: form indices.forcemerge::path.index: in: path @@ -3451,9 +3462,10 @@ components: indices.forcemerge::query.flush: in: query name: flush - description: 'Specify whether the index should be flushed after performing the operation (default: true)' + description: Specify whether the index should be flushed after performing the operation. schema: type: boolean + default: true style: form indices.forcemerge::query.ignore_unavailable: in: query @@ -3465,7 +3477,10 @@ components: indices.forcemerge::query.max_num_segments: in: query name: max_num_segments - description: 'The number of segments the index should be merged into (default: dynamic)' + description: |- + The number of larger segments into which smaller segments are merged. + Set this parameter to 1 to merge all segments into one segment. + The default behavior is to perform the merge as necessary. schema: type: number style: form @@ -3490,6 +3505,7 @@ components: description: Should the request wait until the force merge is completed. schema: type: boolean + default: true style: form indices.get::path.index: in: path @@ -3510,6 +3526,7 @@ components: a request targeting foo*,bar* returns an error if an index starts with foo but no index starts with bar. schema: type: boolean + default: false style: form indices.get::query.cluster_manager_timeout: name: cluster_manager_timeout @@ -3534,6 +3551,7 @@ components: description: If true, returns settings in flat format. schema: type: boolean + default: false style: form indices.get::query.ignore_unavailable: in: query @@ -3541,6 +3559,7 @@ components: description: If false, requests that target a missing index return an error. schema: type: boolean + default: false style: form indices.get::query.include_defaults: in: query @@ -3548,6 +3567,7 @@ components: description: If true, return all default settings in the response. schema: type: boolean + default: false style: form indices.get::query.local: in: query @@ -3555,6 +3575,7 @@ components: description: If true, the request retrieves information from the local node only. Defaults to false, which means information is retrieved from the master node. schema: type: boolean + default: false style: form indices.get::query.master_timeout: in: query @@ -3621,6 +3642,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form indices.get_data_stream::path.name: in: path @@ -3691,6 +3713,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form indices.get_index_template::path.name: in: path @@ -3713,6 +3736,7 @@ components: description: If true, returns settings in flat format. schema: type: boolean + default: false style: form indices.get_index_template::query.local: in: query @@ -3720,6 +3744,7 @@ components: description: If true, the request retrieves information from the local node only. Defaults to false, which means information is retrieved from the master node. schema: type: boolean + default: false style: form indices.get_index_template::query.master_timeout: in: query @@ -3782,6 +3807,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form indices.get_mapping::query.master_timeout: in: query @@ -3849,6 +3875,7 @@ components: description: If `true`, returns settings in flat format. schema: type: boolean + default: false style: form indices.get_settings::query.ignore_unavailable: in: query @@ -3863,6 +3890,7 @@ components: description: If `true`, return all default settings in the response. schema: type: boolean + default: false style: form indices.get_settings::query.local: in: query @@ -3872,6 +3900,7 @@ components: `false`, information is retrieved from the master node. schema: type: boolean + default: false style: form indices.get_settings::query.master_timeout: in: query @@ -3910,6 +3939,7 @@ components: description: If `true`, returns settings in flat format. schema: type: boolean + default: false style: form indices.get_template::query.local: in: query @@ -3917,6 +3947,7 @@ components: description: If `true`, the request retrieves information from the local node only. schema: type: boolean + default: false style: form indices.get_template::query.master_timeout: in: query @@ -4130,6 +4161,7 @@ components: description: If `true`, this request cannot replace or update existing index templates. schema: type: boolean + default: false style: form indices.put_index_template::query.master_timeout: name: master_timeout @@ -4209,6 +4241,7 @@ components: description: If `true`, the mappings are applied only to the current write index for the target. schema: type: boolean + default: false style: form indices.put_settings::path.index: in: path @@ -4257,11 +4290,12 @@ components: description: If `true`, returns settings in flat format. schema: type: boolean + default: false style: form indices.put_settings::query.ignore_unavailable: in: query name: ignore_unavailable - description: If `true`, returns settings in flat format. + description: Whether specified concrete indices should be ignored when unavailable (missing or closed). schema: type: boolean style: form @@ -4284,6 +4318,7 @@ components: description: If `true`, existing index settings remain unchanged. schema: type: boolean + default: false style: form indices.put_settings::query.timeout: in: query @@ -4315,6 +4350,7 @@ components: description: If true, this request cannot replace or update existing index templates. schema: type: boolean + default: false style: form indices.put_template::query.master_timeout: in: query @@ -4357,6 +4393,7 @@ components: description: If `true`, the response only includes ongoing shard recoveries. schema: type: boolean + default: false style: form indices.recovery::query.detailed: in: query @@ -4364,6 +4401,7 @@ components: description: If `true`, the response includes detailed information about shard recoveries. schema: type: boolean + default: false style: form indices.refresh::path.index: in: path @@ -4456,6 +4494,7 @@ components: description: If `true`, checks whether the current index satisfies the specified conditions but does not perform a rollover. schema: type: boolean + default: false style: form indices.rollover::query.master_timeout: in: query @@ -4531,6 +4570,7 @@ components: description: If `true`, the request returns a verbose response. schema: type: boolean + default: false style: form indices.shard_stores::path.index: in: path @@ -4686,6 +4726,7 @@ components: simulation. schema: type: boolean + default: false style: form indices.simulate_index_template::query.master_timeout: in: query @@ -4730,6 +4771,7 @@ components: description: If true, the template passed in the body is only used if no existing templates match the same index patterns. If false, the simulation uses the template with the highest priority. Note that the template is not permanently added or updated in either case; it is only used for the simulation. schema: type: boolean + default: false style: form indices.simulate_template::query.master_timeout: in: query @@ -4869,6 +4911,7 @@ components: description: If true, statistics are not collected from closed indices. schema: type: boolean + default: true style: form indices.stats::query.groups: in: query @@ -4887,6 +4930,7 @@ components: description: If true, the call reports the aggregated disk usage of each one of the Lucene index files (only applies if segment stats are requested). schema: type: boolean + default: false style: form indices.stats::query.include_unloaded_segments: in: query @@ -4894,6 +4938,7 @@ components: description: If true, the response includes information from segments that are not loaded into memory. schema: type: boolean + default: false style: form indices.stats::query.level: in: query @@ -5008,6 +5053,7 @@ components: description: If `true`, wildcard and prefix queries are analyzed. schema: type: boolean + default: false style: form indices.validate_query::query.analyzer: in: query diff --git a/spec/namespaces/ingest.yaml b/spec/namespaces/ingest.yaml index ebd6e94b0..0eca86715 100644 --- a/spec/namespaces/ingest.yaml +++ b/spec/namespaces/ingest.yaml @@ -343,4 +343,5 @@ components: description: If `true`, the response includes output data for each processor in the executed pipeline. schema: type: boolean + default: false style: form diff --git a/spec/namespaces/nodes.yaml b/spec/namespaces/nodes.yaml index e0ed1f03e..f9e2c21ba 100644 --- a/spec/namespaces/nodes.yaml +++ b/spec/namespaces/nodes.yaml @@ -569,6 +569,7 @@ components: description: If true, returns settings in flat format. schema: type: boolean + default: false style: form nodes.info::query.timeout: in: query @@ -658,6 +659,7 @@ components: description: If true, the call reports the aggregated disk usage of each one of the Lucene index files (only applies if segment stats are requested). schema: type: boolean + default: false style: form nodes.stats::query.level: in: query diff --git a/spec/namespaces/notifications.yaml b/spec/namespaces/notifications.yaml index 5dd403ca4..379735087 100644 --- a/spec/namespaces/notifications.yaml +++ b/spec/namespaces/notifications.yaml @@ -4,6 +4,17 @@ info: description: OpenSearch Notifications API version: 1.0.0 paths: + /_plugins/_notifications/channels: + get: + operationId: notifications.list_channels.0 + x-operation-group: notifications.list_channels + x-version-added: '2.0' + description: List created notification channels. + externalDocs: + url: https://opensearch.org/docs/latest/observing-your-data/notifications/api/#list-all-notification-channels + responses: + '200': + $ref: '#/components/responses/notifications.list_channels@200' /_plugins/_notifications/configs: delete: operationId: notifications.delete_configs.0 @@ -225,6 +236,25 @@ components: application/json: schema: $ref: '../schemas/notifications._common.yaml#/components/schemas/GetConfigsResponse' + notifications.list_channels@200: + description: '' + content: + application/json: + schema: + type: object + properties: + start_index: + type: integer + format: int64 + total_hits: + type: integer + format: int64 + total_hit_relation: + $ref: '../schemas/notifications._common.yaml#/components/schemas/TotalHitRelation' + channel_list: + type: array + items: + $ref: '../schemas/notifications._common.yaml#/components/schemas/NotificationChannel' notifications.list_features@200: description: '' content: diff --git a/spec/namespaces/rollups.yaml b/spec/namespaces/rollups.yaml new file mode 100644 index 000000000..427c89aed --- /dev/null +++ b/spec/namespaces/rollups.yaml @@ -0,0 +1,178 @@ +openapi: 3.1.0 +info: + title: OpenSearch Index Rollups API + description: OpenSearch Index Rollups API + version: 1.0.0 +paths: + /_plugins/_rollup/jobs/{id}: + get: + operationId: rollups.get_rollup.0 + x-operation-group: rollups.get_rollup + x-version-added: 1.0 + description: Get an index rollup. + externalDocs: + url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#get-an-index-rollup-job + parameters: + - $ref: '#/components/parameters/rollups.get_rollup::path.id' + responses: + '200': + $ref: '#/components/responses/rollups.get_rollup@200' + put: + operationId: rollups.put_rollup.0 + x-operation-group: rollups.put_rollup + x-version-added: 1.0 + description: Create or update index rollup. + externalDocs: + url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#create-or-update-an-index-rollup-job + responses: + '200': + $ref: '#/components/responses/rollups.put_rollup@200' + parameters: + - $ref: '#/components/parameters/rollups.put_rollup::path.id' + - $ref: '#/components/parameters/rollups.put_rollup::query.if_seq_no' + - $ref: '#/components/parameters/rollups.put_rollup::query.if_primary_term' + requestBody: + $ref: '#/components/requestBodies/rollups.put_rollup' + delete: + operationId: rollups.delete_rollup.0 + x-operation-group: rollups.delete_rollup + x-version-added: 1.0 + description: Delete index rollup. + externalDocs: + url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#delete-an-index-rollup-job + responses: + '200': + $ref: '#/components/responses/rollups.delete_rollup@200' + parameters: + - $ref: '#/components/parameters/rollups.delete_rollup::path.id' + /_plugins/_rollup/jobs/{id}/_start: + post: + operationId: rollups.start_rollup.0 + x-operation-group: rollups.start_rollup + x-version-added: 1.0 + description: Start rollup. + externalDocs: + url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#start-or-stop-an-index-rollup-job + parameters: + - $ref: '#/components/parameters/rollups.start_rollup::path.id' + responses: + '200': + $ref: '#/components/responses/rollups.start_rollup@200' + /_plugins/_rollup/jobs/{id}/_stop: + post: + operationId: rollups.stop_rollup.0 + x-operation-group: rollups.stop_rollup + x-version-added: 1.0 + description: Stop rollup. + externalDocs: + url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#start-or-stop-an-index-rollup-job + parameters: + - $ref: '#/components/parameters/rollups.stop_rollup::path.id' + responses: + '200': + $ref: '#/components/responses/rollups.stop_rollup@200' + /_plugins/_rollup/jobs/{id}/_explain: + get: + operationId: rollups.explain.0 + x-operation-group: rollups.explain + x-version-added: 1.0 + description: Get a rollup's current status. + externalDocs: + url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#explain-an-index-rollup-job + parameters: + - $ref: '#/components/parameters/rollups.explain::path.id' + responses: + '200': + $ref: '#/components/responses/rollups.explain@200' +components: + requestBodies: + rollups.put_rollup: + content: + application/json: + schema: + $ref: '../schemas/rollups._common.yaml#/components/schemas/RollupEntity' + responses: + rollups.get_rollup@200: + content: + application/json: + schema: + $ref: '../schemas/rollups._common.yaml#/components/schemas/RollupEntity' + rollups.put_rollup@200: + content: + application/json: + schema: + $ref: '../schemas/rollups._common.yaml#/components/schemas/RollupEntity' + rollups.delete_rollup@200: + description: '' + rollups.start_rollup@200: + description: '' + rollups.stop_rollup@200: + description: '' + rollups.explain@200: + content: + application/json: + schema: + $ref: '../schemas/rollups._common.yaml#/components/schemas/ExplainEntities' + parameters: + rollups.get_rollup::path.id: + name: id + in: path + description: Rollup to access + required: true + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + style: simple + rollups.put_rollup::path.id: + name: id + in: path + description: Rollup to access + required: true + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + style: simple + rollups.delete_rollup::path.id: + name: id + in: path + description: Rollup to access + required: true + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + style: simple + rollups.start_rollup::path.id: + name: id + in: path + description: Rollup to access + required: true + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + style: simple + rollups.stop_rollup::path.id: + name: id + in: path + description: Rollup to access + required: true + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + style: simple + rollups.explain::path.id: + name: id + in: path + description: Rollup to access + required: true + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + style: simple + rollups.put_rollup::query.if_seq_no: + name: if_seq_no + in: query + description: Only perform the operation if the document has this sequence number. + schema: + $ref: '../schemas/_common.yaml#/components/schemas/SequenceNumber' + style: form + rollups.put_rollup::query.if_primary_term: + name: if_primary_term + in: query + description: Only perform the operation if the document has this primary term. + schema: + type: number + style: form diff --git a/spec/namespaces/search_pipeline.yaml b/spec/namespaces/search_pipeline.yaml index 9381241da..775e89b4b 100644 --- a/spec/namespaces/search_pipeline.yaml +++ b/spec/namespaces/search_pipeline.yaml @@ -4,41 +4,76 @@ info: description: OpenSearch Search_pipeline API version: 1.0.0 paths: - /_search/pipeline/{pipeline}: + /_search/pipeline: get: operationId: search_pipeline.get.0 x-operation-group: search_pipeline.get x-version-added: '2.9' description: Retrieves information about a specified search pipeline. parameters: - - $ref: '#/components/parameters/search_pipeline.get::path.pipeline' + - $ref: '#/components/parameters/search_pipeline.get::query.cluster_manager_timeout' + responses: + '200': + $ref: '#/components/responses/search_pipeline.get@200' + /_search/pipeline/{id}: + get: + operationId: search_pipeline.get.1 + x-operation-group: search_pipeline.get + x-version-added: '2.9' + description: Retrieves information about a specified search pipeline. + parameters: + - $ref: '#/components/parameters/search_pipeline.get::query.cluster_manager_timeout' + - $ref: '#/components/parameters/search_pipeline.get::path.id' responses: '200': $ref: '#/components/responses/search_pipeline.get@200' put: - operationId: search_pipeline.create.0 - x-operation-group: search_pipeline.create + operationId: search_pipeline.put.0 + x-operation-group: search_pipeline.put x-version-added: '2.9' description: Creates or replaces the specified search pipeline. externalDocs: url: https://opensearch.org/docs/latest/search-plugins/search-pipelines/creating-search-pipeline/ parameters: - - $ref: '#/components/parameters/search_pipeline.create::path.pipeline' + - $ref: '#/components/parameters/search_pipeline.put::query.cluster_manager_timeout' + - $ref: '#/components/parameters/search_pipeline.put::query.timeout' + - $ref: '#/components/parameters/search_pipeline.put::path.id' requestBody: - $ref: '#/components/requestBodies/search_pipeline.create' + $ref: '#/components/requestBodies/search_pipeline.put' responses: '200': - $ref: '#/components/responses/search_pipeline.create@200' + $ref: '#/components/responses/search_pipeline.put@200' + delete: + operationId: search_pipeline.delete.0 + x-operation-group: search_pipeline.delete + x-version-added: '2.9' + description: Deletes the specified search pipeline. + parameters: + - $ref: '#/components/parameters/search_pipeline.delete::query.cluster_manager_timeout' + - $ref: '#/components/parameters/search_pipeline.delete::query.timeout' + - $ref: '#/components/parameters/search_pipeline.delete::path.id' + responses: + '200': + $ref: '#/components/responses/search_pipeline.delete@200' components: requestBodies: - search_pipeline.create: + search_pipeline.put: content: application/json: schema: $ref: '../schemas/search_pipeline._common.yaml#/components/schemas/SearchPipelineStructure' required: true responses: - search_pipeline.create@200: + search_pipeline.put@200: + description: '' + content: + application/json: + schema: + type: object + properties: + acknowledged: + type: boolean + search_pipeline.delete@200: description: '' content: application/json: @@ -54,15 +89,56 @@ components: schema: $ref: '../schemas/search_pipeline._common.yaml#/components/schemas/SearchPipelineMap' parameters: - search_pipeline.create::path.pipeline: - name: pipeline + search_pipeline.put::path.id: + name: id in: path + description: Pipeline ID. schema: type: string required: true - search_pipeline.get::path.pipeline: - name: pipeline + search_pipeline.delete::path.id: + name: id in: path + description: Pipeline ID. schema: type: string required: true + search_pipeline.get::path.id: + name: id + in: path + description: Comma-separated list of search pipeline ids. Wildcards supported. + schema: + type: string + search_pipeline.get::query.cluster_manager_timeout: + name: cluster_manager_timeout + in: query + description: operation timeout for connection to cluster-manager node. + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Duration' + x-version-added: 2.0.0 + search_pipeline.put::query.cluster_manager_timeout: + name: cluster_manager_timeout + in: query + description: operation timeout for connection to cluster-manager node. + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Duration' + x-version-added: 2.0.0 + search_pipeline.delete::query.cluster_manager_timeout: + name: cluster_manager_timeout + in: query + description: Operation timeout for connection to cluster-manager node. + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Duration' + x-version-added: 2.0.0 + search_pipeline.put::query.timeout: + name: timeout + in: query + description: Operation timeout. + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Duration' + search_pipeline.delete::query.timeout: + name: timeout + in: query + description: Operation timeout. + schema: + $ref: '../schemas/_common.yaml#/components/schemas/Duration' diff --git a/spec/namespaces/security.yaml b/spec/namespaces/security.yaml index 06a4d6bab..48b48993f 100644 --- a/spec/namespaces/security.yaml +++ b/spec/namespaces/security.yaml @@ -637,7 +637,7 @@ components: required: true security.patch_action_group: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -645,7 +645,7 @@ components: required: true security.patch_action_groups: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -653,7 +653,7 @@ components: required: true security.patch_audit_configuration: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -661,7 +661,7 @@ components: required: true security.patch_configuration: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -669,7 +669,7 @@ components: required: true security.patch_distinguished_names: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -677,7 +677,7 @@ components: required: true security.patch_role: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -685,7 +685,7 @@ components: required: true security.patch_role_mapping: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -693,7 +693,7 @@ components: required: true security.patch_role_mappings: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -701,7 +701,7 @@ components: required: true security.patch_roles: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -709,7 +709,7 @@ components: required: true security.patch_tenant: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -717,7 +717,7 @@ components: required: true security.patch_tenants: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -725,7 +725,7 @@ components: required: true security.patch_user: content: - application/json: + application/x-ndjson: schema: type: array items: @@ -733,7 +733,7 @@ components: required: true security.patch_users: content: - application/json: + application/x-ndjson: schema: type: array items: diff --git a/spec/namespaces/snapshot.yaml b/spec/namespaces/snapshot.yaml index 26cfe0796..d4113cf1b 100644 --- a/spec/namespaces/snapshot.yaml +++ b/spec/namespaces/snapshot.yaml @@ -590,6 +590,7 @@ components: description: If `true`, the request returns a response when the snapshot is complete. If `false`, the request returns a response when the snapshot initializes. schema: type: boolean + default: false style: form snapshot.create_repository::path.repository: in: path @@ -727,6 +728,7 @@ components: description: If false, the request returns an error for any snapshots that are unavailable. schema: type: boolean + default: false style: form snapshot.get::query.master_timeout: in: query @@ -763,9 +765,10 @@ components: snapshot.get_repository::query.local: in: query name: local - description: 'Return local information, do not retrieve the state from cluster-manager node (default: false)' + description: Return local information, do not retrieve the state from cluster-manager node. schema: type: boolean + default: false style: form snapshot.get_repository::query.master_timeout: in: query @@ -816,6 +819,7 @@ components: description: Should this request wait until the operation has completed before returning schema: type: boolean + default: false style: form snapshot.status::path.repository: in: path @@ -846,6 +850,7 @@ components: description: Whether to ignore unavailable snapshots, defaults to false which means a SnapshotMissingException is thrown schema: type: boolean + default: false style: form snapshot.status::query.master_timeout: in: query diff --git a/spec/namespaces/tasks.yaml b/spec/namespaces/tasks.yaml index d3c542df4..b67eed193 100644 --- a/spec/namespaces/tasks.yaml +++ b/spec/namespaces/tasks.yaml @@ -146,6 +146,7 @@ components: description: Should the request block until the cancellation of the task and its descendant tasks is completed. Defaults to false schema: type: boolean + default: false style: form tasks.get::path.task_id: in: path @@ -170,6 +171,7 @@ components: description: If `true`, the request blocks until the task has completed. schema: type: boolean + default: false style: form tasks.list::query.actions: in: query @@ -188,6 +190,7 @@ components: description: If `true`, the response includes detailed information about shard recoveries. schema: type: boolean + default: false style: form tasks.list::query.group_by: in: query @@ -227,4 +230,5 @@ components: description: If `true`, the request blocks until the operation is complete. schema: type: boolean + default: false style: form diff --git a/spec/opensearch-openapi.yaml b/spec/opensearch-openapi.yaml deleted file mode 100644 index 23caf7ddd..000000000 --- a/spec/opensearch-openapi.yaml +++ /dev/null @@ -1,544 +0,0 @@ -openapi: 3.1.0 -info: - title: OpenSearch API - description: OpenSearch API - version: 1.0.0 -paths: - /: - $ref: 'namespaces/_core.yaml#/paths/~1' - /_bulk: - $ref: 'namespaces/_core.yaml#/paths/~1_bulk' - /_count: - $ref: 'namespaces/_core.yaml#/paths/~1_count' - /_delete_by_query/{task_id}/_rethrottle: - $ref: 'namespaces/_core.yaml#/paths/~1_delete_by_query~1{task_id}~1_rethrottle' - /_field_caps: - $ref: 'namespaces/_core.yaml#/paths/~1_field_caps' - /_mget: - $ref: 'namespaces/_core.yaml#/paths/~1_mget' - /_msearch: - $ref: 'namespaces/_core.yaml#/paths/~1_msearch' - /_msearch/template: - $ref: 'namespaces/_core.yaml#/paths/~1_msearch~1template' - /_mtermvectors: - $ref: 'namespaces/_core.yaml#/paths/~1_mtermvectors' - /_rank_eval: - $ref: 'namespaces/_core.yaml#/paths/~1_rank_eval' - /_reindex: - $ref: 'namespaces/_core.yaml#/paths/~1_reindex' - /_reindex/{task_id}/_rethrottle: - $ref: 'namespaces/_core.yaml#/paths/~1_reindex~1{task_id}~1_rethrottle' - /_render/template: - $ref: 'namespaces/_core.yaml#/paths/~1_render~1template' - /_render/template/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1_render~1template~1{id}' - /_script_context: - $ref: 'namespaces/_core.yaml#/paths/~1_script_context' - /_script_language: - $ref: 'namespaces/_core.yaml#/paths/~1_script_language' - /_scripts/painless/_execute: - $ref: 'namespaces/_core.yaml#/paths/~1_scripts~1painless~1_execute' - /_scripts/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1_scripts~1{id}' - /_scripts/{id}/{context}: - $ref: 'namespaces/_core.yaml#/paths/~1_scripts~1{id}~1{context}' - /_search: - $ref: 'namespaces/_core.yaml#/paths/~1_search' - /_search/point_in_time: - $ref: 'namespaces/_core.yaml#/paths/~1_search~1point_in_time' - /_search/point_in_time/_all: - $ref: 'namespaces/_core.yaml#/paths/~1_search~1point_in_time~1_all' - /_search/scroll: - $ref: 'namespaces/_core.yaml#/paths/~1_search~1scroll' - /_search/scroll/{scroll_id}: - $ref: 'namespaces/_core.yaml#/paths/~1_search~1scroll~1{scroll_id}' - /_search/template: - $ref: 'namespaces/_core.yaml#/paths/~1_search~1template' - /_search_shards: - $ref: 'namespaces/_core.yaml#/paths/~1_search_shards' - /_update_by_query/{task_id}/_rethrottle: - $ref: 'namespaces/_core.yaml#/paths/~1_update_by_query~1{task_id}~1_rethrottle' - /{index}/_bulk: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_bulk' - /{index}/_count: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_count' - /{index}/_create/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_create~1{id}' - /{index}/_delete_by_query: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_delete_by_query' - /{index}/_doc: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_doc' - /{index}/_doc/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_doc~1{id}' - /{index}/_explain/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_explain~1{id}' - /{index}/_field_caps: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_field_caps' - /{index}/_mget: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_mget' - /{index}/_msearch: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_msearch' - /{index}/_msearch/template: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_msearch~1template' - /{index}/_mtermvectors: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_mtermvectors' - /{index}/_rank_eval: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_rank_eval' - /{index}/_search: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_search' - /{index}/_search/point_in_time: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_search~1point_in_time' - /{index}/_search/template: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_search~1template' - /{index}/_search_shards: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_search_shards' - /{index}/_source/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_source~1{id}' - /{index}/_termvectors: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_termvectors' - /{index}/_termvectors/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_termvectors~1{id}' - /{index}/_update/{id}: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_update~1{id}' - /{index}/_update_by_query: - $ref: 'namespaces/_core.yaml#/paths/~1{index}~1_update_by_query' - /_alias: - $ref: 'namespaces/indices.yaml#/paths/~1_alias' - /_alias/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_alias~1{name}' - /_aliases: - $ref: 'namespaces/indices.yaml#/paths/~1_aliases' - /_analyze: - $ref: 'namespaces/indices.yaml#/paths/~1_analyze' - /_cache/clear: - $ref: 'namespaces/indices.yaml#/paths/~1_cache~1clear' - /_data_stream: - $ref: 'namespaces/indices.yaml#/paths/~1_data_stream' - /_data_stream/_stats: - $ref: 'namespaces/indices.yaml#/paths/~1_data_stream~1_stats' - /_data_stream/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_data_stream~1{name}' - /_data_stream/{name}/_stats: - $ref: 'namespaces/indices.yaml#/paths/~1_data_stream~1{name}~1_stats' - /_flush: - $ref: 'namespaces/indices.yaml#/paths/~1_flush' - /_forcemerge: - $ref: 'namespaces/indices.yaml#/paths/~1_forcemerge' - /_index_template: - $ref: 'namespaces/indices.yaml#/paths/~1_index_template' - /_index_template/_simulate: - $ref: 'namespaces/indices.yaml#/paths/~1_index_template~1_simulate' - /_index_template/_simulate/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_index_template~1_simulate~1{name}' - /_index_template/_simulate_index/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_index_template~1_simulate_index~1{name}' - /_index_template/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_index_template~1{name}' - /_mapping: - $ref: 'namespaces/indices.yaml#/paths/~1_mapping' - /_mapping/field/{fields}: - $ref: 'namespaces/indices.yaml#/paths/~1_mapping~1field~1{fields}' - /_recovery: - $ref: 'namespaces/indices.yaml#/paths/~1_recovery' - /_refresh: - $ref: 'namespaces/indices.yaml#/paths/~1_refresh' - /_resolve/index/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_resolve~1index~1{name}' - /_segments: - $ref: 'namespaces/indices.yaml#/paths/~1_segments' - /_settings: - $ref: 'namespaces/indices.yaml#/paths/~1_settings' - /_settings/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_settings~1{name}' - /_shard_stores: - $ref: 'namespaces/indices.yaml#/paths/~1_shard_stores' - /_stats: - $ref: 'namespaces/indices.yaml#/paths/~1_stats' - /_stats/{metric}: - $ref: 'namespaces/indices.yaml#/paths/~1_stats~1{metric}' - /_template: - $ref: 'namespaces/indices.yaml#/paths/~1_template' - /_template/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1_template~1{name}' - /_upgrade: - $ref: 'namespaces/indices.yaml#/paths/~1_upgrade' - /_validate/query: - $ref: 'namespaces/indices.yaml#/paths/~1_validate~1query' - /{alias}/_rollover: - $ref: 'namespaces/indices.yaml#/paths/~1{alias}~1_rollover' - /{alias}/_rollover/{new_index}: - $ref: 'namespaces/indices.yaml#/paths/~1{alias}~1_rollover~1{new_index}' - /{index}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}' - /{index}/_alias: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_alias' - /{index}/_alias/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_alias~1{name}' - /{index}/_aliases/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_aliases~1{name}' - /{index}/_analyze: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_analyze' - /{index}/_block/{block}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_block~1{block}' - /{index}/_cache/clear: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_cache~1clear' - /{index}/_clone/{target}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_clone~1{target}' - /{index}/_close: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_close' - /{index}/_flush: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_flush' - /{index}/_forcemerge: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_forcemerge' - /{index}/_mapping: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_mapping' - /{index}/_mapping/field/{fields}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_mapping~1field~1{fields}' - /{index}/_open: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_open' - /{index}/_recovery: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_recovery' - /{index}/_refresh: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_refresh' - /{index}/_segments: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_segments' - /{index}/_settings: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_settings' - /{index}/_settings/{name}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_settings~1{name}' - /{index}/_shard_stores: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_shard_stores' - /{index}/_shrink/{target}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_shrink~1{target}' - /{index}/_split/{target}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_split~1{target}' - /{index}/_stats: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_stats' - /{index}/_stats/{metric}: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_stats~1{metric}' - /{index}/_upgrade: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_upgrade' - /{index}/_validate/query: - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_validate~1query' - /_cat: - $ref: 'namespaces/cat.yaml#/paths/~1_cat' - /_cat/aliases: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1aliases' - /_cat/aliases/{name}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1aliases~1{name}' - /_cat/allocation: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1allocation' - /_cat/allocation/{node_id}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1allocation~1{node_id}' - /_cat/cluster_manager: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1cluster_manager' - /_cat/count: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1count' - /_cat/count/{index}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1count~1{index}' - /_cat/fielddata: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1fielddata' - /_cat/fielddata/{fields}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1fielddata~1{fields}' - /_cat/health: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1health' - /_cat/indices: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1indices' - /_cat/indices/{index}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1indices~1{index}' - /_cat/master: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1master' - /_cat/nodeattrs: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1nodeattrs' - /_cat/nodes: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1nodes' - /_cat/pending_tasks: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1pending_tasks' - /_cat/pit_segments: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1pit_segments' - /_cat/pit_segments/_all: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1pit_segments~1_all' - /_cat/plugins: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1plugins' - /_cat/recovery: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1recovery' - /_cat/recovery/{index}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1recovery~1{index}' - /_cat/repositories: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1repositories' - /_cat/segment_replication: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1segment_replication' - /_cat/segment_replication/{index}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1segment_replication~1{index}' - /_cat/segments: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1segments' - /_cat/segments/{index}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1segments~1{index}' - /_cat/shards: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1shards' - /_cat/shards/{index}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1shards~1{index}' - /_cat/snapshots: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1snapshots' - /_cat/snapshots/{repository}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1snapshots~1{repository}' - /_cat/tasks: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1tasks' - /_cat/templates: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1templates' - /_cat/templates/{name}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1templates~1{name}' - /_cat/thread_pool: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1thread_pool' - /_cat/thread_pool/{thread_pool_patterns}: - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1thread_pool~1{thread_pool_patterns}' - /_cluster/allocation/explain: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1allocation~1explain' - /_cluster/decommission/awareness: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1decommission~1awareness' - /_cluster/decommission/awareness/{awareness_attribute_name}/_status: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1decommission~1awareness~1{awareness_attribute_name}~1_status' - /_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1decommission~1awareness~1{awareness_attribute_name}~1{awareness_attribute_value}' - /_cluster/health: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1health' - /_cluster/health/{index}: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1health~1{index}' - /_cluster/pending_tasks: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1pending_tasks' - /_cluster/reroute: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1reroute' - /_cluster/routing/awareness/weights: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1routing~1awareness~1weights' - /_cluster/routing/awareness/{attribute}/weights: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1routing~1awareness~1{attribute}~1weights' - /_cluster/settings: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1settings' - /_cluster/state: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1state' - /_cluster/state/{metric}: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1state~1{metric}' - /_cluster/state/{metric}/{index}: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1state~1{metric}~1{index}' - /_cluster/stats: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1stats' - /_cluster/stats/nodes/{node_id}: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1stats~1nodes~1{node_id}' - /_cluster/voting_config_exclusions: - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1voting_config_exclusions' - /_component_template: - $ref: 'namespaces/cluster.yaml#/paths/~1_component_template' - /_component_template/{name}: - $ref: 'namespaces/cluster.yaml#/paths/~1_component_template~1{name}' - /_remote/info: - $ref: 'namespaces/cluster.yaml#/paths/~1_remote~1info' - /_cluster/nodes/hot_threads: - $ref: 'namespaces/nodes.yaml#/paths/~1_cluster~1nodes~1hot_threads' - /_cluster/nodes/hotthreads: - $ref: 'namespaces/nodes.yaml#/paths/~1_cluster~1nodes~1hotthreads' - /_cluster/nodes/{node_id}/hot_threads: - $ref: 'namespaces/nodes.yaml#/paths/~1_cluster~1nodes~1{node_id}~1hot_threads' - /_cluster/nodes/{node_id}/hotthreads: - $ref: 'namespaces/nodes.yaml#/paths/~1_cluster~1nodes~1{node_id}~1hotthreads' - /_nodes: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes' - /_nodes/hot_threads: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1hot_threads' - /_nodes/hotthreads: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1hotthreads' - /_nodes/reload_secure_settings: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1reload_secure_settings' - /_nodes/stats: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1stats' - /_nodes/stats/{metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1stats~1{metric}' - /_nodes/stats/{metric}/{index_metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1stats~1{metric}~1{index_metric}' - /_nodes/usage: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1usage' - /_nodes/usage/{metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1usage~1{metric}' - /_nodes/{metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{metric}' - /_nodes/{node_id}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}' - /_nodes/{node_id}/hot_threads: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1hot_threads' - /_nodes/{node_id}/hotthreads: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1hotthreads' - /_nodes/{node_id}/reload_secure_settings: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1reload_secure_settings' - /_nodes/{node_id}/stats: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1stats' - /_nodes/{node_id}/stats/{metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1stats~1{metric}' - /_nodes/{node_id}/stats/{metric}/{index_metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1stats~1{metric}~1{index_metric}' - /_nodes/{node_id}/usage: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1usage' - /_nodes/{node_id}/usage/{metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1usage~1{metric}' - /_nodes/{node_id}/{metric}: - $ref: 'namespaces/nodes.yaml#/paths/~1_nodes~1{node_id}~1{metric}' - /_dangling: - $ref: 'namespaces/dangling_indices.yaml#/paths/~1_dangling' - /_dangling/{index_uuid}: - $ref: 'namespaces/dangling_indices.yaml#/paths/~1_dangling~1{index_uuid}' - /_ingest/pipeline: - $ref: 'namespaces/ingest.yaml#/paths/~1_ingest~1pipeline' - /_ingest/pipeline/_simulate: - $ref: 'namespaces/ingest.yaml#/paths/~1_ingest~1pipeline~1_simulate' - /_ingest/pipeline/{id}: - $ref: 'namespaces/ingest.yaml#/paths/~1_ingest~1pipeline~1{id}' - /_ingest/pipeline/{id}/_simulate: - $ref: 'namespaces/ingest.yaml#/paths/~1_ingest~1pipeline~1{id}~1_simulate' - /_ingest/processor/grok: - $ref: 'namespaces/ingest.yaml#/paths/~1_ingest~1processor~1grok' - /_plugins/_knn/models/_search: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1models~1_search' - /_plugins/_knn/models/_train: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1models~1_train' - /_plugins/_knn/models/{model_id}: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1models~1{model_id}' - /_plugins/_knn/models/{model_id}/_train: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1models~1{model_id}~1_train' - /_plugins/_knn/stats: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1stats' - /_plugins/_knn/stats/{stat}: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1stats~1{stat}' - /_plugins/_knn/warmup/{index}: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1warmup~1{index}' - /_plugins/_knn/{node_id}/stats: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1{node_id}~1stats' - /_plugins/_knn/{node_id}/stats/{stat}: - $ref: 'namespaces/knn.yaml#/paths/~1_plugins~1_knn~1{node_id}~1stats~1{stat}' - /_plugins/_notifications/configs: - $ref: 'namespaces/notifications.yaml#/paths/~1_plugins~1_notifications~1configs' - /_plugins/_notifications/configs/{config_id}: - $ref: 'namespaces/notifications.yaml#/paths/~1_plugins~1_notifications~1configs~1{config_id}' - /_plugins/_notifications/feature/test/{config_id}: - $ref: 'namespaces/notifications.yaml#/paths/~1_plugins~1_notifications~1feature~1test~1{config_id}' - /_plugins/_notifications/features: - $ref: 'namespaces/notifications.yaml#/paths/~1_plugins~1_notifications~1features' - /_plugins/_security/api/account: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1account' - /_plugins/_security/api/actiongroups: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1actiongroups' - /_plugins/_security/api/actiongroups/{action_group}: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1actiongroups~1{action_group}' - /_plugins/_security/api/audit: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1audit' - /_plugins/_security/api/audit/config: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1audit~1config' - /_plugins/_security/api/cache: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1cache' - /_plugins/_security/api/internalusers: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1internalusers' - /_plugins/_security/api/internalusers/{username}: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1internalusers~1{username}' - /_plugins/_security/api/nodesdn: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1nodesdn' - /_plugins/_security/api/nodesdn/{cluster_name}: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1nodesdn~1{cluster_name}' - /_plugins/_security/api/roles: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1roles' - /_plugins/_security/api/roles/: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1roles~1' - /_plugins/_security/api/roles/{role}: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1roles~1{role}' - /_plugins/_security/api/rolesmapping: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1rolesmapping' - /_plugins/_security/api/rolesmapping/{role}: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1rolesmapping~1{role}' - /_plugins/_security/api/securityconfig: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1securityconfig' - /_plugins/_security/api/securityconfig/config: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1securityconfig~1config' - /_plugins/_security/api/ssl/certs: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1ssl~1certs' - /_plugins/_security/api/ssl/http/reloadcerts: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1ssl~1http~1reloadcerts' - /_plugins/_security/api/ssl/transport/reloadcerts: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1ssl~1transport~1reloadcerts' - /_plugins/_security/api/tenants/: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1tenants~1' - /_plugins/_security/api/tenants/{tenant}: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1api~1tenants~1{tenant}' - /_plugins/_security/health: - $ref: 'namespaces/security.yaml#/paths/~1_plugins~1_security~1health' - /_remotestore/_restore: - $ref: 'namespaces/remote_store.yaml#/paths/~1_remotestore~1_restore' - /_search/pipeline/{pipeline}: - $ref: 'namespaces/search_pipeline.yaml#/paths/~1_search~1pipeline~1{pipeline}' - /_snapshot: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot' - /_snapshot/_status: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1_status' - /_snapshot/{repository}: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}' - /_snapshot/{repository}/_cleanup: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1_cleanup' - /_snapshot/{repository}/_status: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1_status' - /_snapshot/{repository}/_verify: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1_verify' - /_snapshot/{repository}/{snapshot}: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1{snapshot}' - /_snapshot/{repository}/{snapshot}/_clone/{target_snapshot}: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1{snapshot}~1_clone~1{target_snapshot}' - /_snapshot/{repository}/{snapshot}/_restore: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1{snapshot}~1_restore' - /_snapshot/{repository}/{snapshot}/_status: - $ref: 'namespaces/snapshot.yaml#/paths/~1_snapshot~1{repository}~1{snapshot}~1_status' - /_tasks: - $ref: 'namespaces/tasks.yaml#/paths/~1_tasks' - /_tasks/_cancel: - $ref: 'namespaces/tasks.yaml#/paths/~1_tasks~1_cancel' - /_tasks/{task_id}: - $ref: 'namespaces/tasks.yaml#/paths/~1_tasks~1{task_id}' - /_tasks/{task_id}/_cancel: - $ref: 'namespaces/tasks.yaml#/paths/~1_tasks~1{task_id}~1_cancel' -components: - parameters: - _global::query.pretty: - x-global: true - name: pretty - in: query - description: Whether to pretty format the returned JSON response. - schema: - type: boolean - default: false - _global::query.human: - x-global: true - name: human - in: query - description: Whether to return human readable values for statistics. - schema: - type: boolean - default: true - _global::query.error_trace: - x-global: true - name: error_trace - in: query - description: Whether to include the stack trace of returned errors. - schema: - type: boolean - default: false - _global::query.source: - x-global: true - name: source - in: query - description: The URL-encoded request definition. Useful for libraries that do not accept a request body for non-POST requests. - schema: - type: string - _global::query.filter_path: - x-global: true - name: filter_path - in: query - description: Comma-separated list of filters used to reduce the response. - schema: - oneOf: - - type: string - - type: array - items: - type: string diff --git a/spec/schemas/notifications._common.yaml b/spec/schemas/notifications._common.yaml index e85bee4c7..2ddcfc6a5 100644 --- a/spec/schemas/notifications._common.yaml +++ b/spec/schemas/notifications._common.yaml @@ -291,3 +291,16 @@ components: type: object additionalProperties: type: string + NotificationChannel: + type: object + properties: + config_id: + type: string + name: + type: string + description: + type: string + config_type: + $ref: '#/components/schemas/NotificationConfigType' + is_enabled: + type: boolean diff --git a/spec/schemas/rollups._common.yaml b/spec/schemas/rollups._common.yaml new file mode 100644 index 000000000..e847affe4 --- /dev/null +++ b/spec/schemas/rollups._common.yaml @@ -0,0 +1,195 @@ +openapi: 3.1.0 +info: + title: Schemas of rollup._common category + description: Schemas of rollup._common category + version: 1.0.0 +paths: {} +components: + schemas: + RollupEntity: + type: object + properties: + _id: + type: string + _seqNo: + type: number + _primaryTerm: + type: number + rollup: + $ref: '#/components/schemas/Rollup' + Rollup: + type: object + properties: + rollup_id: + type: string + enabled: + type: boolean + schedule: + $ref: '#/components/schemas/Schedule' + last_updated_time: + type: number + enabled_time: + type: number + description: + type: string + schema_version: + type: number + source_index: + type: string + target_index: + type: string + metadata_id: + type: string + page_size: + type: number + delay: + type: number + continuous: + type: boolean + dimensions: + type: array + items: + $ref: '#/components/schemas/DimensionsConfigItem' + metrics: + type: array + items: + $ref: '#/components/schemas/MetricsConfigItem' + error_notification: + type: string + description: 'Set up a Mustache message template for error notifications. For example, if an index rollup job fails, the system sends a message to a Slack channel.' + ExplainEntities: + type: object + properties: + item: + $ref: '#/components/schemas/Explain' + Explain: + type: object + properties: + rollup_id: + type: string + last_updated_time: + type: number + continuous: + $ref: '#/components/schemas/Continuous' + DimensionsConfigItem: + type: object + properties: + histogram: + $ref: '#/components/schemas/HistogramDimension' + date_histogram: + $ref: '#/components/schemas/DateHistogramDimension' + terms: + $ref: '#/components/schemas/TermsDimension' + minproperties: 1 + maxproperties: 1 + DateHistogramDimension: + type: object + properties: + fixed_interval: + type: string + calendar_interval: + type: string + timezone: + type: string + source_field: + type: string + target_field: + type: string + HistogramDimension: + type: object + properties: + source_field: + type: string + target_field: + type: string + interval: + type: string + TermsDimension: + type: object + properties: + source_field: + type: string + target_field: + type: string + MetricsConfigItem: + type: object + properties: + source_field: + type: string + target_field: + type: string + metrics: + type: array + items: + $ref: '#/components/schemas/MetricsConfigMetrics' + MetricsConfigMetrics: + type: object + properties: + avg: + type: object + sum: + type: object + max: + type: object + min: + type: object + value_count: + type: object + minproperties: 1 + maxproperties: 1 + Cron: + type: object + properties: + expression: + type: string + timezone: + type: string + Schedule: + type: object + properties: + interval: + $ref: '#/components/schemas/Interval' + Interval: + type: object + properties: + start_time: + type: number + period: + type: number + unit: + type: string + cron: + oneOf: + - type: array + items: + $ref: '#/components/schemas/Cron' + - $ref: '#/components/schemas/Cron' + schedule_delay: + type: number + Continuous: + type: object + properties: + next_window_start_time: + type: number + next_window_end_time: + type: number + status: + type: string + failure_reason: + type: string + stats: + $ref: '#/components/schemas/Stats' + Stats: + type: object + properties: + pages_processsed: + type: number + documents_processed: + type: number + rollups_indexed: + type: number + index_time_in_ms: + type: number + search_time_in_ms: + type: number + diff --git a/tools/README.md b/tools/README.md index 41b5daf96..a9bffabab 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,25 +1,45 @@ # OpenSearch OpenAPI Tools + This folder contains tools for the repo: -- Merger: merges multiple OpenAPI files into one -- Linter: validates files in the spec folder + +- [Merger](./merger): merges multiple OpenAPI files into one +- [Linter](./linter): validates files in the spec folder ## Setup + 1. Install [Node.js](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs) 2. Run `npm install` in the `tools` folder ## Merger + The merger tool merges the multi-file OpenSearch spec into a single file for programmatic use. It takes 2 parameters: -- The path to the root folder of the multi-file spec -- The path to the output file + +- the path to the root folder of the multi-file spec +- the path to the output file + Example: + ```bash -npm run merge -- ../spec ../build/opensearch-openapi.latest.yaml +mkdir -p ../build +export ROOT_PATH=../spec +export OUTPUT_PATH=../build/opensearch-openapi.yaml +npm run merge -- $ROOT_PATH $OUTPUT_PATH ``` -## Linter +As a shortcut, if those parameters are not provided, the tool will use the default values: +- `../spec` as the root path (i.e. the repo's [spec folder](../spec)) +- `../opensearch-openapi.yaml` as the output path + +```bash +npm run merge +``` + +## Spec Linter + The linter tool validates the OpenSearch spec files in the `spec` folder: + ```bash -npm run lint +npm run lint:spec ``` -It will print out all the errors and warnings in the spec files. This tool in still in development, and it will be integrated into the CI/CD pipeline and run automatically with every PR. -``` \ No newline at end of file + +It will print out all the errors and warnings in the spec files. \ No newline at end of file diff --git a/tools/eslint.config.mjs b/tools/eslint.config.mjs new file mode 100644 index 000000000..9e13e3c1a --- /dev/null +++ b/tools/eslint.config.mjs @@ -0,0 +1,57 @@ +import path from 'path' +import { fileURLToPath } from 'url' +import { FlatCompat } from '@eslint/eslintrc' +import pluginJs from '@eslint/js' + +// mimic CommonJS variables -- not needed if using CommonJS +const _filename = fileURLToPath(import.meta.url) +const _dirname = path.dirname(_filename) +const compat = new FlatCompat({ baseDirectory: _dirname, recommendedConfig: pluginJs.configs.recommended }) + +export default [ + pluginJs.configs.recommended, + ...compat.extends('standard-with-typescript'), + { + files: ['**/*.{js,ts}'], + rules: { + '@typescript-eslint/consistent-indexed-object-style': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/naming-convention': ['error', + { selector: 'classProperty', modifiers: ['static', 'readonly'], format: ['UPPER_CASE'], leadingUnderscore: 'allow' }, + { selector: 'memberLike', modifiers: ['public'], format: ['snake_case'], leadingUnderscore: 'forbid' }, + { selector: 'memberLike', modifiers: ['private', 'protected'], format: ['snake_case'], leadingUnderscore: 'require' }, + { selector: 'variableLike', format: ['snake_case', 'UPPER_CASE'], leadingUnderscore: 'allow' }, + { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'objectLiteralProperty', format: null }, + { selector: 'typeProperty', format: null } + ], + '@typescript-eslint/no-confusing-void-expression': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unsafe-argument': 'error', + '@typescript-eslint/prefer-nullish-coalescing': 'error', + '@typescript-eslint/require-array-sort-compare': 'error', + '@typescript-eslint/strict-boolean-expressions': ['error', + { + allowString: true, + allowNumber: true, + allowNullableObject: true, + allowNullableBoolean: true, + allowNullableString: false, + allowNullableNumber: false, + allowNullableEnum: false, + allowAny: false, + allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false + } + ], + 'array-callback-return': 'off', + 'new-cap': 'off', + 'no-return-assign': 'error', + 'object-shorthand': 'error' + } + } +] diff --git a/tools/helpers.ts b/tools/helpers.ts index 336ffb68c..cdbb5af53 100644 --- a/tools/helpers.ts +++ b/tools/helpers.ts @@ -1,47 +1,69 @@ -import fs from "fs"; -import YAML from "yaml"; -import _ from "lodash"; - -export function resolve(ref: string, root: Record) { - const paths = ref.replace('#/', '').split('/'); - for(const p of paths) { - root = root[p]; - if(root === undefined) break; +import fs from 'fs' +import YAML from 'yaml' +import _ from 'lodash' + +export function resolve_ref (ref: string, root: Record): Record | undefined { + const paths = ref.replace('#/', '').split('/') + for (const p of paths) { + root = root[p] + if (root === undefined) break + } + return root +} + +export function resolve_obj (obj: Record | undefined, root: Record): Record | undefined { + if (obj === undefined) return undefined + if (obj.$ref !== null) return resolve_ref(obj.$ref as string, root) + return obj +} + +export function dig (obj: Record, path: string[], root: Record): any { + let value = obj + for (const p of path) { + value = resolve_obj(value, root)?.[p] + if (value === undefined) break + } + return value +} + +export function sort_by_keys (obj: Record, priorities: string[] = []): void { + const orders = _.fromPairs(priorities.map((k, i) => [k, i + 1])) + const sorted = _.entries(obj).sort((a, b) => { + const order_a = orders[a[0]] + const order_b = orders[b[0]] + if (order_a != null && order_b != null) return order_a - order_b + if (order_a != null) return 1 + if (order_b != null) return -1 + return a[0].localeCompare(b[0]) + }) + sorted.forEach(([k, v]) => { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete obj[k] + obj[k] = v + }) +} + +export function read_yaml (file_path: string, exclude_schema: boolean = false): Record { + const doc = YAML.parse(fs.readFileSync(file_path, 'utf8')) + if (exclude_schema) delete doc.$schema + return doc +} + +export function write_yaml (file_path: string, content: Record): void { + fs.writeFileSync(file_path, quote_refs(YAML.stringify(remove_anchors(content), { lineWidth: 0, singleQuote: true }))) +} + +function quote_refs (str: string): string { + return str.split('\n').map((line) => { + if (line.includes('$ref')) { + const [key, value] = line.split(': ') + if (!value.startsWith("'")) line = `${key}: '${value}'` } - return root; -} - -export function sortByKey(obj: Record, priorities: string[] = []) { - const orders = _.fromPairs(priorities.map((k, i) => [k, i+1])); - const sorted = _.entries(obj).sort((a,b) => { - const order_a = orders[a[0]]; - const order_b = orders[b[0]]; - if(order_a && order_b) return order_a - order_b; - if(order_a) return 1; - if(order_b) return -1; - return a[0].localeCompare(b[0]); - }); - sorted.forEach(([k, v]) => { - delete obj[k]; - obj[k] = v; - }); -} - -export function write2file(file_path: string, content: Record): void { - fs.writeFileSync(file_path, quoteRefs(YAML.stringify(removeAnchors(content), {lineWidth: 0, singleQuote: true}))); -} - -function quoteRefs(str: string): string { - return str.split('\n').map((line) => { - if(line.includes('$ref')) { - const [key, value] = line.split(': '); - if(!value.startsWith("'")) line = `${key}: '${value}'`; - } - return line - }).join('\n'); -} - -function removeAnchors(content: Record): Record { - const replacer = (key: string, value: any) => key === '$anchor' ? undefined : value; - return JSON.parse(JSON.stringify(content, replacer)); -} \ No newline at end of file + return line + }).join('\n') +} + +function remove_anchors (content: Record): Record { + const replacer = (key: string, value: any): any => key === '$anchor' ? undefined : value + return JSON.parse(JSON.stringify(content, replacer)) +} diff --git a/tools/jest.config.js b/tools/jest.config.js index b413e106d..e5fbf09c0 100644 --- a/tools/jest.config.js +++ b/tools/jest.config.js @@ -1,5 +1,5 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { preset: 'ts-jest', - testEnvironment: 'node', -}; \ No newline at end of file + testEnvironment: 'node' +} diff --git a/tools/linter/InlineObjectSchemaValidator.ts b/tools/linter/InlineObjectSchemaValidator.ts new file mode 100644 index 000000000..7acf5eb48 --- /dev/null +++ b/tools/linter/InlineObjectSchemaValidator.ts @@ -0,0 +1,44 @@ +import type NamespacesFolder from './components/NamespacesFolder' +import type SchemasFolder from './components/SchemasFolder' +import { type ValidationError } from '../types' +import { SchemaVisitor } from './utils/SpecificationVisitor' +import { is_ref, type MaybeRef, SpecificationContext } from './utils' +import { type OpenAPIV3 } from 'openapi-types' + +export default class InlineObjectSchemaValidator { + private readonly _namespaces_folder: NamespacesFolder + private readonly _schemas_folder: SchemasFolder + + constructor (namespaces_folder: NamespacesFolder, schemas_folder: SchemasFolder) { + this._namespaces_folder = namespaces_folder + this._schemas_folder = schemas_folder + } + + validate (): ValidationError[] { + const errors: ValidationError[] = [] + + const visitor = new SchemaVisitor((ctx, schema) => { + this.#validate_schema(ctx, schema, errors) + }); + + [ + ...this._namespaces_folder.files, + ...this._schemas_folder.files + ].forEach(f => { visitor.visit_specification(new SpecificationContext(f.file), f.spec()) }) + + return errors + } + + #validate_schema (ctx: SpecificationContext, schema: MaybeRef, errors: ValidationError[]): void { + if (is_ref(schema) || schema.type !== 'object' || schema.properties === undefined) { + return + } + + const this_key = ctx.key + const parent_key = ctx.parent().key + + if (parent_key === 'properties' || this_key === 'additionalProperties' || this_key === 'items') { + errors.push(ctx.error('object schemas should be defined out-of-line via a $ref')) + } + } +} diff --git a/tools/linter/PathRefsValidator.ts b/tools/linter/PathRefsValidator.ts deleted file mode 100644 index 7875bf208..000000000 --- a/tools/linter/PathRefsValidator.ts +++ /dev/null @@ -1,76 +0,0 @@ -import {ValidationError} from "../types"; -import RootFile from "./components/RootFile"; -import NamespacesFolder from "./components/NamespacesFolder"; - -export default class PathRefsValidator { - root_file: RootFile; - namespaces_folder: NamespacesFolder; - - referenced_paths: Record> = {}; // file -> paths - available_paths: Record> = {}; // file -> paths - - constructor(root_file: RootFile, namespaces_folder: NamespacesFolder) { - this.root_file = root_file; - this.namespaces_folder = namespaces_folder; - this.#build_referenced_paths(); - this.#build_available_paths(); - } - - #build_referenced_paths() { - for (const [path, spec] of Object.entries(this.root_file.spec().paths)) { - const ref = spec!.$ref!; - const file = ref.split('#')[0]; - if(!this.referenced_paths[file]) this.referenced_paths[file] = new Set(); - this.referenced_paths[file].add(path); - } - } - - #build_available_paths() { - for (const file of this.namespaces_folder.files) { - this.available_paths[file.file] = new Set(Object.keys(file.spec().paths || {})); - } - } - - validate(): ValidationError[] { - return [ - ...this.validate_unresolved_refs(), - ...this.validate_unreferenced_paths(), - ]; - } - - validate_unresolved_refs(): ValidationError[] { - return Object.entries(this.referenced_paths).flatMap(([ref_file, ref_paths]) => { - const available = this.available_paths[ref_file]; - if(!available) return { - file: this.root_file.file, - location: `Paths: ${[...ref_paths].join(' , ')}`, - message: `Unresolved path reference: Namespace file ${ref_file} does not exist.`, - }; - - return Array.from(ref_paths).map((path) => { - if(!available.has(path)) return { - file: this.root_file.file, - location: `Path: ${path}`, - message: `Unresolved path reference: Path ${path} does not exist in namespace file ${ref_file}.`, - }; - }).filter((e) => e) as ValidationError[]; - }); - } - - validate_unreferenced_paths(): ValidationError[] { - return Object.entries(this.available_paths).flatMap(([ns_file, ns_paths]) => { - const referenced = this.referenced_paths[ns_file]; - if(!referenced) return { - file: ns_file, - message: `Unreferenced paths: No paths are referenced in the root file.`, - }; - return Array.from(ns_paths).map((path) => { - if(!referenced || !referenced.has(path)) return { - file: ns_file, - location: `Path: ${path}`, - message: `Unreferenced path: Path ${path} is not referenced in the root file.`, - }; - }).filter((e) => e) as ValidationError[]; - }); - } -} \ No newline at end of file diff --git a/tools/linter/SchemaRefsValidator.ts b/tools/linter/SchemaRefsValidator.ts index 20da2d269..149d40f34 100644 --- a/tools/linter/SchemaRefsValidator.ts +++ b/tools/linter/SchemaRefsValidator.ts @@ -1,100 +1,108 @@ -import NamespacesFolder from "./components/NamespacesFolder"; -import SchemasFolder from "./components/SchemasFolder"; -import {ValidationError} from "../types"; +import type NamespacesFolder from './components/NamespacesFolder' +import type SchemasFolder from './components/SchemasFolder' +import { type ValidationError } from '../types' export default class SchemaRefsValidator { - namespaces_folder: NamespacesFolder; - schemas_folder: SchemasFolder; + namespaces_folder: NamespacesFolder + schemas_folder: SchemasFolder - referenced_schemas: Record> = {}; // file -> schemas - available_schemas: Record> = {}; // file -> schemas + referenced_schemas: Record> = {} // file -> schemas + available_schemas: Record> = {} // file -> schemas - constructor(namespaces_folder: NamespacesFolder, schemas_folder: SchemasFolder) { - this.namespaces_folder = namespaces_folder; - this.schemas_folder = schemas_folder; - this.#find_refs_in_namespaces_folder(); - this.#find_refs_in_schemas_folder(); - this.#build_available_schemas(); - } - - #find_refs_in_namespaces_folder() { - const search = (obj: Record) => { - const ref = obj.$ref; - if(ref) { - const file = ref.split('#')[0].replace("../", ""); - const name = ref.split('/').pop(); - if(!this.referenced_schemas[file]) this.referenced_schemas[file] = new Set(); - this.referenced_schemas[file].add(name); - } - for (const key in obj) - if(typeof obj[key] === 'object') search(obj[key]); - }; + constructor (namespaces_folder: NamespacesFolder, schemas_folder: SchemasFolder) { + this.namespaces_folder = namespaces_folder + this.schemas_folder = schemas_folder + this.#find_refs_in_namespaces_folder() + this.#find_refs_in_schemas_folder() + this.#build_available_schemas() + } - this.namespaces_folder.files.forEach((file) => { search(file.spec().components || {}) }); + #find_refs_in_namespaces_folder (): void { + const search = (obj: any): void => { + const ref: string = obj.$ref ?? '' + if (ref !== '') { + const file = ref.split('#')[0].replace('../', '') + const name = ref.split('/').pop() ?? '' + if (name === '') throw new Error(`Invalid schema reference: ${ref}`) + if (this.referenced_schemas[file] == null) this.referenced_schemas[file] = new Set() + this.referenced_schemas[file].add(name) + } + for (const key in obj) { if (typeof obj[key] === 'object') search(obj[key]) } } - #find_refs_in_schemas_folder() { - const search = (obj: Record, ref_file: string) => { - const ref = obj.$ref; - if(ref) { - const file = ref.startsWith('#') ? ref_file : `schemas/${ref.split('#')[0]}`; - const name = ref.split('/').pop(); - if(!this.referenced_schemas[file]) this.referenced_schemas[file] = new Set(); - this.referenced_schemas[file].add(name); - } - for (const key in obj) - if(typeof obj[key] === 'object') search(obj[key], ref_file); - } + this.namespaces_folder.files.forEach((file) => { search(file.spec().components ?? {}) }) + } - this.schemas_folder.files.forEach((file) => { search(file.spec().components?.schemas || {}, file.file) }); + #find_refs_in_schemas_folder (): void { + const search = (obj: any, ref_file: string): void => { + const ref = obj.$ref as string ?? '' + if (ref !== '') { + const file = ref.startsWith('#') ? ref_file : `schemas/${ref.split('#')[0]}` + const name = ref.split('/').pop() ?? '' + if (name === '') throw new Error(`Invalid schema reference: ${ref}`) + if (this.referenced_schemas[file] == null) this.referenced_schemas[file] = new Set() + this.referenced_schemas[file].add(name) + } + for (const key in obj) { if (typeof obj[key] === 'object') search(obj[key], ref_file) } } - #build_available_schemas() { - this.schemas_folder.files.forEach((file) => { - this.available_schemas[file.file] = new Set(Object.keys(file.spec().components?.schemas || {})); - }); - } + this.schemas_folder.files.forEach((file) => { search(file.spec().components?.schemas ?? {}, file.file) }) + } - validate(): ValidationError[] { - return [ - ...this.validate_unresolved_refs(), - ...this.validate_unreferenced_schemas(), - ]; - } + #build_available_schemas (): void { + this.schemas_folder.files.forEach((file) => { + this.available_schemas[file.file] = new Set(Object.keys(file.spec().components?.schemas ?? {})) + }) + } - validate_unresolved_refs(): ValidationError[] { - return Object.entries(this.referenced_schemas).flatMap(([ref_file, ref_schemas]) => { - const available = this.available_schemas[ref_file]; - if(!available) return { - file: this.namespaces_folder.file, - message: `Unresolved schema reference: Schema file ${ref_file} is referenced but does not exist.`, - }; + validate (): ValidationError[] { + return [ + ...this.validate_unresolved_refs(), + ...this.validate_unreferenced_schemas() + ] + } - return Array.from(ref_schemas).map((schema) => { - if(!available.has(schema)) return { - file: ref_file, - location: `#/components/schemas/${schema}`, - message: `Unresolved schema reference: Schema ${schema} is referenced but does not exist.`, - }; - }).filter((e) => e) as ValidationError[]; - }); - } + validate_unresolved_refs (): ValidationError[] { + return Object.entries(this.referenced_schemas).flatMap(([ref_file, ref_schemas]) => { + const available = this.available_schemas[ref_file] + if (available == null) { + return { + file: this.namespaces_folder.file, + message: `Unresolved schema reference: Schema file ${ref_file} is referenced but does not exist.` + } + } - validate_unreferenced_schemas(): ValidationError[] { - return Object.entries(this.available_schemas).flatMap(([file, schemas]) => { - const referenced = this.referenced_schemas[file]; - if(!referenced) return { - file: file, - message: `Unreferenced schema: Schema file ${file} is not referenced anywhere.`, - }; + return Array.from(ref_schemas).map((schema) => { + if (!available.has(schema)) { + return { + file: ref_file, + location: `#/components/schemas/${schema}`, + message: `Unresolved schema reference: Schema ${schema} is referenced but does not exist.` + } + } + }).filter((e) => e) as ValidationError[] + }) + } - return Array.from(schemas).map((schema) => { - if(!referenced.has(schema)) return { - file: file, - location: `#/components/schemas/${schema}`, - message: `Unreferenced schema: Schema ${schema} is not referenced anywhere.`, - }; - }).filter((e) => e) as ValidationError[]; - }); - } -} \ No newline at end of file + validate_unreferenced_schemas (): ValidationError[] { + return Object.entries(this.available_schemas).flatMap(([file, schemas]) => { + const referenced = this.referenced_schemas[file] + if (referenced == null) { + return { + file, + message: `Unreferenced schema: Schema file ${file} is not referenced anywhere.` + } + } + + return Array.from(schemas).map((schema) => { + if (!referenced.has(schema)) { + return { + file, + location: `#/components/schemas/${schema}`, + message: `Unreferenced schema: Schema ${schema} is not referenced anywhere.` + } + } + }).filter((e) => e) as ValidationError[] + }) + } +} diff --git a/tools/linter/SpecValidator.ts b/tools/linter/SpecValidator.ts index c01353c31..50f489e12 100644 --- a/tools/linter/SpecValidator.ts +++ b/tools/linter/SpecValidator.ts @@ -1,36 +1,40 @@ -import SchemasFolder from "./components/SchemasFolder"; -import NamespacesFolder from "./components/NamespacesFolder"; -import RootFile from "./components/RootFile"; -import {ValidationError} from "../types"; -import PathRefsValidator from "./PathRefsValidator"; -import SchemaRefsValidator from "./SchemaRefsValidator"; +import SchemasFolder from './components/SchemasFolder' +import NamespacesFolder from './components/NamespacesFolder' +import { type ValidationError } from '../types' +import SchemaRefsValidator from './SchemaRefsValidator' +import SupersededOperationsFile from './components/SupersededOperationsFile' +import InfoFile from './components/InfoFile' +import InlineObjectSchemaValidator from './InlineObjectSchemaValidator' export default class SpecValidator { - root_file: RootFile; - namespaces_folder: NamespacesFolder; - schemas_folder: SchemasFolder; - path_refs_validator: PathRefsValidator; - schema_refs_validator: SchemaRefsValidator; + superseded_ops_file: SupersededOperationsFile + info_file: InfoFile + namespaces_folder: NamespacesFolder + schemas_folder: SchemasFolder + schema_refs_validator: SchemaRefsValidator + inline_object_schema_validator: InlineObjectSchemaValidator - constructor(root_folder: string) { - this.root_file = new RootFile(`${root_folder}/opensearch-openapi.yaml`); - this.namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`); - this.schemas_folder = new SchemasFolder(`${root_folder}/schemas`); - this.path_refs_validator = new PathRefsValidator(this.root_file, this.namespaces_folder); - this.schema_refs_validator = new SchemaRefsValidator(this.namespaces_folder, this.schemas_folder); - } + constructor (root_folder: string) { + this.superseded_ops_file = new SupersededOperationsFile(`${root_folder}/_superseded_operations.yaml`) + this.info_file = new InfoFile(`${root_folder}/_info.yaml`) + this.namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`) + this.schemas_folder = new SchemasFolder(`${root_folder}/schemas`) + this.schema_refs_validator = new SchemaRefsValidator(this.namespaces_folder, this.schemas_folder) + this.inline_object_schema_validator = new InlineObjectSchemaValidator(this.namespaces_folder, this.schemas_folder) + } - validate(): ValidationError[] { - const component_errors = [ - ...this.root_file.validate(), - ...this.namespaces_folder.validate(), - ...this.schemas_folder.validate(), - ]; - if(component_errors.length) return component_errors; + validate (): ValidationError[] { + const component_errors = [ + ...this.namespaces_folder.validate(), + ...this.schemas_folder.validate() + ] + if (component_errors.length > 0) return component_errors - return [ - ...this.path_refs_validator.validate(), - ...this.schema_refs_validator.validate() - ] - } + return [ + ...this.schema_refs_validator.validate(), + ...this.superseded_ops_file.validate(), + ...this.info_file.validate(), + ...this.inline_object_schema_validator.validate() + ] + } } diff --git a/tools/linter/components/InfoFile.ts b/tools/linter/components/InfoFile.ts new file mode 100644 index 000000000..791beda41 --- /dev/null +++ b/tools/linter/components/InfoFile.ts @@ -0,0 +1,5 @@ +import FileValidator from './base/FileValidator' + +export default class InfoFile extends FileValidator { + has_json_schema = true +} diff --git a/tools/linter/components/NamespaceFile.ts b/tools/linter/components/NamespaceFile.ts index 7a3b95476..f163c0c28 100644 --- a/tools/linter/components/NamespaceFile.ts +++ b/tools/linter/components/NamespaceFile.ts @@ -1,103 +1,103 @@ -import {OpenAPIV3} from "openapi-types"; -import {OperationSpec, ValidationError} from "../../types"; -import OperationGroup from "./OperationGroup"; -import _ from "lodash"; -import Operation from "./Operation"; -import {resolve} from "../../helpers" -import FileValidator from "./base/FileValidator"; +import { type OpenAPIV3 } from 'openapi-types' +import { type OperationSpec, type ValidationError } from '../../types' +import OperationGroup from './OperationGroup' +import _ from 'lodash' +import Operation from './Operation' +import { resolve_ref } from '../../helpers' +import FileValidator from './base/FileValidator' -const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']; -const NAME_REGEX = /^[a-z]+[a-z_]*[a-z]+$/; +const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'] +const NAME_REGEX = /^[a-z]+[a-z_]*[a-z]+$/ export default class NamespaceFile extends FileValidator { - namespace: string; - _operation_groups: OperationGroup[] | undefined; - _refs: Set | undefined; + namespace: string + private _operation_groups: OperationGroup[] | undefined + private _refs: Set | undefined - constructor(file_path: string) { - super(file_path); - this.namespace = file_path.split('/').slice(-1)[0].replace('.yaml', ''); - } + constructor (file_path: string) { + super(file_path) + this.namespace = file_path.split('/').slice(-1)[0].replace('.yaml', '') + } - validate_file(): ValidationError[] { - const name_error = this.validate_name(); - if(name_error) return [name_error]; - const group_errors = this.operation_groups().flatMap((group) => group.validate()); - if(group_errors.length > 0) return group_errors; + validate_file (): ValidationError[] { + const name_error = this.validate_name() + if (name_error) return [name_error] + const group_errors = this.operation_groups().flatMap((group) => group.validate()) + if (group_errors.length > 0) return group_errors - return [ - this.validate_schemas(), - ...this.validate_unresolved_refs(), - ...this.validate_unused_refs(), - ...this.validate_parameter_refs(), - ].filter((e) => e) as ValidationError[] - } + return [ + this.validate_schemas(), + ...this.validate_unresolved_refs(), + ...this.validate_unused_refs(), + ...this.validate_parameter_refs() + ].filter((e) => e) as ValidationError[] + } - operation_groups(): OperationGroup[] { - if(this._operation_groups) return this._operation_groups; - const ops: Operation[] = _.entries(this.spec().paths).flatMap(([path, ops]) => { - return _.entries(_.pick(ops, HTTP_METHODS)).map(([verb, op]) => { - return new Operation(this.file, path, verb, op as OperationSpec); - }); - }); + operation_groups (): OperationGroup[] { + if (this._operation_groups) return this._operation_groups + const ops: Operation[] = _.entries(this.spec().paths).flatMap(([path, ops]) => { + return _.entries(_.pick(ops, HTTP_METHODS)).map(([verb, op]) => { + return new Operation(this.file, path, verb, op as OperationSpec) + }) + }) - return this._operation_groups = _.entries(_.groupBy(ops, (op) => op.group)).map(([group, ops]) => { - return new OperationGroup(this.file, group, ops); - }); - } + this._operation_groups = _.entries(_.groupBy(ops, (op) => op.group)).map(([group, ops]) => { + return new OperationGroup(this.file, group, ops) + }) + return this._operation_groups + } - refs(): Set { - if(this._refs) return this._refs; - this._refs = new Set(); - const find_refs = (obj: Record) => { - if(obj.$ref) this._refs!.add(obj.$ref); - _.values(obj).forEach((value) => { if(typeof value === 'object') find_refs(value); }); - } - find_refs(this.spec().paths || {}); - return this._refs; + refs (): Set { + if (this._refs) return this._refs + this._refs = new Set() + const find_refs = (obj: Record): void => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (obj.$ref != null) this._refs!.add(obj.$ref as string) + _.values(obj).forEach((value) => { if (typeof value === 'object') find_refs(value as Record) }) } + find_refs(this.spec().paths ?? {}) + return this._refs + } - validate_name(name = this.namespace): ValidationError | void { - if(name === '_core') return; - if(!name.match(NAME_REGEX)) - return this.error(`Invalid namespace name '${name}'. Must match regex: /${NAME_REGEX.source}/.`, 'File Name'); - return; - } + validate_name (name = this.namespace): ValidationError | undefined { + if (name === '_core') return + if (!name.match(NAME_REGEX)) { return this.error(`Invalid namespace name '${name}'. Must match regex: /${NAME_REGEX.source}/.`, 'File Name') } + } - validate_schemas(): ValidationError | void { - if(this.spec().components?.schemas) - return this.error(`components/schemas is not allowed in namespace files`, '#/components/schemas'); - } + validate_schemas (): ValidationError | undefined { + if (this.spec().components?.schemas) { return this.error('components/schemas is not allowed in namespace files', '#/components/schemas') } + } - validate_unresolved_refs(): ValidationError[] { - return Array.from(this.refs()).map((ref) => { - if(resolve(ref, this.spec()) === undefined) return this.error(`Unresolved reference: ${ref}`, ref); - }).filter((e) => e) as ValidationError[]; - } + validate_unresolved_refs (): ValidationError[] { + return Array.from(this.refs()).map((ref) => { + if (resolve_ref(ref, this.spec()) === undefined) return this.error(`Unresolved reference: ${ref}`, ref) + }).filter((e) => e) as ValidationError[] + } - validate_unused_refs(): ValidationError[] { - return _.entries(this.spec().components || {}).flatMap(([type, collection]) => { - return _.keys(collection).map((name) => { - if (!this.refs().has(`#/components/${type}/${name}`)) - return this.error(`Unused ${type} component: ${name}`, `#/components/${type}/${name}`); - }) - }).filter((e) => e) as ValidationError[]; - } + validate_unused_refs (): ValidationError[] { + return _.entries(this.spec().components ?? {}).flatMap(([type, collection]) => { + return _.keys(collection).map((name) => { + if (!this.refs().has(`#/components/${type}/${name}`)) { return this.error(`Unused ${type} component: ${name}`, `#/components/${type}/${name}`) } + }) + }).filter((e) => e) as ValidationError[] + } - validate_parameter_refs(): ValidationError[] { - const parameters = this.spec().components?.parameters as Record - if(!parameters) return []; - return _.entries(parameters).map(([name, p]) => { - const group = name.split('::')[0]; - const expected = `${group}::${p.in}.${p.name}`; - if(name !== expected) - return this.error( - `Parameter component '${name}' must be named '${expected}' since it is a ${p.in} parameter named '${p.name}'.`, - `#/components/parameters/#${name}`); - if(!p.name.match(/^[a-z0-9._]+$/)) - return this.error( - `Invalid parameter name '${p.name}'. A parameter's name can only contain lower-cased alphanumerics, underscores, and periods.`, - `#/components/parameters/#${name}`); - }).filter((e) => e) as ValidationError[]; - } -} \ No newline at end of file + validate_parameter_refs (): ValidationError[] { + const parameters = this.spec().components?.parameters as Record | undefined + if (!parameters) return [] + return _.entries(parameters).map(([name, p]) => { + const group = name.split('::')[0] + const expected = `${group}::${p.in}.${p.name}` + if (name !== expected) { + return this.error( + `Parameter component '${name}' must be named '${expected}' since it is a ${p.in} parameter named '${p.name}'.`, + `#/components/parameters/#${name}`) + } + if (!p.name.match(/^[a-z0-9._]+$/)) { + return this.error( + `Invalid parameter name '${p.name}'. A parameter's name can only contain lower-cased alphanumerics, underscores, and periods.`, + `#/components/parameters/#${name}`) + } + }).filter((e) => e) as ValidationError[] + } +} diff --git a/tools/linter/components/NamespacesFolder.ts b/tools/linter/components/NamespacesFolder.ts index a8b18c400..86c544fb3 100644 --- a/tools/linter/components/NamespacesFolder.ts +++ b/tools/linter/components/NamespacesFolder.ts @@ -1,28 +1,27 @@ -import NamespaceFile from "./NamespaceFile"; -import {ValidationError} from "../../types"; -import FolderValidator from "./base/FolderValidator"; +import NamespaceFile from './NamespaceFile' +import { type ValidationError } from '../../types' +import FolderValidator from './base/FolderValidator' export default class NamespacesFolder extends FolderValidator { - constructor(folder_path: string) { - super(folder_path, NamespaceFile); - } + constructor (folder_path: string) { + super(folder_path, NamespaceFile) + } - validate_folder(): ValidationError[] { - return this.validate_duplicate_paths(); - } + validate_folder (): ValidationError[] { + return this.validate_duplicate_paths() + } - validate_duplicate_paths(): ValidationError[] { - const paths: { [path: string]: string[] } = {}; - for (const file of this.files) { - if(!file._spec?.paths) continue; - Object.keys(file.spec().paths).sort().forEach((path) => { - if(paths[path]) paths[path].push(file.namespace); - else paths[path] = [file.namespace]; - }); - } - return Object.entries(paths).map(([path, namespaces]) => { - if(namespaces.length > 1) - return this.error(`Duplicate path '${path}' found in namespaces: ${namespaces.sort().join(', ')}.`); - }).filter((e) => e) as ValidationError[]; + validate_duplicate_paths (): ValidationError[] { + const paths: Record = {} + for (const file of this.files) { + if (file.spec().paths == null) continue + Object.keys(file.spec().paths).sort().forEach((path) => { + if (paths[path] == null) paths[path] = [file.namespace] + else paths[path].push(file.namespace) + }) } -} \ No newline at end of file + return Object.entries(paths).map(([path, namespaces]) => { + if (namespaces.length > 1) { return this.error(`Duplicate path '${path}' found in namespaces: ${namespaces.sort().join(', ')}.`) } + }).filter((e) => e) as ValidationError[] + } +} diff --git a/tools/linter/components/Operation.ts b/tools/linter/components/Operation.ts index 1be04bf12..5714473b1 100644 --- a/tools/linter/components/Operation.ts +++ b/tools/linter/components/Operation.ts @@ -1,120 +1,108 @@ -import {OperationSpec, ValidationError} from "../../types"; -import _ from "lodash"; -import ValidatorBase from "./base/ValidatorBase"; -import {OpenAPIV3} from "openapi-types"; +import { type OperationSpec, type ValidationError } from '../../types' +import _ from 'lodash' +import ValidatorBase from './base/ValidatorBase' -const GROUP_REGEX = /^([a-z]+[a-z_]*[a-z]+\.)?([a-z]+[a-z_]*[a-z]+)$/; +const GROUP_REGEX = /^([a-z]+[a-z_]*[a-z]+\.)?([a-z]+[a-z_]*[a-z]+)$/ export default class Operation extends ValidatorBase { - path: string; - verb: string; - group: string; - group_regex: string; - namespace: string | undefined; - spec: OperationSpec; + path: string + verb: string + group: string + group_regex: string + namespace: string | undefined + spec: OperationSpec - constructor(file: string, path: string, verb: string, spec: OperationSpec) { - super(file, `Operation: ${verb.toUpperCase()} ${path}`); - this.path = path; - this.verb = verb; - this.spec = spec; - this.group = spec['x-operation-group']; - this.group_regex = this.group?.replace('.','\\.'); - if(this.group?.indexOf('.') > 0) this.namespace = this.group.split('.')[0]; - } + constructor (file: string, path: string, verb: string, spec: OperationSpec) { + super(file, `Operation: ${verb.toUpperCase()} ${path}`) + this.path = path + this.verb = verb + this.spec = spec + this.group = spec['x-operation-group'] + this.group_regex = this.group?.replace('.', '\\.') + if (this.group?.indexOf('.') > 0) this.namespace = this.group.split('.')[0] + } - validate(): ValidationError[] { - const group_error = this.validate_group(); - if(group_error) return [group_error]; - const namespace_error = this.validate_namespace(); - if(namespace_error) return [namespace_error]; - return [ - this.validate_operationId(), - this.validate_description(), - this.validate_requestBody(), - this.validate_parameters(), - this.validate_path_parameters(), - ...this.validate_responses(), - ].filter((e) => e) as ValidationError[]; - } + validate (): ValidationError[] { + const group_error = this.validate_group() + if (group_error) return [group_error] + const namespace_error = this.validate_namespace() + if (namespace_error) return [namespace_error] + return [ + this.validate_operation_id(), + this.validate_description(), + this.validate_request_body(), + this.validate_parameters(), + this.validate_path_parameters(), + ...this.validate_responses() + ].filter((e) => e) as ValidationError[] + } - validate_group(): ValidationError | void{ - if(!this.group || this.group === '') - return this.error(`Missing x-operation-group property`); - if(!this.group.match(GROUP_REGEX)) - return this.error(`Invalid x-operation-group '${this.group}'. Must match regex: /${GROUP_REGEX.source}/.`); - } + validate_group (): ValidationError | undefined { + if (!this.group || this.group === '') { return this.error('Missing x-operation-group property') } + if (!GROUP_REGEX.test(this.group)) { return this.error(`Invalid x-operation-group '${this.group}'. Must match regex: /${GROUP_REGEX.source}/.`) } + } - validate_namespace(): ValidationError | void { - const expected_namespace = this.file.match(/namespaces\/(.*)\.yaml/)![1]; + validate_namespace (): ValidationError | undefined { + const expected_namespace = this.file.match(/\/(.*)\.yaml/)?.[1] - if(expected_namespace === '_core' && this.namespace === undefined) return; - if(expected_namespace === '_core' && this.namespace === '_core') - return this.error(`Invalid x-operation-group '${this.group}'. '_core' namespace must be omitted in x-operation-group.`); + if (expected_namespace === '_core' && this.namespace === undefined) return + if (expected_namespace === '_core' && this.namespace === '_core') { return this.error(`Invalid x-operation-group '${this.group}'. '_core' namespace must be omitted in x-operation-group.`) } - if(this.namespace === expected_namespace ) return; - return this.error(`Invalid x-operation-group '${this.group}'. '${this.namespace}' namespace detected. ` + - `Only '${expected_namespace}' namespace is allowed in this file.`); - } + if (this.namespace === expected_namespace) return + return this.error(`Invalid x-operation-group '${this.group}'. '${this.namespace}' namespace detected. ` + + `Only '${expected_namespace}' namespace is allowed in this file.`) + } - validate_description(): ValidationError | void { - const description = this.spec.description; - if(!description || description === '') - return this.error(`Missing description property.`); - if(!description.endsWith('.')) - return this.error(`Description must end with a period.`); - } + validate_description (): ValidationError | undefined { + const description = this.spec.description ?? '' + if (description === '') { return this.error('Missing description property.') } + if (!description.endsWith('.')) { return this.error('Description must end with a period.') } + } - validate_operationId(): ValidationError | void { - const id = this.spec.operationId; - if(!id || id === '') - return this.error(`Missing operationId property.`); - if(!id.match(new RegExp(`^${this.group_regex}\\.[0-9]+$`))) - return this.error(`Invalid operationId '${id}'. Must be in {x-operation-group}.{number} format.`); - } + validate_operation_id (): ValidationError | undefined { + const id = this.spec.operationId ?? '' + if (id === '') { return this.error('Missing operationId property.') } + const regex = new RegExp(`^${this.group_regex}\\.[0-9]+$`) + if (!regex.test(id)) { return this.error(`Invalid operationId '${id}'. Must be in {x-operation-group}.{number} format.`) } + } - validate_requestBody(): ValidationError | void { - const body = this.spec.requestBody; - if(!body) return; - const expected = `#/components/requestBodies/${this.group}`; - if(body.$ref !== expected) - return this.error(`The requestBody must be a reference object to '${expected}'.`); - } + validate_request_body (): ValidationError | undefined { + const body = this.spec.requestBody + if (!body) return + const expected = `#/components/requestBodies/${this.group}` + if (body.$ref !== expected) { return this.error(`The requestBody must be a reference object to '${expected}'.`) } + } - validate_responses(): ValidationError[] { - const responses = this.spec.responses; - if(!responses || _.keys(responses).length == 0) return [this.error(`Missing responses property.`)]; - return _.entries(responses).map(([code, response]) => { - const expected = `#/components/responses/${this.group}@${code}`; - if(response.$ref && response.$ref !== expected) - return this.error(`The ${code} response must be a reference object to '${expected}'.`); - return; - }).filter((error) => error) as ValidationError[]; - } + validate_responses (): ValidationError[] { + const responses = this.spec.responses ?? {} + if (_.keys(responses).length === 0) return [this.error('Missing responses property.')] + return _.entries(responses).map(([code, response]) => { + const expected = `#/components/responses/${this.group}@${code}` + if (response.$ref !== expected) { return this.error(`The ${code} response must be a reference object to '${expected}'.`) } + }).filter((error) => error) as ValidationError[] + } - validate_parameters(): ValidationError | void{ - const parameters = this.spec.parameters; - if(!parameters) return; - const regex = new RegExp(`^#/components/parameters/${this.group_regex}::((path)|(query))\\.[a-z0-9_.]+$`); - for(const parameter of parameters){ - if(!parameter.$ref.match(regex)) - return this.error(`Every parameter must be a reference object to '#/components/parameters/{x-operation-group}::{path|query}.{parameter_name}'.`); - } + validate_parameters (): ValidationError | undefined { + const parameters = this.spec.parameters + if (!parameters) return + const regex = new RegExp(`^#/components/parameters/${this.group_regex}::((path)|(query))\\.[a-z0-9_.]+$`) + for (const parameter of parameters) { + if (!regex.test(parameter.$ref)) { return this.error('Every parameter must be a reference object to \'#/components/parameters/{x-operation-group}::{path|query}.{parameter_name}\'.') } } + } - validate_path_parameters(): ValidationError | void { - const path_params = this.path_params(); - const expected = this.path.match(/{[a-z0-9_]+}/g)?.map(p => p.slice(1, -1)) || []; - if(path_params.sort().join(', ') !== expected.sort().join(', ')) - return this.error(`Path parameters must match the parameters in the path: {${expected.join('}, {')}}.`); - } + validate_path_parameters (): ValidationError | undefined { + const path_params = this.path_params() + const expected = this.path.match(/{[a-z0-9_]+}/g)?.map(p => p.slice(1, -1)) ?? [] + if (path_params.sort().join(', ') !== expected.sort().join(', ')) { return this.error(`Path parameters must match the parameters in the path: {${expected.join('}, {')}}.`) } + } - path_params(): string[] { - return this.spec.parameters?.map(p => p.$ref?.match(/::path\.(.+)/)?.[1]) - .filter((p): p is string => p !== undefined) || [] - } + path_params (): string[] { + return this.spec.parameters?.map(p => p.$ref?.match(/::path\.(.+)/)?.[1]) + .filter((p): p is string => p !== undefined) ?? [] + } - query_params(): string[] { - return this.spec.parameters?.map(p => p.$ref?.match(/::query\.(.+)/)?.[1]) - .filter((p): p is string => p !== undefined) || [] - } -} \ No newline at end of file + query_params (): string[] { + return this.spec.parameters?.map(p => p.$ref?.match(/::query\.(.+)/)?.[1]) + .filter((p): p is string => p !== undefined) ?? [] + } +} diff --git a/tools/linter/components/OperationGroup.ts b/tools/linter/components/OperationGroup.ts index af1f5679f..681563875 100644 --- a/tools/linter/components/OperationGroup.ts +++ b/tools/linter/components/OperationGroup.ts @@ -1,63 +1,58 @@ -import Operation from "./Operation"; -import {ValidationError} from "../../types"; -import ValidatorBase from "./base/ValidatorBase"; +import type Operation from './Operation' +import { type ValidationError } from '../../types' +import ValidatorBase from './base/ValidatorBase' export default class OperationGroup extends ValidatorBase { - readonly OP_PRIORITY = ['operationId', 'x-operation-group', 'x-ignorable', 'deprecated', - 'x-deprecation-message', 'x-version-added', 'x-version-deprecated', 'x-version-removed', - 'description', 'externalDocs', 'parameters', 'requestBody', 'responses']; - name: string; - operations: Operation[]; - - constructor(file: string, name: string, operations: Operation[]) { - super(file, `Operation Group: ${name}`); - this.name = name; - this.operations = operations; - } - - validate(): ValidationError[] { - const location = `Operation Group: ${this.name}`; - const ops_errors = this.operations.flatMap((op) => op.validate()); - if(ops_errors.length > 0) return ops_errors; - if(this.operations.length == 1) return []; - return [ - this.validate_description(), - this.validate_externalDocs(), - this.validate_requestBody(), - this.validate_responses(), - this.validate_query_parameters(), - ].filter((e) => e) as ValidationError[]; - } - - validate_description(): ValidationError | void { - const uniq_descriptions = new Set(this.operations.map((op) => op.spec.description)); - if(uniq_descriptions.size > 1) - return this.error(`${this.operations.length} '${this.name}' operations must have identical description property.`); - } - - validate_externalDocs(): ValidationError | void { - const uniq_externalDocs = new Set(this.operations.map((op) => op.spec.externalDocs?.url)); - if(uniq_externalDocs.size > 1) - return this.error(`${this.operations.length} '${this.name}' operations must have identical externalDocs property.`); - } - - validate_requestBody(): ValidationError | void { - const uniq_requestBodies = new Set(this.operations.map((op) => op.spec.requestBody?.$ref)); - if(uniq_requestBodies.size > 1) - return this.error(`${this.operations.length} '${this.name}' operations must have identical requestBody property.`); - } - - validate_responses(): ValidationError | void { - const key_signatures = this.operations.map((op) => Object.keys(op.spec.responses).sort().join('#$@')); - const uniq_signatures = new Set(key_signatures); - if(uniq_signatures.size > 1) - return this.error(`${this.operations.length} '${this.name}' operations must have an identical set of responses.`); - } - - validate_query_parameters(): ValidationError | void { - const query_signatures = this.operations.map((op) => op.query_params().sort().join('#$@')); - const uniq_signatures = new Set(query_signatures); - if(uniq_signatures.size > 1) - return this.error(`${this.operations.length} '${this.name}' operations must have an identical set of query parameters.`); - } -} \ No newline at end of file + static readonly OP_PRIORITY = ['operationId', 'x-operation-group', 'x-ignorable', 'deprecated', + 'x-deprecation-message', 'x-version-added', 'x-version-deprecated', 'x-version-removed', + 'description', 'externalDocs', 'parameters', 'requestBody', 'responses'] + + name: string + operations: Operation[] + + constructor (file: string, name: string, operations: Operation[]) { + super(file, `Operation Group: ${name}`) + this.name = name + this.operations = operations + } + + validate (): ValidationError[] { + const ops_errors = this.operations.flatMap((op) => op.validate()) + if (ops_errors.length > 0) return ops_errors + if (this.operations.length === 1) return [] + return [ + this.validate_description(), + this.validate_external_docs(), + this.validate_request_body(), + this.validate_responses(), + this.validate_query_parameters() + ].filter((e) => e) as ValidationError[] + } + + validate_description (): ValidationError | undefined { + const uniq_descriptions = new Set(this.operations.map((op) => op.spec.description)) + if (uniq_descriptions.size > 1) { return this.error(`${this.operations.length} '${this.name}' operations must have identical description property.`) } + } + + validate_external_docs (): ValidationError | undefined { + const uniq_external_docs = new Set(this.operations.map((op) => op.spec.externalDocs?.url)) + if (uniq_external_docs.size > 1) { return this.error(`${this.operations.length} '${this.name}' operations must have identical externalDocs property.`) } + } + + validate_request_body (): ValidationError | undefined { + const uniq_request_bodies = new Set(this.operations.map((op) => op.spec.requestBody?.$ref)) + if (uniq_request_bodies.size > 1) { return this.error(`${this.operations.length} '${this.name}' operations must have identical requestBody property.`) } + } + + validate_responses (): ValidationError | undefined { + const key_signatures = this.operations.map((op) => Object.keys(op.spec.responses).sort().join('#$@')) + const uniq_signatures = new Set(key_signatures) + if (uniq_signatures.size > 1) { return this.error(`${this.operations.length} '${this.name}' operations must have an identical set of responses.`) } + } + + validate_query_parameters (): ValidationError | undefined { + const query_signatures = this.operations.map((op) => op.query_params().sort().join('#$@')) + const uniq_signatures = new Set(query_signatures) + if (uniq_signatures.size > 1) { return this.error(`${this.operations.length} '${this.name}' operations must have an identical set of query parameters.`) } + } +} diff --git a/tools/linter/components/RootFile.ts b/tools/linter/components/RootFile.ts deleted file mode 100644 index 5e84bac7a..000000000 --- a/tools/linter/components/RootFile.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {ParameterSpec, ValidationError} from "../../types"; -import FileValidator from "./base/FileValidator"; - -export default class RootFile extends FileValidator { - constructor(file_path: string) { - super(file_path); - this.file = file_path.split('/').pop()!; - } - - validate_file(): ValidationError[] { - return [ - this.validate_paths(), - this.validate_params(), - ].flat(); - } - - validate_paths(): ValidationError[] { - return Object.entries(this.spec().paths).map(([path, spec]) => { - if(!spec?.$ref) - return this.error(`Every path must be a reference object to a path in a namespace file.`, `Path: ${path}`); - }).filter((e) => e) as ValidationError[]; - } - - validate_params(): ValidationError[] { - const params = (this.spec().components?.parameters || {}) as Record; - return Object.entries(params).map(([name, param]) => { - const expected = `_global::${param.in}.${param.name}`; - if(name !== expected) - return this.error(`Parameters in root file must be in the format '_global::{in}.{name}'. Expected '${expected}'.`, `#/components/parameters/${name}`); - if(!param['x-global']) - return this.error(`Parameters in root file must have 'x-global' extension set to true.`, `#/components/parameters/${name}`); - }).filter((e) => e) as ValidationError[]; - } -} \ No newline at end of file diff --git a/tools/linter/components/Schema.ts b/tools/linter/components/Schema.ts index d7ab3d1a0..38ac29017 100644 --- a/tools/linter/components/Schema.ts +++ b/tools/linter/components/Schema.ts @@ -1,25 +1,24 @@ -import ValidatorBase from "./base/ValidatorBase"; -import {OpenAPIV3} from "openapi-types"; -import {ValidationError} from "../../types"; +import ValidatorBase from './base/ValidatorBase' +import { type OpenAPIV3 } from 'openapi-types' +import { type ValidationError } from '../../types' -const NAME_REGEX = /^[A-Za-z0-9]+$/; +const NAME_REGEX = /^[A-Za-z0-9]+$/ export default class Schema extends ValidatorBase { - name: string; - spec: OpenAPIV3.SchemaObject; + name: string + spec: OpenAPIV3.SchemaObject - constructor(error_file: string, name: string, spec: OpenAPIV3.SchemaObject) { - super(error_file, `#/components/schemas/${name}`); - this.name = name - this.spec = spec; - } + constructor (error_file: string, name: string, spec: OpenAPIV3.SchemaObject) { + super(error_file, `#/components/schemas/${name}`) + this.name = name + this.spec = spec + } - validate(): ValidationError[] { - return [this.validate_name()].filter(e => e) as ValidationError[]; - } + validate (): ValidationError[] { + return [this.validate_name()].filter(e => e) as ValidationError[] + } - validate_name(): ValidationError | undefined { - if(!NAME_REGEX.test(this.name)) - return this.error(`Invalid schema name '${this.name}'. Only alphanumeric characters are allowed.`,); - } -} \ No newline at end of file + validate_name (): ValidationError | undefined { + if (!NAME_REGEX.test(this.name)) { return this.error(`Invalid schema name '${this.name}'. Only alphanumeric characters are allowed.`) } + } +} diff --git a/tools/linter/components/SchemaFile.ts b/tools/linter/components/SchemaFile.ts index 2d989365a..074b76892 100644 --- a/tools/linter/components/SchemaFile.ts +++ b/tools/linter/components/SchemaFile.ts @@ -1,42 +1,41 @@ -import FileValidator from "./base/FileValidator"; -import {ValidationError} from "../../types"; -import Schema from "./Schema"; -import {OpenAPIV3} from "openapi-types"; +import FileValidator from './base/FileValidator' +import { type ValidationError } from '../../types' +import Schema from './Schema' +import { type OpenAPIV3 } from 'openapi-types' -const CATEGORY_REGEX = /^[a-z_]+\.[a-z_]+$/; -const NAME_REGEX = /^[a-z]+[a-z_]*[a-z]+$/; +const CATEGORY_REGEX = /^[a-z_]+\.[a-z_]+$/ +const NAME_REGEX = /^[a-z]+[a-z_]*[a-z]+$/ export default class SchemaFile extends FileValidator { - category: string; - _schemas: Schema[] | undefined; + category: string + private _schemas: Schema[] | undefined - constructor(file_path: string) { - super(file_path); - this.category = file_path.split('/').slice(-1)[0].replace('.yaml', ''); - } + constructor (file_path: string) { + super(file_path) + this.category = file_path.split('/').slice(-1)[0].replace('.yaml', '') + } - validate_file(): ValidationError[] { - const category_error = this.validate_category(); - if(category_error) return [category_error]; + validate_file (): ValidationError[] { + const category_error = this.validate_category() + if (category_error) return [category_error] - return [ - ...this.schemas().flatMap(s => s.validate()) - ]; - } + return [ + ...this.schemas().flatMap(s => s.validate()) + ] + } - schemas(): Schema[] { - if(this._schemas) return this._schemas; - return Object.entries(this.spec().components?.schemas || {}).map(([name, spec]) => { - return new Schema(this.file, name, spec as OpenAPIV3.SchemaObject); - }); - } + schemas (): Schema[] { + if (this._schemas) return this._schemas + this._schemas = Object.entries(this.spec().components?.schemas ?? {}).map(([name, spec]) => { + return new Schema(this.file, name, spec as OpenAPIV3.SchemaObject) + }) + return this._schemas + } - validate_category(category = this.category): ValidationError | void { - if(category === '_common') return; - if(!category.match(CATEGORY_REGEX)) - return this.error(`Invalid category name '${category}'. Must match regex: /${CATEGORY_REGEX.source}/.`, 'File Name'); - const name = category.split('.')[1]; - if(name !== '_common' && !name.match(NAME_REGEX)) - return this.error(`Invalid category name '${category}'. '${name}' does not match regex: /${NAME_REGEX.source}/.`, 'File Name'); - } -} \ No newline at end of file + validate_category (category = this.category): ValidationError | undefined { + if (category === '_common') return + if (!CATEGORY_REGEX.test(category)) { return this.error(`Invalid category name '${category}'. Must match regex: /${CATEGORY_REGEX.source}/.`, 'File Name') } + const name = category.split('.')[1] + if (name !== '_common' && !NAME_REGEX.test(name)) { return this.error(`Invalid category name '${category}'. '${name}' does not match regex: /${NAME_REGEX.source}/.`, 'File Name') } + } +} diff --git a/tools/linter/components/SchemasFolder.ts b/tools/linter/components/SchemasFolder.ts index 3f65eae54..e40ff4f87 100644 --- a/tools/linter/components/SchemasFolder.ts +++ b/tools/linter/components/SchemasFolder.ts @@ -1,13 +1,13 @@ -import SchemaFile from "./SchemaFile"; -import FolderValidator from "./base/FolderValidator"; -import {ValidationError} from "../../types"; +import SchemaFile from './SchemaFile' +import FolderValidator from './base/FolderValidator' +import { type ValidationError } from '../../types' export default class SchemasFolder extends FolderValidator { - constructor(folder_path: string) { - super(folder_path, SchemaFile); - } + constructor (folder_path: string) { + super(folder_path, SchemaFile) + } - validate_folder(): ValidationError[] { - return []; - } -} \ No newline at end of file + validate_folder (): ValidationError[] { + return [] + } +} diff --git a/tools/linter/components/SupersededOperationsFile.ts b/tools/linter/components/SupersededOperationsFile.ts new file mode 100644 index 000000000..3591ede8e --- /dev/null +++ b/tools/linter/components/SupersededOperationsFile.ts @@ -0,0 +1,5 @@ +import FileValidator from './base/FileValidator' + +export default class SupersededOperationsFile extends FileValidator { + has_json_schema = true +} diff --git a/tools/linter/components/base/FileValidator.ts b/tools/linter/components/base/FileValidator.ts index 76d6632ac..a0e0fa7c7 100644 --- a/tools/linter/components/base/FileValidator.ts +++ b/tools/linter/components/base/FileValidator.ts @@ -1,45 +1,62 @@ -import ValidatorBase from "./ValidatorBase"; -import {ValidationError} from "../../../types"; -import fs from "fs"; -import YAML from "yaml"; -import {OpenAPIV3} from "openapi-types"; +import ValidatorBase from './ValidatorBase' +import { type ValidationError } from '../../../types' +import { type OpenAPIV3 } from 'openapi-types' +import { read_yaml } from '../../../helpers' +import AJV from 'ajv' +import addFormats from 'ajv-formats' export default class FileValidator extends ValidatorBase { - file_path: string; - _spec: OpenAPIV3.Document | undefined; + file_path: string + has_json_schema: boolean = false + protected _spec: OpenAPIV3.Document | undefined - constructor(file_path: string) { - super(file_path.split('/').slice(-2).join('/')); - this.file_path = file_path; - } + constructor (file_path: string) { + super(file_path.split('/').slice(-2).join('/')) + this.file_path = file_path + } - spec(): OpenAPIV3.Document { - if(this._spec) return this._spec; - return this._spec = YAML.parse(fs.readFileSync(this.file_path, 'utf8')) || {}; - } + spec (): OpenAPIV3.Document { + if (this._spec) return this._spec + this._spec = read_yaml(this.file_path) as OpenAPIV3.Document + return this._spec + } - validate(...args: any[]): ValidationError[] { - const extension_error = this.validate_extension(); - if(extension_error) return [extension_error]; - const yaml_error = this.validate_yaml(); - if(yaml_error) return [yaml_error]; - return this.validate_file(); - } + validate (): ValidationError[] { + const extension_error = this.validate_extension() + if (extension_error) return [extension_error] + const yaml_error = this.validate_yaml() + if (yaml_error) return [yaml_error] + const json_schema_error = this.validate_json_schema() + if (json_schema_error) return [json_schema_error] + return this.validate_file() + } - validate_file(...args: any[]): ValidationError[] { - throw new Error('Method not implemented.'); - } + validate_file (): ValidationError[] { + return [] + } + + validate_extension (): ValidationError | undefined { + if (!this.file_path.endsWith('.yaml')) { return this.error('Invalid file extension. Only \'.yaml\' files are allowed.', 'File Extension') } + } - validate_extension(): ValidationError | undefined { - if(!this.file_path.endsWith('.yaml')) - return this.error(`Invalid file extension. Only '.yaml' files are allowed.`, 'File Extension'); + validate_yaml (): ValidationError | undefined { + try { + this.spec() + } catch (e: any) { + return this.error('Unable to read or parse YAML.', 'File Content') } + } - validate_yaml(): ValidationError | undefined { - try { - this.spec(); - } catch (e: any) { - return this.error(`Unable to read or parse YAML.`, 'File Content'); - } + validate_json_schema (): ValidationError | undefined { + if (!this.has_json_schema) return + const json_schema_path: string = (this.spec() as any).$schema ?? '' + if (json_schema_path === '') return this.error('JSON Schema is required but not found in this file.', '$schema') + const schema = read_yaml(json_schema_path) + const ajv = new AJV({ schemaId: 'id' }) + addFormats(ajv) + const validator = ajv.compile(schema) + if (!validator(this.spec())) { + return this.error(`File content does not match JSON schema found in '${json_schema_path}':\n ${JSON.stringify(validator.errors, null, 2)}`) } -} \ No newline at end of file + } +} diff --git a/tools/linter/components/base/FolderValidator.ts b/tools/linter/components/base/FolderValidator.ts index 7f7e0be1d..30eafbfac 100644 --- a/tools/linter/components/base/FolderValidator.ts +++ b/tools/linter/components/base/FolderValidator.ts @@ -1,30 +1,29 @@ -import fs from "fs"; -import ValidatorBase from "./ValidatorBase"; -import FileValidator from "./FileValidator"; -import {ValidationError} from "../../../types"; +import fs from 'fs' +import ValidatorBase from './ValidatorBase' +import type FileValidator from './FileValidator' +import { type ValidationError } from '../../../types' export default class FolderValidator extends ValidatorBase { - folder_path: string; - files: F[]; + folder_path: string + files: F[] - constructor(folder_path: string, file_type: new (file_path: string) => F) { - const parts = folder_path.split('/').reverse(); - const folder_name = (parts[0] === undefined ? parts[1] : parts[0]) + '/'; - super(folder_name, 'Folder'); - this.folder_path = folder_path; - this.files = fs.readdirSync(this.folder_path).sort() - .filter((file) => file !== '.gitkeep') - .map((file) => { return new file_type(`${this.folder_path}/${file}`) as F; }); - } + constructor (folder_path: string, file_type: new (file_path: string) => F) { + const parts = folder_path.split('/').reverse() + const folder_name = (parts[0] ?? parts[1]) + '/' + super(folder_name, 'Folder') + this.folder_path = folder_path + this.files = fs.readdirSync(this.folder_path).sort() + .filter((file) => file !== '.gitkeep') + .map((file) => { return new file_type(`${this.folder_path}/${file}`) }) + } - validate(): ValidationError[] { - return [ - ...this.files.flatMap((file) => file.validate()), - ...this.validate_folder(), - ]; - } + validate (): ValidationError[] { + const file_errors = this.files.flatMap((file) => file.validate()) + if (file_errors.length > 0) return file_errors + return this.validate_folder() + } - validate_folder(): ValidationError[] { - throw new Error('Method not implemented.'); - } -} \ No newline at end of file + validate_folder (): ValidationError[] { + throw new Error('Method not implemented.') + } +} diff --git a/tools/linter/components/base/ValidatorBase.ts b/tools/linter/components/base/ValidatorBase.ts index d47483455..7b0deaafe 100644 --- a/tools/linter/components/base/ValidatorBase.ts +++ b/tools/linter/components/base/ValidatorBase.ts @@ -1,18 +1,18 @@ -import { ValidationError } from "../../../types"; +import { type ValidationError } from '../../../types' export default class ValidatorBase { - file: string; - location: string | undefined; + file: string + location: string | undefined - constructor(file: string, location?: string) { - this.file = file; - this.location = location; - } + constructor (file: string, location?: string) { + this.file = file + this.location = location + } - error(message: string, location = this.location, file = this.file): ValidationError { - return { file, location, message }; - } + error (message: string, location = this.location, file = this.file): ValidationError { + return { file, location, message } + } - validate(): ValidationError[] { - throw new Error('Method not implemented.'); - } -} \ No newline at end of file + validate (): ValidationError[] { + throw new Error('Method not implemented.') + } +} diff --git a/tools/linter/lint.ts b/tools/linter/lint.ts index f137b23a0..9b8d05abe 100644 --- a/tools/linter/lint.ts +++ b/tools/linter/lint.ts @@ -1,16 +1,15 @@ -import SpecValidator from "./SpecValidator"; +import SpecValidator from './SpecValidator' +const root_folder = process.argv[2] ?? '../spec' +const validator = new SpecValidator(root_folder) +const errors = validator.validate() -const root_folder = process.argv[2] || '../spec'; -const validator = new SpecValidator(root_folder); -const errors = validator.validate(); - -if(errors.length === 0) { - console.log('No errors found.'); - process.exit(0); -} else { - console.log('Errors found:\n'); - errors.forEach(e => console.error(e)); - console.log('\nTotal errors:', errors.length) - process.exit(1); -} \ No newline at end of file +if (errors.length === 0) { + console.log('No errors found.') + process.exit(0) +} else { + console.log('Errors found:\n') + errors.forEach(e => { console.error(e) }) + console.log('\nTotal errors:', errors.length) + process.exit(1) +} diff --git a/tools/linter/utils/SpecificationVisitor.ts b/tools/linter/utils/SpecificationVisitor.ts new file mode 100644 index 000000000..d94731a73 --- /dev/null +++ b/tools/linter/utils/SpecificationVisitor.ts @@ -0,0 +1,117 @@ +import { is_array_schema, is_ref, type KeysMatching, type MaybeRef, type SpecificationContext } from './index' +import { OpenAPIV3 } from 'openapi-types' + +type VisitorCallback = (ctx: SpecificationContext, o: NonNullable) => void +type SchemaVisitorCallback = VisitorCallback> + +function visit ( + ctx: SpecificationContext, + parent: Parent, + key: Key, + visitor: VisitorCallback +): void { + const child = parent[key] + if (child == null) return + visitor(ctx.child(key as string), child) +} + +type EnumerableKeys = KeysMatching | undefined> | KeysMatching | undefined> +type ElementOf = T extends Record ? V : T extends ArrayLike ? V : never + +function visit_each> ( + ctx: SpecificationContext, + parent: Parent, + key: Key, + visitor: VisitorCallback> +): void { + const children = parent[key] + if (children == null) return + ctx = ctx.child(key as string) + Object.entries>(children).forEach(([key, child]) => { + if (child == null) return + visitor(ctx.child(key), child) + }) +} + +export class SpecificationVisitor { + visit_specification (ctx: SpecificationContext, specification: OpenAPIV3.Document): void { + visit_each(ctx, specification, 'paths', this.visit_path.bind(this)) + visit(ctx, specification, 'components', this.visit_components.bind(this)) + } + + visit_path (ctx: SpecificationContext, path: OpenAPIV3.PathItemObject): void { + visit_each(ctx, path, 'parameters', this.visit_parameter.bind(this)) + + for (const method of Object.values(OpenAPIV3.HttpMethods)) { + visit(ctx, path, method, this.visit_operation.bind(this)) + } + } + + visit_operation (ctx: SpecificationContext, operation: OpenAPIV3.OperationObject): void { + visit_each(ctx, operation, 'parameters', this.visit_parameter.bind(this)) + visit(ctx, operation, 'requestBody', this.visit_request_body.bind(this)) + visit_each(ctx, operation, 'responses', this.visit_response.bind(this)) + } + + visit_components (ctx: SpecificationContext, components: OpenAPIV3.ComponentsObject): void { + visit_each(ctx, components, 'parameters', this.visit_parameter.bind(this)) + visit_each(ctx, components, 'requestBodies', this.visit_request_body.bind(this)) + visit_each(ctx, components, 'responses', this.visit_response.bind(this)) + visit_each(ctx, components, 'schemas', this.visit_schema.bind(this)) + } + + visit_parameter (ctx: SpecificationContext, parameter: MaybeRef): void { + if (is_ref(parameter)) return + + visit(ctx, parameter, 'schema', this.visit_schema.bind(this)) + } + + visit_request_body (ctx: SpecificationContext, request_body: MaybeRef): void { + if (is_ref(request_body)) return + + visit_each(ctx, request_body, 'content', this.visit_media_type.bind(this)) + } + + visit_response (ctx: SpecificationContext, response: MaybeRef): void { + if (is_ref(response)) return + + visit_each(ctx, response, 'content', this.visit_media_type.bind(this)) + } + + visit_media_type (ctx: SpecificationContext, media_type: OpenAPIV3.MediaTypeObject): void { + visit(ctx, media_type, 'schema', this.visit_schema.bind(this)) + } + + visit_schema (ctx: SpecificationContext, schema: MaybeRef): void { + if (is_ref(schema)) return + + if (is_array_schema(schema)) { + visit(ctx, schema, 'items', this.visit_schema.bind(this)) + } + + visit(ctx, schema, 'additionalProperties', (ctx, v) => { + if (typeof v !== 'object') return + this.visit_schema(ctx, v) + }) + + visit_each(ctx, schema, 'properties', this.visit_schema.bind(this)) + visit_each(ctx, schema, 'allOf', this.visit_schema.bind(this)) + visit_each(ctx, schema, 'anyOf', this.visit_schema.bind(this)) + visit_each(ctx, schema, 'oneOf', this.visit_schema.bind(this)) + visit(ctx, schema, 'not', this.visit_schema.bind(this)) + } +} + +export class SchemaVisitor extends SpecificationVisitor { + private readonly _callback: SchemaVisitorCallback + + constructor (callback: SchemaVisitorCallback) { + super() + this._callback = callback + } + + visit_schema (ctx: SpecificationContext, schema: MaybeRef): void { + super.visit_schema(ctx, schema) + this._callback(ctx, schema) + } +} diff --git a/tools/linter/utils/index.ts b/tools/linter/utils/index.ts new file mode 100644 index 000000000..d20fbf5d4 --- /dev/null +++ b/tools/linter/utils/index.ts @@ -0,0 +1,62 @@ +import { type OpenAPIV3 } from 'openapi-types' +import { type ValidationError } from '../../types' + +export function is_ref (o: MaybeRef): o is OpenAPIV3.ReferenceObject { + return '$ref' in o +} + +export function is_array_schema (schema: OpenAPIV3.SchemaObject): schema is OpenAPIV3.ArraySchemaObject { + return schema.type === 'array' +} + +export function is_primitive_schema (schema: OpenAPIV3.SchemaObject): boolean { + return schema.type === 'boolean' || + schema.type === 'integer' || + schema.type === 'number' || + schema.type === 'string' +} + +export class SpecificationContext { + private readonly _file: string + private readonly _location: string[] + + constructor (file: string, location?: string[]) { + this._file = file + this._location = location ?? ['#'] + } + + parent (): SpecificationContext { + if (this._location.length <= 1) return this + return new SpecificationContext(this._file, this._location.slice(0, -1)) + } + + child (child: string): SpecificationContext { + return new SpecificationContext(this._file, [...this._location, child]) + } + + error (message: string): ValidationError { + return { file: this._file, location: this.location, message } + } + + get file (): string { + return this._file + } + + get location (): string { + return this._location + .map(k => k + .replaceAll('~', '~0') + .replaceAll('/', '~1')) + .join('/') + } + + get key (): string { + return this._location[this._location.length - 1] + } +} + +export type MaybeRef = O | OpenAPIV3.ReferenceObject + +export type KeysMatching = { + [K in keyof T]-?: T[K] extends V ? K : never +}[keyof T] diff --git a/tools/merger/GlobalParamsGenerator.ts b/tools/merger/GlobalParamsGenerator.ts new file mode 100644 index 000000000..612c18fa0 --- /dev/null +++ b/tools/merger/GlobalParamsGenerator.ts @@ -0,0 +1,36 @@ +import { type OpenAPIV3 } from 'openapi-types' +import _ from 'lodash' +import { read_yaml } from '../helpers' + +export default class GlobalParamsGenerator { + global_params: Record + + constructor (root_path: string) { + const file_path = root_path + '/_global_parameters.yaml' + const spec = read_yaml(file_path) as OpenAPIV3.Document + this.global_params = this.create_global_params(spec) + } + + generate (spec: Record): void { + spec.components.parameters = { ...this.global_params, ...spec.components.parameters } + + const global_param_refs = Object.keys(this.global_params).map(param => ({ $ref: `#/components/parameters/${param}` })) + Object.entries(spec.paths as Document).forEach(([path, path_item]) => { + Object.entries(path_item as Document).forEach(([method, operation]) => { + const params = operation.parameters ?? [] + operation.parameters = [...params, ...Object.values(global_param_refs)] + }) + }) + } + + create_global_params (spec: OpenAPIV3.Document): Record { + const params = (spec.components?.parameters ?? {}) as Record + _.entries(params).forEach(([original_key, param]) => { + const global_key = `_global::${param.in}.${param.name}` + _.set(param, 'x-global', true) + _.unset(params, original_key) + params[global_key] = param + }) + return params + } +} diff --git a/tools/merger/OpenApiMerger.ts b/tools/merger/OpenApiMerger.ts index 2e108ce55..0bb0be431 100644 --- a/tools/merger/OpenApiMerger.ts +++ b/tools/merger/OpenApiMerger.ts @@ -1,127 +1,122 @@ -import { OpenAPIV3 } from "openapi-types"; -import fs from 'fs'; -import _ from 'lodash'; -import yaml from 'yaml'; -import { write2file } from '../helpers'; +import { type OpenAPIV3 } from 'openapi-types' +import fs from 'fs' +import _ from 'lodash' +import { read_yaml, write_yaml } from '../helpers' +import SupersededOpsGenerator from './SupersededOpsGenerator' +import GlobalParamsGenerator from './GlobalParamsGenerator' // Create a single-file OpenAPI spec from multiple files for OpenAPI validation and programmatic consumption export default class OpenApiMerger { - root_path: string; - root_folder: string; - spec: Record; - global_param_refs: OpenAPIV3.ReferenceObject[]; - - paths: Record> = {}; // namespace -> path -> path_item_object - schemas: Record> = {}; // category -> schema -> schema_object - - constructor(root_path: string) { - this.root_path = fs.realpathSync(root_path); - this.root_folder = this.root_path.split('/').slice(0, -1).join('/'); - this.spec = yaml.parse(fs.readFileSync(this.root_path, 'utf8')); - const global_params: OpenAPIV3.ParameterObject = this.spec.components?.parameters || {}; - this.global_param_refs = Object.keys(global_params).map(param => ({$ref: `#/components/parameters/${param}`})); - this.spec.components = { - parameters: global_params, - requestBodies: {}, - responses: {}, - schemas: {}, - }; + root_folder: string + spec: Record + + paths: Record> = {} // namespace -> path -> path_item_object + schemas: Record> = {} // category -> schema -> schema_object + + constructor (root_folder: string) { + this.root_folder = fs.realpathSync(root_folder) + this.spec = { + openapi: '3.1.0', + info: read_yaml(`${this.root_folder}/_info.yaml`, true), + paths: {}, + components: { + parameters: {}, + requestBodies: {}, + responses: {}, + schemas: {} + } } - - merge(output_path?: string): OpenAPIV3.Document { - this.#merge_schemas(); - this.#merge_namespaces(); - this.#apply_global_params(); - this.#sort_spec_keys(); - - if(output_path) write2file(output_path, this.spec); - return this.spec as OpenAPIV3.Document; - } - - // Apply global parameters to all operations in the spec. - #apply_global_params(): void { - Object.entries(this.spec.paths).forEach(([path, pathItem]) => { - Object.entries(pathItem!).forEach(([method, operation]) => { - const params = operation.parameters || []; - operation.parameters = [...params, ...Object.values(this.global_param_refs)]; - }); - }); - } - - // Merge files from /namespaces folder. - #merge_namespaces(): void { - const folder = `${this.root_folder}/namespaces`; - fs.readdirSync(folder).forEach(file => { - const spec = yaml.parse(fs.readFileSync(`${folder}/${file}`, 'utf8')); - const namespace = file.split('.yaml')[0]; - this.redirect_refs_in_namespace(spec); - this.paths[namespace] = spec['paths']; - this.spec.components.parameters = {...this.spec.components.parameters, ...spec['components']['parameters']}; - this.spec.components.responses = {...this.spec.components.responses, ...spec['components']['responses']}; - this.spec.components.requestBodies = {...this.spec.components.requestBodies, ...spec['components']['requestBodies']}; - }); - - Object.entries(this.spec.paths).forEach(([path, refObj]) => { - const ref = (refObj as Record).$ref!; - const namespace = ref.match(/namespaces\/(.*)\.yaml/)![1]; - this.spec.paths[path] = this.paths[namespace][path]; - }); + } + + merge (output_path: string = ''): OpenAPIV3.Document { + this.#merge_schemas() + this.#merge_namespaces() + this.#sort_spec_keys() + this.#generate_global_params() + this.#generate_superseded_ops() + + if (output_path !== '') write_yaml(output_path, this.spec) + return this.spec as OpenAPIV3.Document + } + + // Merge files from /namespaces folder. + #merge_namespaces (): void { + const folder = `${this.root_folder}/namespaces` + fs.readdirSync(folder).forEach(file => { + const spec = read_yaml(`${folder}/${file}`) + this.redirect_refs_in_namespace(spec) + this.spec.paths = { ...this.spec.paths, ...spec.paths } + this.spec.components.parameters = { ...this.spec.components.parameters, ...spec.components.parameters } + this.spec.components.responses = { ...this.spec.components.responses, ...spec.components.responses } + this.spec.components.requestBodies = { ...this.spec.components.requestBodies, ...spec.components.requestBodies } + }) + } + + // Redirect schema references in namespace files to local references in single-file spec. + redirect_refs_in_namespace (obj: any): void { + const ref: string = obj.$ref + if (ref?.startsWith('../schemas/')) { obj.$ref = ref.replace('../schemas/', '#/components/schemas/').replace('.yaml#/components/schemas/', ':') } + + for (const key in obj) { + if (typeof obj[key] === 'object') { this.redirect_refs_in_namespace(obj[key]) } } - - // Redirect schema references in namespace files to local references in single-file spec. - redirect_refs_in_namespace(obj: Record): void { - const ref = obj.$ref; - if(ref?.startsWith('../schemas/')) - obj.$ref = ref.replace('../schemas/', '#/components/schemas/').replace('.yaml#/components/schemas/', ':'); - - for(const key in obj) - if(typeof obj[key] === 'object') - this.redirect_refs_in_namespace(obj[key]); - } - - // Merge files from /schemas folder. - #merge_schemas(): void { - const folder = `${this.root_folder}/schemas`; - fs.readdirSync(folder).forEach(file => { - const spec = yaml.parse(fs.readFileSync(`${folder}/${file}`, 'utf8')); - const category = file.split('.yaml')[0]; - this.redirect_refs_in_schema(category, spec); - this.schemas[category] = spec['components']['schemas'] as Record; - }); - - Object.entries(this.schemas).forEach(([category, schemas]) => { - Object.entries(schemas).forEach(([name, schemaObj]) => { - this.spec.components.schemas[`${category}:${name}`] = schemaObj; - }); - }); - } - - // Redirect schema references in schema files to local references in single-file spec. - redirect_refs_in_schema(category: string, obj: Record): void { - const ref = obj.$ref; - if(ref) - if(ref.startsWith('#/components/schemas')) - obj.$ref = `#/components/schemas/${category}:${ref.split('/').pop()}`; - else { - const other_category = ref.match(/(.*)\.yaml/)![1]; - obj.$ref = `#/components/schemas/${other_category}:${ref.split('/').pop()}`; - } - - for(const key in obj) - if(typeof obj[key] === 'object') - this.redirect_refs_in_schema(category, obj[key]); + } + + // Merge files from /schemas folder. + #merge_schemas (): void { + const folder = `${this.root_folder}/schemas` + fs.readdirSync(folder).forEach(file => { + const spec = read_yaml(`${folder}/${file}`) + const category = file.split('.yaml')[0] + this.redirect_refs_in_schema(category, spec) + this.schemas[category] = spec.components.schemas as Record + }) + + Object.entries(this.schemas).forEach(([category, schemas]) => { + Object.entries(schemas).forEach(([name, schema_obj]) => { + this.spec.components.schemas[`${category}:${name}`] = schema_obj + }) + }) + } + + // Redirect schema references in schema files to local references in single-file spec. + redirect_refs_in_schema (category: string, obj: any): void { + const ref: string = obj.$ref ?? '' + if (ref !== '') { + if (ref.startsWith('#/components/schemas')) { obj.$ref = `#/components/schemas/${category}:${ref.split('/').pop()}` } else { + const other_category = ref.match(/(.*)\.yaml/)?.[1] ?? '' + if (other_category === '') throw new Error(`Invalid schema reference: ${ref}`) + obj.$ref = `#/components/schemas/${other_category}:${ref.split('/').pop()}` + } } - // Sort keys in the spec to make it easier to read and compare. - #sort_spec_keys(): void { - this.spec.components.schemas = _.fromPairs(Object.entries(this.spec.components.schemas).sort()); - this.spec.components.parameters = _.fromPairs(Object.entries(this.spec.components.parameters).sort()); - this.spec.components.responses = _.fromPairs(Object.entries(this.spec.components.responses).sort()); - this.spec.components.requestBodies = _.fromPairs(Object.entries(this.spec.components.requestBodies).sort()); - - this.spec.paths = _.fromPairs(Object.entries(this.spec.paths).sort()); - Object.entries(this.spec.paths).forEach(([path, pathItem]) => { - this.spec.paths[path] = _.fromPairs(Object.entries(pathItem!).sort()); - }); + for (const key in obj) { + if (typeof obj[key] === 'object') { this.redirect_refs_in_schema(category, obj[key]) } } -} \ No newline at end of file + } + + // Sort keys in the spec to make it easier to read and compare. + #sort_spec_keys (): void { + this.spec.components.schemas = _.fromPairs(Object.entries(this.spec.components.schemas as Document).sort((a, b) => a[0].localeCompare(b[0]))) + this.spec.components.parameters = _.fromPairs(Object.entries(this.spec.components.parameters as Document).sort((a, b) => a[0].localeCompare(b[0]))) + this.spec.components.responses = _.fromPairs(Object.entries(this.spec.components.responses as Document).sort((a, b) => a[0].localeCompare(b[0]))) + this.spec.components.requestBodies = _.fromPairs(Object.entries(this.spec.components.requestBodies as Document).sort((a, b) => a[0].localeCompare(b[0]))) + + this.spec.paths = _.fromPairs(Object.entries(this.spec.paths as Document).sort((a, b) => a[0].localeCompare(b[0]))) + Object.entries(this.spec.paths as Document).forEach(([path, path_item]) => { + this.spec.paths[path] = _.fromPairs(Object.entries(path_item as Document).sort((a, b) => a[0].localeCompare(b[0]))) + }) + } + + // Generate global parameters from _global_params.yaml file. + #generate_global_params (): void { + const gen = new GlobalParamsGenerator(this.root_folder) + gen.generate(this.spec) + } + + // Generate superseded operations from _superseded_operations.yaml file. + #generate_superseded_ops (): void { + const gen = new SupersededOpsGenerator(this.root_folder) + gen.generate(this.spec) + } +} diff --git a/tools/merger/OpenDistro.ts b/tools/merger/OpenDistro.ts new file mode 100644 index 000000000..e35cace6e --- /dev/null +++ b/tools/merger/OpenDistro.ts @@ -0,0 +1,23 @@ +import { type HttpVerb, type OperationPath, type SupersededOperationMap } from '../types' +import { read_yaml, write_yaml } from '../helpers' + +// One-time script to generate _superseded_operations.yaml file for OpenDistro +// Keeping this for now in case we need to update the file in the near future. Can be removed after a few months. +// TODO: Remove this file in 2025. +export default class OpenDistro { + input: Record + output: SupersededOperationMap = {} + + constructor (file_path: string) { + this.input = read_yaml(file_path) + this.build_output() + write_yaml(file_path, this.output) + } + + build_output (): void { + for (const [path, operations] of Object.entries(this.input)) { + const replaced_by = path.replace('_opendistro', '_plugins') + this.output[path] = { superseded_by: replaced_by, operations } + } + } +} diff --git a/tools/merger/SupersededOpsGenerator.ts b/tools/merger/SupersededOpsGenerator.ts new file mode 100644 index 000000000..747204c61 --- /dev/null +++ b/tools/merger/SupersededOpsGenerator.ts @@ -0,0 +1,48 @@ +import { type OperationSpec, type SupersededOperationMap } from '../types' +import _ from 'lodash' +import { read_yaml } from '../helpers' + +export default class SupersededOpsGenerator { + superseded_ops: SupersededOperationMap + + constructor (root_path: string) { + const file_path = root_path + '/_superseded_operations.yaml' + this.superseded_ops = read_yaml(file_path) as SupersededOperationMap + delete this.superseded_ops.$schema + } + + generate (spec: Record): void { + for (const [path, { superseded_by, operations }] of _.entries(this.superseded_ops)) { + const regex = this.path_to_regex(superseded_by) + const operation_keys = operations.map(op => op.toLowerCase()) + const superseded_path = this.copy_params(superseded_by, path) + const path_entry = _.entries(spec.paths as Document).find(([path, _]) => regex.test(path)) + if (!path_entry) console.log(`Path not found: ${superseded_by}`) + else spec.paths[superseded_path] = this.path_object(path_entry[1], operation_keys) + } + } + + path_object (obj: any, keys: string[]): Record { + const cloned_obj = _.cloneDeep(_.pick(obj, keys)) + for (const key in cloned_obj) { + const operation = cloned_obj[key] as OperationSpec + operation.operationId = operation.operationId + '_superseded' + operation.deprecated = true + operation['x-ignorable'] = true + } + return cloned_obj + } + + path_to_regex (path: string): RegExp { + const source = '^' + path.replace(/\{.+?}/g, '\\{.+?\\}').replace(/\//g, '\\/') + '$' + return new RegExp(source, 'g') + } + + copy_params (source: string, target: string): string { + const target_parts = target.split('/') + const target_params = target_parts.filter(part => part.startsWith('{')) + const source_params = source.split('/').filter(part => part.startsWith('{')).reverse() + if (target_params.length !== source_params.length) { throw new Error('Mismatched parameters in source and target paths: ' + source + ' -> ' + target) } + return target_parts.map((part) => part.startsWith('{') ? source_params.pop() : part).join('/') + } +} diff --git a/tools/merger/merge.ts b/tools/merger/merge.ts index 98758cb34..a32d3059b 100644 --- a/tools/merger/merge.ts +++ b/tools/merger/merge.ts @@ -1,7 +1,6 @@ -import OpenApiMerger from "./OpenApiMerger"; +import OpenApiMerger from './OpenApiMerger' - -const root_path: string = process.argv[2] || '../spec/opensearch-openapi.yaml' +const root_path: string = process.argv[2] || '../spec' const output_path: string = process.argv[3] || '../opensearch-openapi.yaml' -const merger = new OpenApiMerger(root_path); -merger.merge(output_path); \ No newline at end of file +const merger = new OpenApiMerger(root_path) +merger.merge(output_path) diff --git a/tools/package-lock.json b/tools/package-lock.json index 4dfeabe3b..20ab5a4d4 100644 --- a/tools/package-lock.json +++ b/tools/package-lock.json @@ -12,17 +12,37 @@ "@apidevtools/swagger-parser": "^10.1.0", "@types/lodash": "^4.14.202", "@types/node": "^20.10.3", + "ajv": "^8.13.0", + "ajv-formats": "^3.0.1", "lodash": "^4.17.21", "ts-node": "^10.9.1", - "typescript": "^5.3.2", + "typescript": "^5.4.5", "yaml": "^2.3.4" }, "devDependencies": { + "@eslint/eslintrc": "^3.0.2", + "@eslint/js": "^9.1.1", "@types/jest": "^29.5.12", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint": "^8.57.0", + "eslint-config-standard-with-typescript": "^43.0.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.1.1", + "globals": "^15.0.0", "jest": "^29.7.0", "ts-jest": "^29.1.2" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -678,6 +698,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", @@ -718,6 +747,147 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz", + "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.1.1.tgz", + "integrity": "sha512-5WoDz3Y19Bg2BnErkZTp0en+c/i9PvgFS7MBe1+m60HjFr0hrphlAGp4yzI7pxpt4xShln4ZyYp4neJm8hmOkQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1072,6 +1242,41 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1200,6 +1405,18 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "node_modules/@types/lodash": { "version": "4.14.202", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", @@ -1213,6 +1430,12 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -1234,628 +1457,2073 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=0.4.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=10" } }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "lru-cache": "^6.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, "peerDependencies": { - "ajv": "^8.5.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "ajv": { + "typescript": { "optional": true } } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": ">=8" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">= 8" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { - "sprintf-js": "~1.0.2" + "balanced-match": "^1.0.0" } }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/builtins/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001591", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", + "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.683", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.683.tgz", + "integrity": "sha512-FmopjiJjkUzqa5F5Sv+wxd8KimtCxyLFOFgRPwEeMLVmP+vHH/GjNGCuIYrCIchbMSiOe+nG/OPBbR/XoExBNA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", + "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-compat-utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-compat-utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "43.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-43.0.1.tgz", + "integrity": "sha512-WfZ986+qzIzX6dcr4yGUyVb/l9N3Z8wPXCc5z/70fljs3UbWhhV+WxrfgsqMToRzuuyX9MqZ974pq2UPhDTOcA==", + "deprecated": "Please use eslint-config-love, instead.", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^6.4.0", + "eslint-config-standard": "17.1.0" }, "peerDependencies": { - "@babel/core": "^7.8.0" + "@typescript-eslint/eslint-plugin": "^6.4.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" + "ms": "^2.1.1" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "dev": true, "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "debug": "^3.2.7" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "ms": "^2.1.1" } }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "node_modules/eslint-plugin-es-x": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz", + "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.5.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ms": "^2.1.1" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "esutils": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "fast-json-stable-stringify": "2.x" + "type-fest": "^0.20.2" }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/eslint-plugin-n/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "node-int64": "^0.4.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/eslint-plugin-n/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001591", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", - "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] + "node_modules/eslint-plugin-n/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "node_modules/eslint/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=6.0" + "node": ">=8" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "dependencies": { + "argparse": "^2.0.1" }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, "engines": { - "node": ">=0.3.1" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.683", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.683.tgz", - "integrity": "sha512-FmopjiJjkUzqa5F5Sv+wxd8KimtCxyLFOFgRPwEeMLVmP+vHH/GjNGCuIYrCIchbMSiOe+nG/OPBbR/XoExBNA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { - "is-arrayish": "^0.2.1" + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, "node_modules/execa": { @@ -1911,12 +3579,55 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -1926,6 +3637,18 @@ "bser": "2.1.1" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1944,11 +3667,40 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=8" + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" } }, "node_modules/fs.realpath": { @@ -1980,6 +3732,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -1998,6 +3777,25 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -2019,6 +3817,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2039,13 +3866,75 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.0.0.tgz", + "integrity": "sha512-m/C/yR4mjO6pXDTm9/R/SpYTAIyaUB4EOzcaaMEl7mds7Mshct9GfejiJNQGjHHbdMPey13Kpu4TMbYi9ex1pw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graceful-fs": { @@ -2054,6 +3943,21 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2063,10 +3967,61 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -2090,56 +4045,175 @@ "node": ">=10.17.0" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "builtin-modules": "^3.3.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -2152,6 +4226,45 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2170,6 +4283,30 @@ "node": ">=6" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2179,6 +4316,61 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2191,6 +4383,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2914,6 +5169,12 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -2925,6 +5186,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2937,6 +5204,15 @@ "node": ">=6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -2955,6 +5231,19 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -2984,6 +5273,12 @@ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3061,6 +5356,15 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -3095,6 +5399,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3140,6 +5453,91 @@ "node": ">=8" } }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3170,6 +5568,23 @@ "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", "peer": true }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3221,6 +5636,18 @@ "node": ">=6" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -3272,6 +5699,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3311,6 +5747,24 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -3374,12 +5828,50 @@ } ] }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3435,6 +5927,15 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -3444,6 +5945,89 @@ "node": ">=10" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3453,6 +6037,38 @@ "semver": "bin/semver.js" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3474,6 +6090,24 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3558,6 +6192,55 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3638,6 +6321,12 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -3665,6 +6354,18 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-jest": { "version": "29.1.2", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", @@ -3783,6 +6484,51 @@ } } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -3804,10 +6550,83 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3816,6 +6635,21 @@ "node": ">=14.17" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -3902,6 +6736,41 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/tools/package.json b/tools/package.json index 324bce041..23bafa58d 100644 --- a/tools/package.json +++ b/tools/package.json @@ -6,20 +6,32 @@ "license": "Apache-2.0", "scripts": { "merge": "ts-node merger/merge.ts", - "lint": "ts-node linter/lint.ts", + "lint:spec": "ts-node linter/lint.ts", + "lint": "eslint .", "test": "jest" }, "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", "@types/lodash": "^4.14.202", "@types/node": "^20.10.3", + "ajv": "^8.13.0", + "ajv-formats": "^3.0.1", "lodash": "^4.17.21", "ts-node": "^10.9.1", - "typescript": "^5.3.2", + "typescript": "^5.4.5", "yaml": "^2.3.4" }, "devDependencies": { + "@eslint/eslintrc": "^3.0.2", + "@eslint/js": "^9.1.1", "@types/jest": "^29.5.12", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint": "^8.57.0", + "eslint-config-standard-with-typescript": "^43.0.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.1.1", + "globals": "^15.0.0", "jest": "^29.7.0", "ts-jest": "^29.1.2" } diff --git a/tools/test/linter/InfoFile.test.ts b/tools/test/linter/InfoFile.test.ts new file mode 100644 index 000000000..a0a7652bd --- /dev/null +++ b/tools/test/linter/InfoFile.test.ts @@ -0,0 +1,12 @@ +import InfoFile from '../../linter/components/InfoFile' + +test('validate()', () => { + const validator = new InfoFile('./test/linter/fixtures/_info.yaml') + expect(validator.validate()).toEqual([ + { + file: 'fixtures/_info.yaml', + location: '$schema', + message: 'JSON Schema is required but not found in this file.' + } + ]) +}) diff --git a/tools/test/linter/NamespaceFile.test.ts b/tools/test/linter/NamespaceFile.test.ts index 88e47f6a5..3303bbc87 100644 --- a/tools/test/linter/NamespaceFile.test.ts +++ b/tools/test/linter/NamespaceFile.test.ts @@ -1,99 +1,101 @@ -import {mocked_namespace_file, namespace_file} from "./factories/namespace_file"; - +import { mocked_namespace_file, namespace_file } from './factories/namespace_file' test('constructor()', () => { - const file = namespace_file('empty.yaml'); - expect(file.file).toBe('namespaces/empty.yaml'); - expect(file.namespace).toBe('empty'); - expect(file.operation_groups()).toEqual([]); -}); + const file = namespace_file('empty.yaml') + expect(file.file).toBe('namespaces/empty.yaml') + expect(file.namespace).toBe('empty') + expect(file.operation_groups()).toEqual([]) +}) test('validate_name()', () => { - const ns_file = mocked_namespace_file({}); + const ns_file = mocked_namespace_file({}) - expect(ns_file.validate_name('_core')).toBeUndefined(); - expect(ns_file.validate_name('indices')).toBeUndefined(); - expect(ns_file.validate_name('_cat')).toEqual({ - file: 'namespaces/indices.yaml', - location: 'File Name', - message: `Invalid namespace name '_cat'. Must match regex: /^[a-z]+[a-z_]*[a-z]+$/.` - }); -}); + expect(ns_file.validate_name('_core')).toBeUndefined() + expect(ns_file.validate_name('indices')).toBeUndefined() + expect(ns_file.validate_name('_cat')).toEqual({ + file: 'namespaces/indices.yaml', + location: 'File Name', + message: 'Invalid namespace name \'_cat\'. Must match regex: /^[a-z]+[a-z_]*[a-z]+$/.' + }) +}) test('validate_schemas()', () => { - const with_schemas = mocked_namespace_file({spec: {components: {schemas: {}}}}); - expect(with_schemas.validate_schemas()).toEqual({ - file: `namespaces/indices.yaml`, - location: `#/components/schemas`, - message: `components/schemas is not allowed in namespace files` - }); + const with_schemas = mocked_namespace_file({ spec: { components: { schemas: {} } } }) + expect(with_schemas.validate_schemas()).toEqual({ + file: 'namespaces/indices.yaml', + location: '#/components/schemas', + message: 'components/schemas is not allowed in namespace files' + }) - const no_schemas = mocked_namespace_file({spec: {components: {}}}); - expect(no_schemas.validate_schemas()).toBeUndefined(); -}); + const no_schemas = mocked_namespace_file({ spec: { components: {} } }) + expect(no_schemas.validate_schemas()).toBeUndefined() +}) test('validate_unresolved_refs()', () => { - const validator = namespace_file('invalid_components.yaml'); - expect(validator.validate_unresolved_refs()).toEqual([ - { - file: `namespaces/invalid_components.yaml`, - location: `#/components/responses/indices.create@200`, - message: `Unresolved reference: #/components/responses/indices.create@200` - }, - { - file: `namespaces/invalid_components.yaml`, - location: `#/components/parameters/indices.create::query.pretty`, - message: `Unresolved reference: #/components/parameters/indices.create::query.pretty` - } - ]); -}); + const validator = namespace_file('invalid_components.yaml') + expect(validator.validate_unresolved_refs()).toEqual([ + { + file: 'namespaces/invalid_components.yaml', + location: '#/components/responses/indices.create@200', + message: 'Unresolved reference: #/components/responses/indices.create@200' + }, + { + file: 'namespaces/invalid_components.yaml', + location: '#/components/parameters/indices.create::query.pretty', + message: 'Unresolved reference: #/components/parameters/indices.create::query.pretty' + } + ]) +}) test('validate_unused_refs()', () => { - const validator = namespace_file('invalid_components.yaml'); - expect(validator.validate_unused_refs()).toEqual([ - { - file: `namespaces/invalid_components.yaml`, - location: `#/components/requestBodies/indices.create`, - message: `Unused requestBodies component: indices.create` - }, - { - file: `namespaces/invalid_components.yaml`, - location: `#/components/parameters/indices.create::query.h`, - message: `Unused parameters component: indices.create::query.h` - } - ]); -}); + const validator = namespace_file('invalid_components.yaml') + expect(validator.validate_unused_refs()).toEqual([ + { + file: 'namespaces/invalid_components.yaml', + location: '#/components/requestBodies/indices.create', + message: 'Unused requestBodies component: indices.create' + }, + { + file: 'namespaces/invalid_components.yaml', + location: '#/components/parameters/indices.create::query.h', + message: 'Unused parameters component: indices.create::query.h' + } + ]) +}) test('validate_parameter_refs()', () => { - const validator = namespace_file('invalid_components.yaml'); - expect(validator.validate_parameter_refs()).toEqual([ - { - file: "namespaces/invalid_components.yaml", - location: "#/components/parameters/#indices.create::query.ExpandWildcards", - message: "Invalid parameter name 'ExpandWildcards'. A parameter's name can only contain lower-cased alphanumerics, underscores, and periods." - }, - { - file: "namespaces/invalid_components.yaml", - location: "#/components/parameters/#indices.create::query.h", - message: "Parameter component 'indices.create::query.h' must be named 'indices.create::query.v' since it is a query parameter named 'v'." - } - ]); -}); + const validator = namespace_file('invalid_components.yaml') + expect(validator.validate_parameter_refs()).toEqual([ + { + file: 'namespaces/invalid_components.yaml', + location: '#/components/parameters/#indices.create::query.ExpandWildcards', + message: "Invalid parameter name 'ExpandWildcards'. A parameter's name can only contain lower-cased alphanumerics, underscores, and periods." + }, + { + file: 'namespaces/invalid_components.yaml', + location: '#/components/parameters/#indices.create::query.h', + message: "Parameter component 'indices.create::query.h' must be named 'indices.create::query.v' since it is a query parameter named 'v'." + } + ]) +}) test('validate()', () => { - const invalid_name = mocked_namespace_file({returned_values: {validate_name: 'Invalid Name'}, groups_errors: [['group error']]}); - expect(invalid_name.validate()).toEqual(['Invalid Name']); + const invalid_name = mocked_namespace_file({ returned_values: { validate_name: 'Invalid Name' }, groups_errors: [['group error']] }) + expect(invalid_name.validate()).toEqual(['Invalid Name']) - const invalid_groups = mocked_namespace_file({returned_values: { validate_schemas: 'Invalid schemas' }, groups_errors: [['error']]}); - expect(invalid_groups.validate()).toEqual(['error']); + const invalid_groups = mocked_namespace_file({ returned_values: { validate_schemas: 'Invalid schemas' }, groups_errors: [['error']] }) + expect(invalid_groups.validate()).toEqual(['error']) - const typical = mocked_namespace_file({returned_values: { - validate_schemas: 'schemas error', - validate_unresolved_refs: ['unresolved'], - validate_unused_refs: ['unused'], - validate_parameter_refs: ['parameter']}}); - expect(typical.validate()).toEqual(['schemas error', 'unresolved', 'unused', 'parameter']); + const typical = mocked_namespace_file({ + returned_values: { + validate_schemas: 'schemas error', + validate_unresolved_refs: ['unresolved'], + validate_unused_refs: ['unused'], + validate_parameter_refs: ['parameter'] + } + }) + expect(typical.validate()).toEqual(['schemas error', 'unresolved', 'unused', 'parameter']) - const valid = mocked_namespace_file({groups_errors: [[], []]}); - expect(valid.validate()).toEqual([]); -}); \ No newline at end of file + const valid = mocked_namespace_file({ groups_errors: [[], []] }) + expect(valid.validate()).toEqual([]) +}) diff --git a/tools/test/linter/NamespacesFolder.test.ts b/tools/test/linter/NamespacesFolder.test.ts index 9563988aa..9f2f6b839 100644 --- a/tools/test/linter/NamespacesFolder.test.ts +++ b/tools/test/linter/NamespacesFolder.test.ts @@ -1,47 +1,53 @@ -import NamespacesFolder from "../../linter/components/NamespacesFolder"; +import NamespacesFolder from '../../linter/components/NamespacesFolder' -test('validate()', () => { - const validator = new NamespacesFolder('./test/linter/fixtures/folder_validators/namespaces'); - expect(validator.validate()).toEqual([ - { - file: "namespaces/indices.txt", - location: "File Extension", - message: "Invalid file extension. Only '.yaml' files are allowed." - }, - { - file: "namespaces/invalid_spec.yaml", - location: "Operation: GET /{index}/_doc/{id}", - message: "Missing description property." - }, - { - file: "namespaces/invalid_spec.yaml", - location: "Operation: GET /{index}/_doc/{id}", - message: "Every parameter must be a reference object to '#/components/parameters/{x-operation-group}::{path|query}.{parameter_name}'." - }, - { - file: "namespaces/invalid_spec.yaml", - location: "Operation: GET /{index}/_doc/{id}", - message: "Path parameters must match the parameters in the path: {id}, {index}." - }, - { - file: "namespaces/invalid_spec.yaml", - location: "Operation: GET /{index}/_doc/{id}", - message: "The 200 response must be a reference object to '#/components/responses/invalid_spec.fetch@200'." - }, - { - file: "namespaces/invalid_yaml.yaml", - location: "File Content", - message: "Unable to read or parse YAML." - }, - { - file: "namespaces/", - location: "Folder", - message: "Duplicate path '/{index}' found in namespaces: dup_path_a, dup_path_c." - }, - { - file: "namespaces/", - location: "Folder", - message: "Duplicate path '/{index}/_rollover' found in namespaces: dup_path_a, dup_path_b, dup_path_c." - } - ]); -}); +test('validate() - When there invalid files', () => { + const validator = new NamespacesFolder('./test/linter/fixtures/folder_validators/namespaces/invalid_files') + expect(validator.validate()).toEqual([ + { + file: 'invalid_files/indices.txt', + location: 'File Extension', + message: "Invalid file extension. Only '.yaml' files are allowed." + }, + { + file: 'invalid_files/invalid_spec.yaml', + location: 'Operation: GET /{index}/_doc/{id}', + message: 'Missing description property.' + }, + { + file: 'invalid_files/invalid_spec.yaml', + location: 'Operation: GET /{index}/_doc/{id}', + message: "Every parameter must be a reference object to '#/components/parameters/{x-operation-group}::{path|query}.{parameter_name}'." + }, + { + file: 'invalid_files/invalid_spec.yaml', + location: 'Operation: GET /{index}/_doc/{id}', + message: 'Path parameters must match the parameters in the path: {id}, {index}.' + }, + { + file: 'invalid_files/invalid_spec.yaml', + location: 'Operation: GET /{index}/_doc/{id}', + message: "The 200 response must be a reference object to '#/components/responses/invalid_spec.fetch@200'." + }, + { + file: 'invalid_files/invalid_yaml.yaml', + location: 'File Content', + message: 'Unable to read or parse YAML.' + } + ]) +}) + +test('validate() - When the files are valid but the folder is not', () => { + const validator = new NamespacesFolder('./test/linter/fixtures/folder_validators/namespaces/invalid_folder') + expect(validator.validate()).toEqual([ + { + file: 'invalid_folder/', + location: 'Folder', + message: "Duplicate path '/{index}' found in namespaces: dup_path_a, dup_path_c." + }, + { + file: 'invalid_folder/', + location: 'Folder', + message: "Duplicate path '/{index}/_rollover' found in namespaces: dup_path_a, dup_path_b, dup_path_c." + } + ]) +}) diff --git a/tools/test/linter/Operation.test.ts b/tools/test/linter/Operation.test.ts index 43fce1229..f7426a29d 100644 --- a/tools/test/linter/Operation.test.ts +++ b/tools/test/linter/Operation.test.ts @@ -1,153 +1,166 @@ -import {mocked_operation, operation} from "./factories/operation"; +import { mocked_operation, operation } from './factories/operation' test('validate_group()', () => { - const no_group = operation({'x-operation-group': ''}); - expect(no_group.validate_group()).toEqual({ - file: 'namespaces/indices.yaml', - location: `Operation: POST /{index}/something/{abc_xyz}`, - message: `Missing x-operation-group property`, - }); - - const invalid_group = operation({'x-operation-group': 'indices_'}); - expect(invalid_group.validate_group()) - .toEqual(invalid_group.error(`Invalid x-operation-group 'indices_'. Must match regex: /^([a-z]+[a-z_]*[a-z]+\\.)?([a-z]+[a-z_]*[a-z]+)$/.`)); - - const invalid_action = operation({'x-operation-group': 'indices.create.index'}); - expect(invalid_action.validate_group()) - .toEqual(invalid_action.error(`Invalid x-operation-group 'indices.create.index'. Must match regex: /^([a-z]+[a-z_]*[a-z]+\\.)?([a-z]+[a-z_]*[a-z]+)$/.`)); - - const valid_group = operation({'x-operation-group': 'indices.create'}); - expect(valid_group.validate_group()) - .toBeUndefined(); -}); + const no_group = operation({ 'x-operation-group': '' }) + expect(no_group.validate_group()).toEqual({ + file: 'namespaces/indices.yaml', + location: 'Operation: POST /{index}/something/{abc_xyz}', + message: 'Missing x-operation-group property' + }) + + const invalid_group = operation({ 'x-operation-group': 'indices_' }) + expect(invalid_group.validate_group()) + .toEqual(invalid_group.error('Invalid x-operation-group \'indices_\'. Must match regex: /^([a-z]+[a-z_]*[a-z]+\\.)?([a-z]+[a-z_]*[a-z]+)$/.')) + + const invalid_action = operation({ 'x-operation-group': 'indices.create.index' }) + expect(invalid_action.validate_group()) + .toEqual(invalid_action.error('Invalid x-operation-group \'indices.create.index\'. Must match regex: /^([a-z]+[a-z_]*[a-z]+\\.)?([a-z]+[a-z_]*[a-z]+)$/.')) + + const valid_group = operation({ 'x-operation-group': 'indices.create' }) + expect(valid_group.validate_group()) + .toBeUndefined() +}) test('validate_namespace()', () => { - const valid_core = operation({'x-operation-group': 'search'}, '_core.yaml'); - expect(valid_core.validate_namespace()) - .toBeUndefined(); + const valid_core = operation({ 'x-operation-group': 'search' }, '_core.yaml') + expect(valid_core.validate_namespace()) + .toBeUndefined() - const valid_non_core = operation({'x-operation-group': 'indices._create'}, 'indices.yaml'); - expect(valid_non_core.validate_namespace()) - .toBeUndefined(); + const valid_non_core = operation({ 'x-operation-group': 'indices._create' }, 'indices.yaml') + expect(valid_non_core.validate_namespace()) + .toBeUndefined() - const non_omitted_core = operation({'x-operation-group': '_core.search'}, '_core.yaml'); - expect(non_omitted_core.validate_namespace()) - .toEqual(non_omitted_core.error(`Invalid x-operation-group '_core.search'. '_core' namespace must be omitted in x-operation-group.`)); + const non_omitted_core = operation({ 'x-operation-group': '_core.search' }, '_core.yaml') + expect(non_omitted_core.validate_namespace()) + .toEqual(non_omitted_core.error('Invalid x-operation-group \'_core.search\'. \'_core\' namespace must be omitted in x-operation-group.')) - const unmatched_namespace = operation({'x-operation-group': 'indices.create'}, 'cat.yaml'); - expect(unmatched_namespace.validate_namespace()) - .toEqual(unmatched_namespace.error(`Invalid x-operation-group 'indices.create'. 'indices' namespace detected. Only 'cat' namespace is allowed in this file.`)); -}); + const unmatched_namespace = operation({ 'x-operation-group': 'indices.create' }, 'cat.yaml') + expect(unmatched_namespace.validate_namespace()) + .toEqual(unmatched_namespace.error('Invalid x-operation-group \'indices.create\'. \'indices\' namespace detected. Only \'cat\' namespace is allowed in this file.')) +}) test('validate_operationId()', () => { - const no_id = operation({'x-operation-group': 'indices.create'}); - expect(no_id.validate_operationId()) - .toEqual(no_id.error(`Missing operationId property.`)); + const no_id = operation({ 'x-operation-group': 'indices.create' }) + expect(no_id.validate_operation_id()) + .toEqual(no_id.error('Missing operationId property.')) - const invalid_id = operation({'x-operation-group': 'indices.create', operationId: 'create_index'}); - expect(invalid_id.validate_operationId()) - .toEqual(invalid_id.error(`Invalid operationId 'create_index'. Must be in {x-operation-group}.{number} format.`)); + const invalid_id = operation({ 'x-operation-group': 'indices.create', operationId: 'create_index' }) + expect(invalid_id.validate_operation_id()) + .toEqual(invalid_id.error('Invalid operationId \'create_index\'. Must be in {x-operation-group}.{number} format.')) - const valid_id = operation({'x-operation-group': 'indices.create', operationId: 'indices.create.1'}); - expect(valid_id.validate_operationId()) - .toBeUndefined(); -}); + const valid_id = operation({ 'x-operation-group': 'indices.create', operationId: 'indices.create.1' }) + expect(valid_id.validate_operation_id()) + .toBeUndefined() +}) test('validate_description()', () => { - const no_description = operation({'x-operation-group': 'indices.create'}); - expect(no_description.validate_description()) - .toEqual(no_description.error(`Missing description property.`)); + const no_description = operation({ 'x-operation-group': 'indices.create' }) + expect(no_description.validate_description()) + .toEqual(no_description.error('Missing description property.')) - const invalid_description = operation({'x-operation-group': 'indices.create', description: 'This is a description without a period'}); - expect(invalid_description.validate_description()) - .toEqual(invalid_description.error(`Description must end with a period.`)); + const invalid_description = operation({ 'x-operation-group': 'indices.create', description: 'This is a description without a period' }) + expect(invalid_description.validate_description()) + .toEqual(invalid_description.error('Description must end with a period.')) - const valid_description = operation({'x-operation-group': 'indices.create', description: 'This is a description with a period.'}); - expect(valid_description.validate_description()) - .toBeUndefined(); -}); + const valid_description = operation({ 'x-operation-group': 'indices.create', description: 'This is a description with a period.' }) + expect(valid_description.validate_description()) + .toBeUndefined() +}) test('validate_requestBody()', () => { - const no_body = operation({'x-operation-group': 'indices.create'}); - expect(no_body.validate_requestBody()) - .toBeUndefined(); + const no_body = operation({ 'x-operation-group': 'indices.create' }) + expect(no_body.validate_request_body()) + .toBeUndefined() - const valid_body = operation({'x-operation-group': 'indices.create', requestBody: {$ref: '#/components/requestBodies/indices.create'}}); - expect(valid_body.validate_requestBody()) - .toBeUndefined(); + const valid_body = operation({ 'x-operation-group': 'indices.create', requestBody: { $ref: '#/components/requestBodies/indices.create' } }) + expect(valid_body.validate_request_body()) + .toBeUndefined() - const invalid_body = operation({'x-operation-group': 'indices.create', requestBody: {$ref: '#/components/requestBodies/indices.create.1'}}); - expect(invalid_body.validate_requestBody()) - .toEqual(invalid_body.error(`The requestBody must be a reference object to '#/components/requestBodies/indices.create'.`)); -}); + const invalid_body = operation({ 'x-operation-group': 'indices.create', requestBody: { $ref: '#/components/requestBodies/indices.create.1' } }) + expect(invalid_body.validate_request_body()) + .toEqual(invalid_body.error('The requestBody must be a reference object to \'#/components/requestBodies/indices.create\'.')) +}) test('validate_response()', () => { - const no_responses = operation({responses: {}}); - expect(no_responses.validate_responses()).toEqual([no_responses.error(`Missing responses property.`)]); - - const invalid_responses = operation({'x-operation-group': 'cat.info', responses: { - '200': {$ref: '#/components/responses/cat.info'}, - '500': {$ref: '#/components/responses/cat.info@500'}, - '400': {$ref: '#/components/responses/cat.info:bad_request'}, - }}); - expect(invalid_responses.validate_responses()).toEqual([ - invalid_responses.error(`The 200 response must be a reference object to '#/components/responses/cat.info@200'.`), - invalid_responses.error(`The 400 response must be a reference object to '#/components/responses/cat.info@400'.`),]); - - const valid_responses = operation({'x-operation-group': 'cat.info', responses: {'200': {$ref: '#/components/responses/cat.info@200'}}}); - expect(valid_responses.validate_responses()) - .toEqual([]); -}); + const no_responses = operation({ responses: {} }) + expect(no_responses.validate_responses()).toEqual([no_responses.error('Missing responses property.')]) + + const invalid_responses = operation({ + 'x-operation-group': 'cat.info', + responses: { + 200: { $ref: '#/components/responses/cat.info' }, + 500: { $ref: '#/components/responses/cat.info@500' }, + 400: { $ref: '#/components/responses/cat.info:bad_request' } + } + }) + expect(invalid_responses.validate_responses()).toEqual([ + invalid_responses.error('The 200 response must be a reference object to \'#/components/responses/cat.info@200\'.'), + invalid_responses.error('The 400 response must be a reference object to \'#/components/responses/cat.info@400\'.')]) + + const valid_responses = operation({ 'x-operation-group': 'cat.info', responses: { 200: { $ref: '#/components/responses/cat.info@200' } } }) + expect(valid_responses.validate_responses()) + .toEqual([]) +}) test('validate_parameters()', () => { - const no_parameters = operation({'x-operation-group': 'indices.create'}); - expect(no_parameters.validate_parameters()) - .toBeUndefined(); - - const valid_parameters = operation({'x-operation-group': 'indices.create', parameters: [{$ref: '#/components/parameters/indices.create::path.request_timeout'}]}); - expect(valid_parameters.validate_parameters()) - .toBeUndefined(); - - const invalid_parameters = operation({'x-operation-group': 'indices.create', parameters: [ - {$ref: '#/components/parameters/indices.create::path.index'}, - {$ref: '#/components/parameters/indices.create::timeout'}, - {$ref: '#/components/parameters/indices.create::query:pretty'}, - ]}); - expect(invalid_parameters.validate_parameters()) - .toEqual(invalid_parameters.error(`Every parameter must be a reference object to '#/components/parameters/{x-operation-group}::{path|query}.{parameter_name}'.`)); -}); + const no_parameters = operation({ 'x-operation-group': 'indices.create' }) + expect(no_parameters.validate_parameters()) + .toBeUndefined() + + const valid_parameters = operation({ 'x-operation-group': 'indices.create', parameters: [{ $ref: '#/components/parameters/indices.create::path.request_timeout' }] }) + expect(valid_parameters.validate_parameters()) + .toBeUndefined() + + const invalid_parameters = operation({ + 'x-operation-group': 'indices.create', + parameters: [ + { $ref: '#/components/parameters/indices.create::path.index' }, + { $ref: '#/components/parameters/indices.create::timeout' }, + { $ref: '#/components/parameters/indices.create::query:pretty' } + ] + }) + expect(invalid_parameters.validate_parameters()) + .toEqual(invalid_parameters.error('Every parameter must be a reference object to \'#/components/parameters/{x-operation-group}::{path|query}.{parameter_name}\'.')) +}) test('validate_path_parameters()', () => { - const invalid_path_params = operation({parameters: [{$ref: '#/components/parameters/indices.create::path.index'}]}); - expect(invalid_path_params.validate_path_parameters()) - .toEqual(invalid_path_params.error(`Path parameters must match the parameters in the path: {abc_xyz}, {index}.`)); - - const valid_path_params = operation({parameters: [ - {$ref: '#/components/parameters/indices.create::path.index'}, - {$ref: '#/components/parameters/indices.create::path.abc_xyz'} ]}); - expect(valid_path_params.validate_path_parameters()) - .toBeUndefined(); -}); + const invalid_path_params = operation({ parameters: [{ $ref: '#/components/parameters/indices.create::path.index' }] }) + expect(invalid_path_params.validate_path_parameters()) + .toEqual(invalid_path_params.error('Path parameters must match the parameters in the path: {abc_xyz}, {index}.')) + + const valid_path_params = operation({ + parameters: [ + { $ref: '#/components/parameters/indices.create::path.index' }, + { $ref: '#/components/parameters/indices.create::path.abc_xyz' }] + }) + expect(valid_path_params.validate_path_parameters()) + .toBeUndefined() +}) test('validate()', () => { - const invalid_group = mocked_operation({validate_group: 'Invalid group', validate_namespace: 'Invalid namespace'}); - expect(invalid_group.validate()) - .toEqual(['Invalid group']); - - const invalid_namespace = mocked_operation({validate_group: undefined, validate_namespace: 'Invalid namespace', validate_operationId: 'Invalid operationId'}); - expect(invalid_namespace.validate()) - .toEqual(['Invalid namespace']); - - const typical_invalid = mocked_operation({ - validate_group: undefined, validate_namespace: undefined, - validate_operationId: 'Invalid operationId', validate_description: 'Invalid description', validate_requestBody: 'Invalid requestBody', - validate_responses: ['Invalid response 1', 'Invalid response 2'], - validate_parameters: 'Invalid parameters', validate_path_parameters: 'Invalid path parameters' }); - expect(typical_invalid.validate()) - .toEqual(['Invalid operationId', 'Invalid description', 'Invalid requestBody', - 'Invalid parameters', 'Invalid path parameters', 'Invalid response 1', 'Invalid response 2']); - - const valid = mocked_operation({ validate_responses: [] }); - expect(valid.validate()) - .toEqual([]); -}); \ No newline at end of file + const invalid_group = mocked_operation({ validate_group: 'Invalid group', validate_namespace: 'Invalid namespace' }) + expect(invalid_group.validate()) + .toEqual(['Invalid group']) + + const invalid_namespace = mocked_operation({ validate_group: undefined, validate_namespace: 'Invalid namespace', validate_operationId: 'Invalid operationId' }) + expect(invalid_namespace.validate()) + .toEqual(['Invalid namespace']) + + const typical_invalid = mocked_operation({ + validate_group: undefined, + validate_namespace: undefined, + validate_operationId: 'Invalid operationId', + validate_description: 'Invalid description', + validate_requestBody: 'Invalid requestBody', + validate_responses: ['Invalid response 1', 'Invalid response 2'], + validate_parameters: 'Invalid parameters', + validate_path_parameters: 'Invalid path parameters' + }) + expect(typical_invalid.validate()) + .toEqual(['Invalid operationId', 'Invalid description', 'Invalid requestBody', + 'Invalid parameters', 'Invalid path parameters', 'Invalid response 1', 'Invalid response 2']) + + const valid = mocked_operation({ validate_responses: [] }) + expect(valid.validate()) + .toEqual([]) +}) diff --git a/tools/test/linter/OperationGroup.test.ts b/tools/test/linter/OperationGroup.test.ts index b57cb80dd..564cab7b5 100644 --- a/tools/test/linter/OperationGroup.test.ts +++ b/tools/test/linter/OperationGroup.test.ts @@ -1,91 +1,94 @@ -import {mocked_operation_group, operation_group} from "./factories/operation_group"; +import { mocked_operation_group, operation_group } from './factories/operation_group' test('validate_description()', () => { - const invalid_descriptions = operation_group([ - {description: 'This is a description'}, - {description: 'This is a different description'}]); - expect(invalid_descriptions.validate_description()).toEqual({ - file: `namespaces/indices.yaml`, - location: `Operation Group: indices.create`, - message: `2 'indices.create' operations must have identical description property.` - }); + const invalid_descriptions = operation_group([ + { description: 'This is a description' }, + { description: 'This is a different description' }]) + expect(invalid_descriptions.validate_description()).toEqual({ + file: 'namespaces/indices.yaml', + location: 'Operation Group: indices.create', + message: '2 \'indices.create\' operations must have identical description property.' + }) - - const valid_descriptions = operation_group([ - {description: 'This is a description'}, - {description: 'This is a description'}]); - expect(valid_descriptions.validate_description()) - .toBeUndefined(); -}); + const valid_descriptions = operation_group([ + { description: 'This is a description' }, + { description: 'This is a description' }]) + expect(valid_descriptions.validate_description()) + .toBeUndefined() +}) test('validate_externalDocs()', () => { - const valid_externalDocs = operation_group([ - {externalDocs: {url: 'https://example.com'}}, - {externalDocs: {url: 'https://example.com'}}]); - expect(valid_externalDocs.validate_externalDocs()) - .toBeUndefined(); + const valid_external_docs = operation_group([ + { externalDocs: { url: 'https://example.com' } }, + { externalDocs: { url: 'https://example.com' } }]) + expect(valid_external_docs.validate_external_docs()) + .toBeUndefined() - const invalid_externalDocs = operation_group([ - {externalDocs: {url: 'https://example.com'}}, - {externalDocs: {url: 'https://example.com'}}, - {}]); - expect(invalid_externalDocs.validate_externalDocs()) - .toEqual(invalid_externalDocs.error(`3 'indices.create' operations must have identical externalDocs property.`)); -}); + const invalid_external_docs = operation_group([ + { externalDocs: { url: 'https://example.com' } }, + { externalDocs: { url: 'https://example.com' } }, + {}]) + expect(invalid_external_docs.validate_external_docs()) + .toEqual(invalid_external_docs.error('3 \'indices.create\' operations must have identical externalDocs property.')) +}) test('validate_requestBody()', () => { - const valid_requestBodies = operation_group([ - {requestBody: {$ref: '#/components/requestBodies/indices.create'}}, - {requestBody: {$ref: '#/components/requestBodies/indices.create'}}]); - expect(valid_requestBodies.validate_requestBody()) - .toBeUndefined(); + const valid_request_bodies = operation_group([ + { requestBody: { $ref: '#/components/requestBodies/indices.create' } }, + { requestBody: { $ref: '#/components/requestBodies/indices.create' } }]) + expect(valid_request_bodies.validate_request_body()) + .toBeUndefined() - const invalid_requestBodies = operation_group([ - {requestBody: {$ref: '#/components/requestBodies/indices.create'}}, - {}]); - expect(invalid_requestBodies.validate_requestBody()) - .toEqual(invalid_requestBodies.error(`2 'indices.create' operations must have identical requestBody property.`)); -}); + const invalid_request_bodies = operation_group([ + { requestBody: { $ref: '#/components/requestBodies/indices.create' } }, + {}]) + expect(invalid_request_bodies.validate_request_body()) + .toEqual(invalid_request_bodies.error('2 \'indices.create\' operations must have identical requestBody property.')) +}) test('validate_responses()', () => { - const valid_responses = operation_group([ - {responses: {'200': {$ref: '#/components/responses/indices.create@200'}, '400': {$ref: '#/components/responses/indices.create@400'}}}, - {responses: {'400': {$ref: '#/components/responses/indices.create@400'}, '200': {$ref: '#/components/responses/indices.create@200'}}}]); - expect(valid_responses.validate_responses()) - .toBeUndefined(); + const valid_responses = operation_group([ + { responses: { 200: { $ref: '#/components/responses/indices.create@200' }, 400: { $ref: '#/components/responses/indices.create@400' } } }, + { responses: { 400: { $ref: '#/components/responses/indices.create@400' }, 200: { $ref: '#/components/responses/indices.create@200' } } }]) + expect(valid_responses.validate_responses()) + .toBeUndefined() - const invalid_responses = operation_group([ - {responses: {'200': {$ref: '#/components/responses/indices.create@200'}}}, - {responses: {'200': {$ref: '#/components/responses/indices.create@200'}, - '400': {$ref: '#/components/responses/indices.create@400'}}}]); - expect(invalid_responses.validate_responses()) - .toEqual(invalid_responses.error(`2 'indices.create' operations must have an identical set of responses.`)); -}); + const invalid_responses = operation_group([ + { responses: { 200: { $ref: '#/components/responses/indices.create@200' } } }, + { + responses: { + 200: { $ref: '#/components/responses/indices.create@200' }, + 400: { $ref: '#/components/responses/indices.create@400' } + } + }]) + expect(invalid_responses.validate_responses()) + .toEqual(invalid_responses.error('2 \'indices.create\' operations must have an identical set of responses.')) +}) test('validate_query_parameters()', () => { - const valid_query_parameters = operation_group([ - {parameters: [{$ref: '#/components/parameters/indices.create::query.param1'}, {$ref: '#/components/parameters/indices.create::path.param2'}]}, - {parameters: [{$ref: '#/components/parameters/indices.create::query.param1'}]}]); - expect(valid_query_parameters.validate_query_parameters()) - .toBeUndefined(); + const valid_query_parameters = operation_group([ + { parameters: [{ $ref: '#/components/parameters/indices.create::query.param1' }, { $ref: '#/components/parameters/indices.create::path.param2' }] }, + { parameters: [{ $ref: '#/components/parameters/indices.create::query.param1' }] }]) + expect(valid_query_parameters.validate_query_parameters()) + .toBeUndefined() - const invalid_query_parameters = operation_group([ - {parameters: [{$ref: '#/components/parameters/indices.create::query.param1'}]}, - {parameters: [{$ref: '#/components/parameters/indices.create::query.param1'}, {$ref: '#/components/parameters/indices.create::query.param2'}]}]); - expect(invalid_query_parameters.validate_query_parameters()) - .toEqual(invalid_query_parameters.error(`2 'indices.create' operations must have an identical set of query parameters.`)); -}); + const invalid_query_parameters = operation_group([ + { parameters: [{ $ref: '#/components/parameters/indices.create::query.param1' }] }, + { parameters: [{ $ref: '#/components/parameters/indices.create::query.param1' }, { $ref: '#/components/parameters/indices.create::query.param2' }] }]) + expect(invalid_query_parameters.validate_query_parameters()) + .toEqual(invalid_query_parameters.error('2 \'indices.create\' operations must have an identical set of query parameters.')) +}) test('validate()', () => { - const ops_errors = mocked_operation_group({validate_description: 'Invalid description'}, [['Invalid operation 1A'], ['Invalid operation 2A', 'Invalid operation 2B']]); - expect(ops_errors.validate()) - .toEqual(['Invalid operation 1A', 'Invalid operation 2A', 'Invalid operation 2B']); + const ops_errors = mocked_operation_group({ validate_description: 'Invalid description' }, [['Invalid operation 1A'], ['Invalid operation 2A', 'Invalid operation 2B']]) + expect(ops_errors.validate()) + .toEqual(['Invalid operation 1A', 'Invalid operation 2A', 'Invalid operation 2B']) - const other_errors = mocked_operation_group({validate_description: 'Invalid description', validate_externalDocs: 'Invalid externalDocs', validate_requestBody: 'Invalid requestBody', validate_responses: 'Invalid responses', validate_query_parameters: 'Invalid query parameters'}); - expect(other_errors.validate()) - .toEqual(['Invalid description', 'Invalid externalDocs', 'Invalid requestBody', 'Invalid responses', 'Invalid query parameters']); + const other_errors = mocked_operation_group({ validate_description: 'Invalid description', validate_externalDocs: 'Invalid externalDocs', validate_requestBody: 'Invalid requestBody', validate_responses: 'Invalid responses', validate_query_parameters: 'Invalid query parameters' }) + expect(other_errors.validate()) + .toEqual(['Invalid description', 'Invalid externalDocs', 'Invalid requestBody', 'Invalid responses', 'Invalid query parameters']) - const no_errors = mocked_operation_group({}); - expect(no_errors.validate()) - .toEqual([]); -}); \ No newline at end of file + const no_errors = mocked_operation_group({}) + expect(no_errors.validate()) + .toEqual([]) +}) diff --git a/tools/test/linter/PathRefsValidator.test.ts b/tools/test/linter/PathRefsValidator.test.ts deleted file mode 100644 index 527782e3e..000000000 --- a/tools/test/linter/PathRefsValidator.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import PathRefsValidator from "../../linter/PathRefsValidator"; -import RootFile from "../../linter/components/RootFile"; -import NamespacesFolder from "../../linter/components/NamespacesFolder"; - -test('validate()', () => { - const root_folder = './test/linter/fixtures/path_refs_validator'; - const root_file = new RootFile(`${root_folder}/opensearch-openapi.yaml`); - const namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`); - const validator = new PathRefsValidator(root_file, namespaces_folder); - expect(validator.validate()).toEqual([ - { - file: "opensearch-openapi.yaml", - location: "Path: /{index}", - message: "Unresolved path reference: Path /{index} does not exist in namespace file namespaces/indices.yaml." - }, - { - file: "opensearch-openapi.yaml", - location: "Paths: /_cluster/health , /_cluster/{id}", - message: "Unresolved path reference: Namespace file namespaces/cluster.yaml does not exist." - }, - { - file: "namespaces/indices.yaml", - location: "Path: /{index}/_aliases", - message: "Unreferenced path: Path /{index}/_aliases is not referenced in the root file." - }, - { - file: "namespaces/missing.yaml", - message: "Unreferenced paths: No paths are referenced in the root file." - } - ]); -}); \ No newline at end of file diff --git a/tools/test/linter/RootFile.test.ts b/tools/test/linter/RootFile.test.ts deleted file mode 100644 index e27923f69..000000000 --- a/tools/test/linter/RootFile.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import RootFile from "../../linter/components/RootFile"; - -test('validate()', () => { - const validator = new RootFile('./test/linter/fixtures/root.yaml'); - expect(validator.validate()).toEqual([ - { - file: "root.yaml", - location: "Path: /", - message: "Every path must be a reference object to a path in a namespace file." - }, - { - file: "root.yaml", - location: "Path: /{index}", - message: "Every path must be a reference object to a path in a namespace file." - }, - { - file: "root.yaml", - location: "#/components/parameters/_global::query.pretty", - message: "Parameters in root file must be in the format '_global::{in}.{name}'. Expected '_global::query.beautify'." - }, - { - file: "root.yaml", - location: "#/components/parameters/_global::query.human", - message: "Parameters in root file must have 'x-global' extension set to true." - } - ]); -}); \ No newline at end of file diff --git a/tools/test/linter/Schema.test.ts b/tools/test/linter/Schema.test.ts index 3eec71a81..60319b128 100644 --- a/tools/test/linter/Schema.test.ts +++ b/tools/test/linter/Schema.test.ts @@ -1,15 +1,14 @@ -import {schema} from "./factories/schema"; - +import { schema } from './factories/schema' test('validate_name()', () => { - expect(schema('invalid_name').validate_name()).toEqual({ - file: "_common.yaml", - location: "#/components/schemas/invalid_name", - message: "Invalid schema name 'invalid_name'. Only alphanumeric characters are allowed." - }); - expect(schema('Invalid.Name').validate_name()).toBeDefined(); + expect(schema('invalid_name').validate_name()).toEqual({ + file: '_common.yaml', + location: '#/components/schemas/invalid_name', + message: "Invalid schema name 'invalid_name'. Only alphanumeric characters are allowed." + }) + expect(schema('Invalid.Name').validate_name()).toBeDefined() - expect(schema('ValidName1').validate_name()).toBeUndefined(); - expect(schema('Valid1Name').validate_name()).toBeUndefined(); - expect(schema('uint').validate_name()).toBeUndefined(); -}); \ No newline at end of file + expect(schema('ValidName1').validate_name()).toBeUndefined() + expect(schema('Valid1Name').validate_name()).toBeUndefined() + expect(schema('uint').validate_name()).toBeUndefined() +}) diff --git a/tools/test/linter/SchemaFile.test.ts b/tools/test/linter/SchemaFile.test.ts index 06eb0ffbe..ad6b9d897 100644 --- a/tools/test/linter/SchemaFile.test.ts +++ b/tools/test/linter/SchemaFile.test.ts @@ -1,31 +1,31 @@ -import {mocked_schema_file, schema_file} from "./factories/schema_file"; +import { mocked_schema_file, schema_file } from './factories/schema_file' test('validate_category()', () => { - const validator = schema_file('_common.empty.yaml'); + const validator = schema_file('_common.empty.yaml') - expect(validator.validate_category()).toBeUndefined(); - expect(validator.validate_category('_common')).toBeUndefined(); - expect(validator.validate_category('cat._common')).toBeUndefined(); - expect(validator.validate_category('cat.valid_name')).toBeUndefined(); - expect(validator.validate_category('cat._invalid_name')).toEqual({ - file: "schemas/_common.empty.yaml", - location: "File Name", - message: "Invalid category name 'cat._invalid_name'. '_invalid_name' does not match regex: /^[a-z]+[a-z_]*[a-z]+$/." - }); - expect(validator.validate_category('invalid_regex')).toEqual({ - file: "schemas/_common.empty.yaml", - location: "File Name", - message: "Invalid category name 'invalid_regex'. Must match regex: /^[a-z_]+\\.[a-z_]+$/." - }); -}); + expect(validator.validate_category()).toBeUndefined() + expect(validator.validate_category('_common')).toBeUndefined() + expect(validator.validate_category('cat._common')).toBeUndefined() + expect(validator.validate_category('cat.valid_name')).toBeUndefined() + expect(validator.validate_category('cat._invalid_name')).toEqual({ + file: 'schemas/_common.empty.yaml', + location: 'File Name', + message: "Invalid category name 'cat._invalid_name'. '_invalid_name' does not match regex: /^[a-z]+[a-z_]*[a-z]+$/." + }) + expect(validator.validate_category('invalid_regex')).toEqual({ + file: 'schemas/_common.empty.yaml', + location: 'File Name', + message: "Invalid category name 'invalid_regex'. Must match regex: /^[a-z_]+\\.[a-z_]+$/." + }) +}) test('validate()', () => { - const invalid_category = mocked_schema_file({returned_values: { validate_category: 'Invalid Category'}, schema_errors: [['Invalid Schema']]}); - expect(invalid_category.validate()).toEqual(['Invalid Category']); + const invalid_category = mocked_schema_file({ returned_values: { validate_category: 'Invalid Category' }, schema_errors: [['Invalid Schema']] }) + expect(invalid_category.validate()).toEqual(['Invalid Category']) - const invalid_schema = mocked_schema_file({schema_errors: [['Error 1', 'Error 2']]}); - expect(invalid_schema.validate()).toEqual(['Error 1', 'Error 2']); + const invalid_schema = mocked_schema_file({ schema_errors: [['Error 1', 'Error 2']] }) + expect(invalid_schema.validate()).toEqual(['Error 1', 'Error 2']) - const valid = mocked_schema_file({schema_errors: [[]]}); - expect(valid.validate()).toEqual([]); -}); \ No newline at end of file + const valid = mocked_schema_file({ schema_errors: [[]] }) + expect(valid.validate()).toEqual([]) +}) diff --git a/tools/test/linter/SchemaRefsValidator.test.ts b/tools/test/linter/SchemaRefsValidator.test.ts index 048a28fab..e122d2536 100644 --- a/tools/test/linter/SchemaRefsValidator.test.ts +++ b/tools/test/linter/SchemaRefsValidator.test.ts @@ -1,30 +1,30 @@ -import SchemasFolder from "../../linter/components/SchemasFolder"; -import NamespacesFolder from "../../linter/components/NamespacesFolder"; -import SchemaRefsValidator from "../../linter/SchemaRefsValidator"; +import SchemasFolder from '../../linter/components/SchemasFolder' +import NamespacesFolder from '../../linter/components/NamespacesFolder' +import SchemaRefsValidator from '../../linter/SchemaRefsValidator' test('validate()', () => { - const root_folder = './test/linter/fixtures/schema_refs_validator'; - const namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`); - const schemas_folder = new SchemasFolder(`${root_folder}/schemas`); - const validator = new SchemaRefsValidator(namespaces_folder, schemas_folder); - expect(validator.validate()).toEqual([ - { - file: "schemas/animals.yaml", - location: "#/components/schemas/Crab", - message: "Unresolved schema reference: Schema Crab is referenced but does not exist." - }, - { - file: "namespaces/", - message: "Unresolved schema reference: Schema file schemas/vehicles.yaml is referenced but does not exist." - }, - { - file: "schemas/animals.yaml", - location: "#/components/schemas/Goat", - message: "Unreferenced schema: Schema Goat is not referenced anywhere." - }, - { - file: "schemas/others.yaml", - message: "Unreferenced schema: Schema file schemas/others.yaml is not referenced anywhere." - } - ]); -}); \ No newline at end of file + const root_folder = './test/linter/fixtures/schema_refs_validator' + const namespaces_folder = new NamespacesFolder(`${root_folder}/namespaces`) + const schemas_folder = new SchemasFolder(`${root_folder}/schemas`) + const validator = new SchemaRefsValidator(namespaces_folder, schemas_folder) + expect(validator.validate()).toEqual([ + { + file: 'schemas/animals.yaml', + location: '#/components/schemas/Crab', + message: 'Unresolved schema reference: Schema Crab is referenced but does not exist.' + }, + { + file: 'namespaces/', + message: 'Unresolved schema reference: Schema file schemas/vehicles.yaml is referenced but does not exist.' + }, + { + file: 'schemas/animals.yaml', + location: '#/components/schemas/Goat', + message: 'Unreferenced schema: Schema Goat is not referenced anywhere.' + }, + { + file: 'schemas/others.yaml', + message: 'Unreferenced schema: Schema file schemas/others.yaml is not referenced anywhere.' + } + ]) +}) diff --git a/tools/test/linter/SpecValidator.test.ts b/tools/test/linter/SpecValidator.test.ts index 0c30c867b..181393f02 100644 --- a/tools/test/linter/SpecValidator.test.ts +++ b/tools/test/linter/SpecValidator.test.ts @@ -1,24 +1,22 @@ -import SpecValidator from "../../linter/SpecValidator"; +import SpecValidator from '../../linter/SpecValidator' test('validate()', () => { - const validator = new SpecValidator('./test/linter/fixtures/empty'); - expect(validator.validate()).toEqual([]); + const validator = new SpecValidator('./test/linter/fixtures/empty') + expect(validator.validate()).toEqual([]) - validator.namespaces_folder.validate = jest.fn().mockReturnValue([{file: 'namespaces/', message: 'namespace error'},]); - validator.schemas_folder.validate = jest.fn().mockReturnValue([{file: 'schemas/', message: 'schema error'},]); - validator.path_refs_validator.validate = jest.fn().mockReturnValue([{file: 'path_refs', message: 'path refs error'},]); - validator.schema_refs_validator.validate = jest.fn().mockReturnValue([{file: 'schema_refs', message: 'schema refs error'},]); + validator.namespaces_folder.validate = jest.fn().mockReturnValue([{ file: 'namespaces/', message: 'namespace error' }]) + validator.schemas_folder.validate = jest.fn().mockReturnValue([{ file: 'schemas/', message: 'schema error' }]) + validator.schema_refs_validator.validate = jest.fn().mockReturnValue([{ file: 'schema_refs', message: 'schema refs error' }]) - expect(validator.validate()).toEqual([ - {file: 'namespaces/', message: 'namespace error'}, - {file: 'schemas/', message: 'schema error'}, - ]); + expect(validator.validate()).toEqual([ + { file: 'namespaces/', message: 'namespace error' }, + { file: 'schemas/', message: 'schema error' } + ]) - validator.namespaces_folder.validate = jest.fn().mockReturnValue([]); - validator.schemas_folder.validate = jest.fn().mockReturnValue([]); + validator.namespaces_folder.validate = jest.fn().mockReturnValue([]) + validator.schemas_folder.validate = jest.fn().mockReturnValue([]) - expect(validator.validate()).toEqual([ - {file: 'path_refs', message: 'path refs error'}, - {file: 'schema_refs', message: 'schema refs error'}, - ]); -}); \ No newline at end of file + expect(validator.validate()).toEqual([ + { file: 'schema_refs', message: 'schema refs error' } + ]) +}) diff --git a/tools/test/linter/SupersededOperationsFile.test.ts b/tools/test/linter/SupersededOperationsFile.test.ts new file mode 100644 index 000000000..f5c6a7e3a --- /dev/null +++ b/tools/test/linter/SupersededOperationsFile.test.ts @@ -0,0 +1,11 @@ +import SupersededOperationsFile from '../../linter/components/SupersededOperationsFile' + +test('validate()', () => { + const validator = new SupersededOperationsFile('./test/linter/fixtures/_superseded_operations.yaml') + expect(validator.validate()).toEqual([ + { + file: 'fixtures/_superseded_operations.yaml', + message: "File content does not match JSON schema found in '../json_schemas/_superseded_operations.schema.yaml':\n [\n {\n \"instancePath\": \"/~1hello~1world/operations/1\",\n \"schemaPath\": \"#/patternProperties/%5E~1/properties/operations/items/enum\",\n \"keyword\": \"enum\",\n \"params\": {\n \"allowedValues\": [\n \"GET\",\n \"POST\",\n \"PUT\",\n \"DELETE\",\n \"HEAD\",\n \"OPTIONS\",\n \"PATCH\"\n ]\n },\n \"message\": \"must be equal to one of the allowed values\"\n }\n]" + } + ]) +}) diff --git a/tools/test/linter/factories/namespace_file.ts b/tools/test/linter/factories/namespace_file.ts index 42d3f6287..7546460a8 100644 --- a/tools/test/linter/factories/namespace_file.ts +++ b/tools/test/linter/factories/namespace_file.ts @@ -1,47 +1,49 @@ -import NamespaceFile from "../../../linter/components/NamespaceFile"; -import {OpenAPIV3} from "openapi-types"; -import {mocked_operation_group} from "./operation_group"; +import NamespaceFile from '../../../linter/components/NamespaceFile' +import { type OpenAPIV3 } from 'openapi-types' +import { mocked_operation_group } from './operation_group' -export function namespace_file(fixture_file: string): NamespaceFile { - return new NamespaceFile(`./test/linter/fixtures/file_validators/namespaces/${fixture_file}`); +export function namespace_file (fixture_file: string): NamespaceFile { + return new NamespaceFile(`./test/linter/fixtures/file_validators/namespaces/${fixture_file}`) } interface MockedReturnedValues { - validate?: string[]; - validate_name?: string | void; - validate_schemas?: string | void; - validate_unresolved_refs?: string[]; - validate_unused_refs?: string[]; - validate_parameter_refs?: string[]; + validate?: string[] + validate_name?: string | undefined + validate_schemas?: string | undefined + validate_unresolved_refs?: string[] + validate_unused_refs?: string[] + validate_parameter_refs?: string[] } -export function mocked_namespace_file(ops: {returned_values?: MockedReturnedValues, spec?: Record, groups_errors?: string[][]}): NamespaceFile { - const ns_file = namespace_file('empty.yaml'); - ns_file.file = 'namespaces/indices.yaml'; - ns_file.namespace = 'indices'; - - if(ops.groups_errors) ns_file._operation_groups = ops.groups_errors.map((errors) => mocked_operation_group({validate: errors})); - if(ops.spec) ns_file._spec = { paths: {}, components: {}, ...ops.spec } as OpenAPIV3.Document; - - if(ops.returned_values) { - if(ops.returned_values.validate) { - ns_file.validate = jest.fn(); - (ns_file.validate as jest.Mock).mockReturnValue(ops.returned_values.validate); - return ns_file; - } - - ns_file.validate_name = jest.fn(); - ns_file.validate_schemas = jest.fn(); - ns_file.validate_unresolved_refs = jest.fn(); - ns_file.validate_unused_refs = jest.fn(); - ns_file.validate_parameter_refs = jest.fn(); - - if(ops.returned_values.validate_name) (ns_file.validate_name as jest.Mock).mockReturnValue(ops.returned_values.validate_name); - if(ops.returned_values.validate_schemas) (ns_file.validate_schemas as jest.Mock).mockReturnValue(ops.returned_values.validate_schemas); - if(ops.returned_values.validate_unresolved_refs) (ns_file.validate_unresolved_refs as jest.Mock).mockReturnValue(ops.returned_values.validate_unresolved_refs); - if(ops.returned_values.validate_unused_refs) (ns_file.validate_unused_refs as jest.Mock).mockReturnValue(ops.returned_values.validate_unused_refs); - if(ops.returned_values.validate_parameter_refs) (ns_file.validate_parameter_refs as jest.Mock).mockReturnValue(ops.returned_values.validate_parameter_refs); +export function mocked_namespace_file (ops: { returned_values?: MockedReturnedValues, spec?: Record, groups_errors?: string[][] }): NamespaceFile { + const ns_file = namespace_file('empty.yaml') + ns_file.file = 'namespaces/indices.yaml' + ns_file.namespace = 'indices' + + // eslint-disable-next-line @typescript-eslint/dot-notation + if (ops.groups_errors) ns_file['_operation_groups'] = ops.groups_errors.map((errors) => mocked_operation_group({ validate: errors })) + // eslint-disable-next-line @typescript-eslint/dot-notation,@typescript-eslint/consistent-type-assertions + if (ops.spec) ns_file['_spec'] = { paths: {}, components: {}, ...ops.spec } as OpenAPIV3.Document + + if (ops.returned_values) { + if (ops.returned_values.validate) { + ns_file.validate = jest.fn(); + (ns_file.validate as jest.Mock).mockReturnValue(ops.returned_values.validate) + return ns_file } - return ns_file; -} \ No newline at end of file + ns_file.validate_name = jest.fn() + ns_file.validate_schemas = jest.fn() + ns_file.validate_unresolved_refs = jest.fn() + ns_file.validate_unused_refs = jest.fn() + ns_file.validate_parameter_refs = jest.fn() + + if (ops.returned_values.validate_name != null) (ns_file.validate_name as jest.Mock).mockReturnValue(ops.returned_values.validate_name) + if (ops.returned_values.validate_schemas != null) (ns_file.validate_schemas as jest.Mock).mockReturnValue(ops.returned_values.validate_schemas) + if (ops.returned_values.validate_unresolved_refs != null) (ns_file.validate_unresolved_refs as jest.Mock).mockReturnValue(ops.returned_values.validate_unresolved_refs) + if (ops.returned_values.validate_unused_refs != null) (ns_file.validate_unused_refs as jest.Mock).mockReturnValue(ops.returned_values.validate_unused_refs) + if (ops.returned_values.validate_parameter_refs != null) (ns_file.validate_parameter_refs as jest.Mock).mockReturnValue(ops.returned_values.validate_parameter_refs) + } + + return ns_file +} diff --git a/tools/test/linter/factories/operation.ts b/tools/test/linter/factories/operation.ts index 3721e0ec4..f17e092cb 100644 --- a/tools/test/linter/factories/operation.ts +++ b/tools/test/linter/factories/operation.ts @@ -1,48 +1,49 @@ -import Operation from "../../../linter/components/Operation"; -import {OperationSpec} from "../../../types"; +import Operation from '../../../linter/components/Operation' +import { type OperationSpec } from '../../../types' -export function operation(spec: Record, file_name = 'indices.yaml') { - return new Operation(`namespaces/${file_name}`, '/{index}/something/{abc_xyz}', 'post', spec as OperationSpec); +export function operation (spec: Record, file_name = 'indices.yaml'): Operation { + return new Operation(`namespaces/${file_name}`, '/{index}/something/{abc_xyz}', 'post', spec as OperationSpec) } interface MockedReturnedValues { - validate?: string[]; - validate_group?: string | void; - validate_namespace?: string | void; - validate_operationId?: string | void; - validate_description?: string | void; - validate_requestBody?: string | void; - validate_responses?: string[]; - validate_parameters?: string | void; - validate_path_parameters?: string | void; + validate?: string[] + validate_group?: string | undefined + validate_namespace?: string | undefined + validate_operationId?: string | undefined + validate_description?: string | undefined + validate_requestBody?: string | undefined + validate_responses?: string[] + validate_parameters?: string | undefined + validate_path_parameters?: string | undefined } -export function mocked_operation(returned_values: MockedReturnedValues) { - const op = new Operation('', '', '', {} as OperationSpec); +export function mocked_operation (returned_values: MockedReturnedValues): Operation { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const op: Operation = new Operation('', '', '', {} as OperationSpec) - if(returned_values.validate) { - op.validate = jest.fn(); - (op.validate as jest.Mock).mockReturnValue(returned_values.validate) - return op; - } + if (returned_values.validate) { + op.validate = jest.fn(); + (op.validate as jest.Mock).mockReturnValue(returned_values.validate) + return op + } - op.validate_group = jest.fn(); - op.validate_namespace = jest.fn(); - op.validate_operationId = jest.fn(); - op.validate_description = jest.fn(); - op.validate_requestBody = jest.fn(); - op.validate_responses = jest.fn(); - op.validate_parameters = jest.fn(); - op.validate_path_parameters = jest.fn(); + op.validate_group = jest.fn() + op.validate_namespace = jest.fn() + op.validate_operation_id = jest.fn() + op.validate_description = jest.fn() + op.validate_request_body = jest.fn() + op.validate_responses = jest.fn() + op.validate_parameters = jest.fn() + op.validate_path_parameters = jest.fn() - if(returned_values.validate_group) (op.validate_group as jest.Mock).mockReturnValue(returned_values.validate_group); - if(returned_values.validate_namespace) (op.validate_namespace as jest.Mock).mockReturnValue(returned_values.validate_namespace); - if(returned_values.validate_operationId) (op.validate_operationId as jest.Mock).mockReturnValue(returned_values.validate_operationId); - if(returned_values.validate_description) (op.validate_description as jest.Mock).mockReturnValue(returned_values.validate_description); - if(returned_values.validate_requestBody) (op.validate_requestBody as jest.Mock).mockReturnValue(returned_values.validate_requestBody); - if(returned_values.validate_responses) (op.validate_responses as jest.Mock).mockReturnValue(returned_values.validate_responses); - if(returned_values.validate_parameters) (op.validate_parameters as jest.Mock).mockReturnValue(returned_values.validate_parameters); - if(returned_values.validate_path_parameters) (op.validate_path_parameters as jest.Mock).mockReturnValue(returned_values.validate_path_parameters); + if (returned_values.validate_group != null) (op.validate_group as jest.Mock).mockReturnValue(returned_values.validate_group) + if (returned_values.validate_namespace != null) (op.validate_namespace as jest.Mock).mockReturnValue(returned_values.validate_namespace) + if (returned_values.validate_operationId != null) (op.validate_operation_id as jest.Mock).mockReturnValue(returned_values.validate_operationId) + if (returned_values.validate_description != null) (op.validate_description as jest.Mock).mockReturnValue(returned_values.validate_description) + if (returned_values.validate_requestBody != null) (op.validate_request_body as jest.Mock).mockReturnValue(returned_values.validate_requestBody) + if (returned_values.validate_responses != null) (op.validate_responses as jest.Mock).mockReturnValue(returned_values.validate_responses) + if (returned_values.validate_parameters != null) (op.validate_parameters as jest.Mock).mockReturnValue(returned_values.validate_parameters) + if (returned_values.validate_path_parameters != null) (op.validate_path_parameters as jest.Mock).mockReturnValue(returned_values.validate_path_parameters) - return op; + return op } diff --git a/tools/test/linter/factories/operation_group.ts b/tools/test/linter/factories/operation_group.ts index e19cc3080..1439a6834 100644 --- a/tools/test/linter/factories/operation_group.ts +++ b/tools/test/linter/factories/operation_group.ts @@ -1,43 +1,43 @@ -import OperationGroup from "../../../linter/components/OperationGroup"; -import {operation, mocked_operation} from "./operation"; +import OperationGroup from '../../../linter/components/OperationGroup' +import { operation, mocked_operation } from './operation' -export function operation_group (operation_specs: Record[]): OperationGroup { - const operations = operation_specs.map((spec) => { - return operation({'x-operation-group': 'indices.create', ...spec}); - }); - return new OperationGroup(`namespaces/indices.yaml`, 'indices.create', operations); +export function operation_group (operation_specs: Array>): OperationGroup { + const operations = operation_specs.map((spec) => { + return operation({ 'x-operation-group': 'indices.create', ...spec }) + }) + return new OperationGroup('namespaces/indices.yaml', 'indices.create', operations) } interface MockedReturnedValues { - validate?: string[]; - validate_description?: string | void; - validate_externalDocs?: string | void; - validate_requestBody?: string | void; - validate_responses?: string | void; - validate_query_parameters?: string | void; + validate?: string[] + validate_description?: string | undefined + validate_externalDocs?: string | undefined + validate_requestBody?: string | undefined + validate_responses?: string | undefined + validate_query_parameters?: string | undefined } -export function mocked_operation_group(returned_values: MockedReturnedValues, ops_errors: string[][] = []) { - const ops = ops_errors.map((errors) => mocked_operation({validate: errors})); - const op_group = new OperationGroup('', '', ops); +export function mocked_operation_group (returned_values: MockedReturnedValues, ops_errors: string[][] = []): OperationGroup { + const ops = ops_errors.map((errors) => mocked_operation({ validate: errors })) + const op_group = new OperationGroup('', '', ops) - if(returned_values.validate) { - op_group.validate = jest.fn(); - (op_group.validate as jest.Mock).mockReturnValue(returned_values.validate) - return op_group; - } + if (returned_values.validate) { + op_group.validate = jest.fn(); + (op_group.validate as jest.Mock).mockReturnValue(returned_values.validate) + return op_group + } - op_group.validate_description = jest.fn(); - op_group.validate_externalDocs = jest.fn(); - op_group.validate_requestBody = jest.fn(); - op_group.validate_responses = jest.fn(); - op_group.validate_query_parameters = jest.fn(); + op_group.validate_description = jest.fn() + op_group.validate_external_docs = jest.fn() + op_group.validate_request_body = jest.fn() + op_group.validate_responses = jest.fn() + op_group.validate_query_parameters = jest.fn() - if(returned_values.validate_description) (op_group.validate_description as jest.Mock).mockReturnValue(returned_values.validate_description); - if(returned_values.validate_externalDocs) (op_group.validate_externalDocs as jest.Mock).mockReturnValue(returned_values.validate_externalDocs); - if(returned_values.validate_requestBody) (op_group.validate_requestBody as jest.Mock).mockReturnValue(returned_values.validate_requestBody); - if(returned_values.validate_responses) (op_group.validate_responses as jest.Mock).mockReturnValue(returned_values.validate_responses); - if(returned_values.validate_query_parameters) (op_group.validate_query_parameters as jest.Mock).mockReturnValue(returned_values.validate_query_parameters); + if (returned_values.validate_description != null) (op_group.validate_description as jest.Mock).mockReturnValue(returned_values.validate_description) + if (returned_values.validate_externalDocs != null) (op_group.validate_external_docs as jest.Mock).mockReturnValue(returned_values.validate_externalDocs) + if (returned_values.validate_requestBody != null) (op_group.validate_request_body as jest.Mock).mockReturnValue(returned_values.validate_requestBody) + if (returned_values.validate_responses != null) (op_group.validate_responses as jest.Mock).mockReturnValue(returned_values.validate_responses) + if (returned_values.validate_query_parameters != null) (op_group.validate_query_parameters as jest.Mock).mockReturnValue(returned_values.validate_query_parameters) - return op_group; -} \ No newline at end of file + return op_group +} diff --git a/tools/test/linter/factories/schema.ts b/tools/test/linter/factories/schema.ts index c6775da5f..3eb32c943 100644 --- a/tools/test/linter/factories/schema.ts +++ b/tools/test/linter/factories/schema.ts @@ -1,27 +1,27 @@ -import Schema from "../../../linter/components/Schema"; -import {OpenAPIV3} from "openapi-types"; +import Schema from '../../../linter/components/Schema' +import { type OpenAPIV3 } from 'openapi-types' -export function schema(name: string, spec: Record = {}): Schema { - return new Schema(`_common.yaml`, name, spec as OpenAPIV3.SchemaObject); +export function schema (name: string, spec: Record = {}): Schema { + return new Schema('_common.yaml', name, spec as OpenAPIV3.SchemaObject) } interface MockedReturnedValues { - validate?: string[]; - validate_name?: string | void; + validate?: string[] + validate_name?: string | undefined } -export function mocked_schema(returned_values: MockedReturnedValues) { - const schema = new Schema(`_common.yaml`, 'Schema', {} as OpenAPIV3.SchemaObject); +export function mocked_schema (returned_values: MockedReturnedValues): Schema { + const schema = new Schema('_common.yaml', 'Schema', {}) - if(returned_values.validate) { - schema.validate = jest.fn(); - (schema.validate as jest.Mock).mockReturnValue(returned_values.validate); - return schema; - } + if (returned_values.validate) { + schema.validate = jest.fn(); + (schema.validate as jest.Mock).mockReturnValue(returned_values.validate) + return schema + } - schema.validate_name = jest.fn(); + schema.validate_name = jest.fn() - if(returned_values.validate_name) (schema.validate_name as jest.Mock).mockReturnValue(returned_values.validate_name); + if (returned_values.validate_name != null) (schema.validate_name as jest.Mock).mockReturnValue(returned_values.validate_name) - return schema; -} \ No newline at end of file + return schema +} diff --git a/tools/test/linter/factories/schema_file.ts b/tools/test/linter/factories/schema_file.ts index 3dfeebd16..6d232211a 100644 --- a/tools/test/linter/factories/schema_file.ts +++ b/tools/test/linter/factories/schema_file.ts @@ -1,30 +1,31 @@ -import {mocked_schema, schema} from "./schema"; -import SchemaFile from "../../../linter/components/SchemaFile"; +import { mocked_schema } from './schema' +import SchemaFile from '../../../linter/components/SchemaFile' -export function schema_file(fixture:string): SchemaFile { - return new SchemaFile(`./test/linter/fixtures/file_validators/schemas/${fixture}`); +export function schema_file (fixture: string): SchemaFile { + return new SchemaFile(`./test/linter/fixtures/file_validators/schemas/${fixture}`) } interface MockedReturnedValues { - validate?: string[]; - validate_category?: string | void; + validate?: string[] + validate_category?: string | undefined } -export function mocked_schema_file(ops: {returned_values?: MockedReturnedValues, schema_errors?: string[][]}): SchemaFile { - const validator = schema_file('_common.empty.yaml'); - if(ops.schema_errors) validator._schemas = ops.schema_errors.map((errors) => mocked_schema({validate: errors})); +export function mocked_schema_file (ops: { returned_values?: MockedReturnedValues, schema_errors?: string[][] }): SchemaFile { + const validator = schema_file('_common.empty.yaml') + // eslint-disable-next-line @typescript-eslint/dot-notation + if (ops.schema_errors) validator['_schemas'] = ops.schema_errors.map((errors) => mocked_schema({ validate: errors })) - if(ops.returned_values) { - if(ops.returned_values.validate) { - validator.validate = jest.fn(); - (validator.validate as jest.Mock).mockReturnValue(ops.returned_values.validate); - return validator; - } + if (ops.returned_values) { + if (ops.returned_values.validate) { + validator.validate = jest.fn(); + (validator.validate as jest.Mock).mockReturnValue(ops.returned_values.validate) + return validator + } - validator.validate_category = jest.fn(); + validator.validate_category = jest.fn() - if(ops.returned_values.validate_category) (validator.validate_category as jest.Mock).mockReturnValue(ops.returned_values.validate_category); - } + if (ops.returned_values.validate_category != null) (validator.validate_category as jest.Mock).mockReturnValue(ops.returned_values.validate_category) + } - return validator; -} \ No newline at end of file + return validator +} diff --git a/tools/test/linter/fixtures/_info.yaml b/tools/test/linter/fixtures/_info.yaml new file mode 100644 index 000000000..29b411f57 --- /dev/null +++ b/tools/test/linter/fixtures/_info.yaml @@ -0,0 +1,2 @@ +title: OpenSearch API Specification +version: 1.0.0 \ No newline at end of file diff --git a/tools/test/linter/fixtures/_superseded_operations.yaml b/tools/test/linter/fixtures/_superseded_operations.yaml new file mode 100644 index 000000000..20c92eec9 --- /dev/null +++ b/tools/test/linter/fixtures/_superseded_operations.yaml @@ -0,0 +1,7 @@ +$schema: ../json_schemas/_superseded_operations.schema.yaml + +/hello/world: + superseded_by: /goodbye/world + operations: + - GET + - CLEAN \ No newline at end of file diff --git a/tools/test/linter/fixtures/empty/_info.yaml b/tools/test/linter/fixtures/empty/_info.yaml new file mode 100644 index 000000000..66d1923d4 --- /dev/null +++ b/tools/test/linter/fixtures/empty/_info.yaml @@ -0,0 +1,4 @@ +$schema: ../json_schemas/_info.schema.yaml + +title: '' +version: '' \ No newline at end of file diff --git a/tools/test/linter/fixtures/empty/_superseded_operations.yaml b/tools/test/linter/fixtures/empty/_superseded_operations.yaml new file mode 100644 index 000000000..6f143f79b --- /dev/null +++ b/tools/test/linter/fixtures/empty/_superseded_operations.yaml @@ -0,0 +1 @@ +$schema: ../json_schemas/_superseded_operations.schema.yaml \ No newline at end of file diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/cat.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/cat.yaml similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/cat.yaml rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/cat.yaml diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/dup_path_a.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/dup_path_a.yaml similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/dup_path_a.yaml rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/dup_path_a.yaml diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/dup_path_b.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/dup_path_b.yaml similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/dup_path_b.yaml rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/dup_path_b.yaml diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/dup_path_c.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/dup_path_c.yaml similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/dup_path_c.yaml rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/dup_path_c.yaml diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/indices.txt b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/indices.txt similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/indices.txt rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/indices.txt diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/invalid_spec.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/invalid_spec.yaml similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/invalid_spec.yaml rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/invalid_spec.yaml diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/invalid_yaml.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/invalid_yaml.yaml similarity index 100% rename from tools/test/linter/fixtures/folder_validators/namespaces/invalid_yaml.yaml rename to tools/test/linter/fixtures/folder_validators/namespaces/invalid_files/invalid_yaml.yaml diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/cat.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/cat.yaml new file mode 100644 index 000000000..b6c637735 --- /dev/null +++ b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/cat.yaml @@ -0,0 +1,27 @@ +paths: + '/_cat/aliases/{name}': + get: + x-operation-group: cat.aliases + operationId: cat.aliases.0 + description: 'CAT aliases.' + responses: + '200': + $ref: '#/components/responses/cat.aliases@200' + parameters: + - $ref: '#/components/parameters/cat.aliases::path.name' + - $ref: '#/components/parameters/cat.aliases::query.format' + - $ref: '#/components/parameters/cat.aliases::query.local' + +components: + responses: + cat.aliases@200: {} + parameters: + cat.aliases::path.name: + name: name + in: path + cat.aliases::query.format: + name: format + in: query + cat.aliases::query.local: + name: local + in: query \ No newline at end of file diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_a.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_a.yaml new file mode 100644 index 000000000..c0feea716 --- /dev/null +++ b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_a.yaml @@ -0,0 +1,4 @@ +paths: + '/{index}': {} + '/{index}/_clone': {} + '/{index}/_rollover': {} \ No newline at end of file diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_b.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_b.yaml new file mode 100644 index 000000000..6952dddeb --- /dev/null +++ b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_b.yaml @@ -0,0 +1,3 @@ +paths: + '/_rollover': {} + '/{index}/_rollover': {} \ No newline at end of file diff --git a/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_c.yaml b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_c.yaml new file mode 100644 index 000000000..30d77011d --- /dev/null +++ b/tools/test/linter/fixtures/folder_validators/namespaces/invalid_folder/dup_path_c.yaml @@ -0,0 +1,3 @@ +paths: + '/{index}': {} + '/{index}/_rollover': {} \ No newline at end of file diff --git a/tools/test/linter/fixtures/path_refs_validator/namespaces/cat.yaml b/tools/test/linter/fixtures/path_refs_validator/namespaces/cat.yaml deleted file mode 100644 index 983c3de46..000000000 --- a/tools/test/linter/fixtures/path_refs_validator/namespaces/cat.yaml +++ /dev/null @@ -1,3 +0,0 @@ -paths: - '/_cat/aliases': {} - '/_cat/allocation': {} \ No newline at end of file diff --git a/tools/test/linter/fixtures/path_refs_validator/namespaces/indices.yaml b/tools/test/linter/fixtures/path_refs_validator/namespaces/indices.yaml deleted file mode 100644 index e5481a420..000000000 --- a/tools/test/linter/fixtures/path_refs_validator/namespaces/indices.yaml +++ /dev/null @@ -1,3 +0,0 @@ -paths: - '/{index}/_alias/{name}' : {} - '/{index}/_aliases' : {} \ No newline at end of file diff --git a/tools/test/linter/fixtures/path_refs_validator/namespaces/missing.yaml b/tools/test/linter/fixtures/path_refs_validator/namespaces/missing.yaml deleted file mode 100644 index d64fbea98..000000000 --- a/tools/test/linter/fixtures/path_refs_validator/namespaces/missing.yaml +++ /dev/null @@ -1,3 +0,0 @@ -paths: - '/_missing': {} - '/_missing/{id}': {} \ No newline at end of file diff --git a/tools/test/linter/fixtures/path_refs_validator/opensearch-openapi.yaml b/tools/test/linter/fixtures/path_refs_validator/opensearch-openapi.yaml deleted file mode 100644 index 4ea72a813..000000000 --- a/tools/test/linter/fixtures/path_refs_validator/opensearch-openapi.yaml +++ /dev/null @@ -1,13 +0,0 @@ -paths: - '/{index}': - $ref: 'namespaces/indices.yaml#/paths/~1{index}' - '/{index}/_alias/{name}': - $ref: 'namespaces/indices.yaml#/paths/~1{index}~1_alias~1{name}' - '/_cat/aliases': - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1aliases' - '/_cat/allocation': - $ref: 'namespaces/cat.yaml#/paths/~1_cat~1allocation' - '/_cluster/health': - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1health' - '/_cluster/{id}': - $ref: 'namespaces/cluster.yaml#/paths/~1_cluster~1{id}' \ No newline at end of file diff --git a/tools/test/linter/fixtures/root.yaml b/tools/test/linter/fixtures/root.yaml deleted file mode 100644 index 8c4a08750..000000000 --- a/tools/test/linter/fixtures/root.yaml +++ /dev/null @@ -1,32 +0,0 @@ -paths: - '/': {} - '/_cat': - $ref: './namespaces/cat.yaml#/paths/_cat' - '/{index}': - get: - responses: - '200': - $ref: '#/components/responses/indices.create@200' - parameters: - - $ref: '#/components/parameters/indices.create::path.index' - - $ref: '#/components/parameters/indices.create::query.pretty' -components: - parameters: - _global::query.pretty: - x-global: true - name: beautify - in: query - schema: - type: boolean - _global::query.human: - x-global: false - name: human - in: query - schema: - type: boolean - _global::query.error_trace: - x-global: true - name: error_trace - in: query - schema: - type: boolean \ No newline at end of file diff --git a/tools/test/merger/OpenApiMerger.test.ts b/tools/test/merger/OpenApiMerger.test.ts index 32aec55e8..22d966404 100644 --- a/tools/test/merger/OpenApiMerger.test.ts +++ b/tools/test/merger/OpenApiMerger.test.ts @@ -1,12 +1,10 @@ -import OpenApiMerger from '../../merger/OpenApiMerger'; -import fs from 'fs'; -import yaml from 'yaml'; - +import OpenApiMerger from '../../merger/OpenApiMerger' +import fs from 'fs' test('merge()', async () => { - const merger = new OpenApiMerger('./test/merger/fixtures/spec/opensearch-openapi.yaml'); - merger.merge('./test/merger/opensearch-openapi.yaml'); - expect(fs.readFileSync('./test/merger/fixtures/expected.yaml', 'utf8')) - .toEqual(fs.readFileSync('./test/merger/opensearch-openapi.yaml', 'utf8')); - fs.unlinkSync('./test/merger/opensearch-openapi.yaml'); -}); \ No newline at end of file + const merger = new OpenApiMerger('./test/merger/fixtures/spec/') + merger.merge('./test/merger/opensearch-openapi.yaml') + expect(fs.readFileSync('./test/merger/fixtures/expected.yaml', 'utf8')) + .toEqual(fs.readFileSync('./test/merger/opensearch-openapi.yaml', 'utf8')) + fs.unlinkSync('./test/merger/opensearch-openapi.yaml') +}) diff --git a/tools/test/merger/fixtures/expected.yaml b/tools/test/merger/fixtures/expected.yaml index 1d28db66d..3c73f80a1 100644 --- a/tools/test/merger/fixtures/expected.yaml +++ b/tools/test/merger/fixtures/expected.yaml @@ -4,38 +4,70 @@ info: description: OpenSearch API version: 1.0.0 paths: - /adopt/{animal}: + /{index}: + post: + parameters: + - $ref: '#/components/parameters/indices.create::path.index' + - $ref: '#/components/parameters/indices.create::query.pretty' + - $ref: '#/components/parameters/_global::query.human' + requestBody: + $ref: '#/components/requestBodies/indices.create' + responses: + '200': + $ref: '#/components/responses/indices.create@200' + /adopt/{animal}/dockets/{docket}: get: + operationId: adopt.0 parameters: - $ref: '#/components/parameters/adopt::path.animal' + - $ref: '#/components/parameters/adopt::path.docket' - $ref: '#/components/parameters/_global::query.human' responses: '200': $ref: '#/components/responses/adopt@200' post: + operationId: adopt.1 parameters: - $ref: '#/components/parameters/adopt::path.animal' + - $ref: '#/components/parameters/adopt::path.docket' - $ref: '#/components/parameters/_global::query.human' requestBody: $ref: '#/components/requestBodies/adopt' responses: '200': $ref: '#/components/responses/adopt@200' + /replaced/adopting/{animal}/something/{docket}: + get: + operationId: adopt.0_superseded + parameters: + - $ref: '#/components/parameters/adopt::path.animal' + - $ref: '#/components/parameters/adopt::path.docket' + - $ref: '#/components/parameters/_global::query.human' + responses: + '200': + $ref: '#/components/responses/adopt@200' + deprecated: true + x-ignorable: true components: parameters: _global::query.human: - x-global: true name: human in: query description: Whether to return human readable values for statistics. schema: type: boolean default: true + x-global: true adopt::path.animal: name: animal in: path schema: $ref: '#/components/schemas/animals:Animal' + adopt::path.docket: + name: docket + in: path + schema: + type: number indices.create::path.index: name: index in: path diff --git a/tools/test/merger/fixtures/spec/opensearch-openapi.yaml b/tools/test/merger/fixtures/spec/_global_parameters.yaml similarity index 50% rename from tools/test/merger/fixtures/spec/opensearch-openapi.yaml rename to tools/test/merger/fixtures/spec/_global_parameters.yaml index 1e9a06617..cf491eee2 100644 --- a/tools/test/merger/fixtures/spec/opensearch-openapi.yaml +++ b/tools/test/merger/fixtures/spec/_global_parameters.yaml @@ -1,15 +1,10 @@ openapi: 3.1.0 info: - title: OpenSearch API - description: OpenSearch API - version: 1.0.0 -paths: - '/adopt/{animal}': - $ref: 'namespaces/shelter.yaml#/paths/~1adopt~1{animal}' + title: '' + version: '' components: parameters: - _global::query.human: - x-global: true + human: name: human in: query description: Whether to return human readable values for statistics. diff --git a/tools/test/merger/fixtures/spec/_info.yaml b/tools/test/merger/fixtures/spec/_info.yaml new file mode 100644 index 000000000..721aed8c9 --- /dev/null +++ b/tools/test/merger/fixtures/spec/_info.yaml @@ -0,0 +1,5 @@ +$schema: should-be-ignored + +title: OpenSearch API +description: OpenSearch API +version: 1.0.0 \ No newline at end of file diff --git a/tools/test/merger/fixtures/spec/_superseded_operations.yaml b/tools/test/merger/fixtures/spec/_superseded_operations.yaml new file mode 100644 index 000000000..4ddca20c2 --- /dev/null +++ b/tools/test/merger/fixtures/spec/_superseded_operations.yaml @@ -0,0 +1,10 @@ +/replaced/adopting/{a}/something/{b}: + superseded_by: /adopt/{animal}/dockets/{docket} + operations: + - GET + - DELETE +/something/else: + superseded_by: /not/here + operations: + - POST + - PUT \ No newline at end of file diff --git a/tools/test/merger/fixtures/spec/namespaces/ignored.yaml b/tools/test/merger/fixtures/spec/namespaces/indices.yaml similarity index 100% rename from tools/test/merger/fixtures/spec/namespaces/ignored.yaml rename to tools/test/merger/fixtures/spec/namespaces/indices.yaml diff --git a/tools/test/merger/fixtures/spec/namespaces/shelter.yaml b/tools/test/merger/fixtures/spec/namespaces/shelter.yaml index b91dc827a..0f1a2970b 100644 --- a/tools/test/merger/fixtures/spec/namespaces/shelter.yaml +++ b/tools/test/merger/fixtures/spec/namespaces/shelter.yaml @@ -4,16 +4,20 @@ info: description: OpenSearch API version: 1.0.0 paths: - '/adopt/{animal}': + '/adopt/{animal}/dockets/{docket}': get: + operationId: adopt.0 parameters: - $ref: '#/components/parameters/adopt::path.animal' + - $ref: '#/components/parameters/adopt::path.docket' responses: '200': $ref: '#/components/responses/adopt@200' post: + operationId: adopt.1 parameters: - $ref: '#/components/parameters/adopt::path.animal' + - $ref: '#/components/parameters/adopt::path.docket' requestBody: $ref: '#/components/requestBodies/adopt' responses: @@ -28,6 +32,11 @@ components: in: path schema: $ref: '../schemas/animals.yaml#/components/schemas/Animal' + adopt::path.docket: + name: docket + in: path + schema: + type: number responses: adopt@200: description: '' diff --git a/tools/types.ts b/tools/types.ts index dfd485de8..083a05f72 100644 --- a/tools/types.ts +++ b/tools/types.ts @@ -1,28 +1,32 @@ -import {OpenAPIV3} from "openapi-types"; +import { type OpenAPIV3 } from 'openapi-types' export interface OperationSpec extends OpenAPIV3.OperationObject { - 'x-operation-group': string; - 'x-version-added': string; - 'x-version-removed'?: string; - 'x-version-deprecated'?: string; - 'x-deprecation-message'?: string; - 'x-ignorable'?: boolean; + 'x-operation-group': string + 'x-version-added': string + 'x-version-removed'?: string + 'x-version-deprecated'?: string + 'x-deprecation-message'?: string + 'x-ignorable'?: boolean - parameters?: OpenAPIV3.ReferenceObject[]; - requestBody?: OpenAPIV3.ReferenceObject; - responses: { [code: string]: OpenAPIV3.ReferenceObject }; + parameters?: OpenAPIV3.ReferenceObject[] + requestBody?: OpenAPIV3.ReferenceObject + responses: Record } export interface ParameterSpec extends OpenAPIV3.ParameterObject { - schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject; - 'x-data-type'?: string; - 'x-version-deprecated'?: string; - 'x-deprecation-message'?: string; - 'x-global'?: boolean; + schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject + 'x-data-type'?: string + 'x-version-deprecated'?: string + 'x-deprecation-message'?: string + 'x-global'?: boolean } export interface ValidationError { - file: string; - location?: string; - message: string; -} \ No newline at end of file + file: string + location?: string + message: string +} + +export type HttpVerb = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE' +export type OperationPath = string +export type SupersededOperationMap = Record