diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
new file mode 100644
index 0000000..be0bac4
--- /dev/null
+++ b/.github/workflows/integration-tests.yml
@@ -0,0 +1,60 @@
+name: Run Integration Tests
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ integration-tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [18.x]
+ services:
+ registry:
+ image: registry:2
+ ports:
+ - 5000:5000
+ steps:
+ - name: Check out the repo
+ uses: actions/checkout@v4
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ id: buildx
+ uses: docker/setup-buildx-action@v3
+ with:
+ driver-opts: network=host
+
+ - name: Copy patches for Docker Buildx
+ run: cp -r patches/* packages/graphql-mesh/patches
+
+ - name: Build and push on local registry
+ id: docker_build
+ uses: docker/build-push-action@v5
+ with:
+ context: ./packages/graphql-mesh
+ push: true
+ tags: localhost:5000/test/graphql-mesh:latest
+ platforms: linux/amd64
+
+ - name: Setup services for testing purpose
+ run: export IMAGE_TAG=localhost:5000/test/graphql-mesh:latest && cd ./test/integration && docker compose up -d
+
+ - name: Wait for services to be ready
+ run: sleep 30
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ matrix.node-version }}
+
+ - name: Install dependencies
+ run: cd ./test/integration/tests && npm install
+
+ - name: Run tests
+ run: cd ./test/integration/tests && npm test
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 2dea47d..49ac0f0 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -15,8 +15,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
- with:
- node-version: ${{ matrix.node-version }}
- name: Set up Node.js
uses: actions/setup-node@v3
diff --git a/packages/directive-headers/src/index.ts b/packages/directive-headers/src/index.ts
index cab6ce1..42d1433 100644
--- a/packages/directive-headers/src/index.ts
+++ b/packages/directive-headers/src/index.ts
@@ -56,3 +56,15 @@ export default class HeadersDirectiveTransform implements MeshTransform {
})
}
}
+
+export const headersDirectiveTypeDef: string = /* GraphQL */ `
+ input Header {
+ key: String
+ value: String
+ }
+
+ """
+ This directive is used to add headers to the request.
+ """
+ directive @headers(input: [Header]) on FIELD
+`
diff --git a/packages/directive-no-auth/src/index.ts b/packages/directive-no-auth/src/index.ts
index 51d4808..c6b84ca 100644
--- a/packages/directive-no-auth/src/index.ts
+++ b/packages/directive-no-auth/src/index.ts
@@ -50,3 +50,10 @@ export default class NoAuthDirectiveTransform implements MeshTransform {
})
}
}
+
+export const noAuthDirectiveTypeDef: string = /* GraphQL */ `
+ """
+ This directive is used to disable the authorization header for the request
+ """
+ directive @noAuth on FIELD
+`
diff --git a/packages/directive-spl/package.json b/packages/directive-spl/package.json
index 523197a..b874f8e 100644
--- a/packages/directive-spl/package.json
+++ b/packages/directive-spl/package.json
@@ -19,7 +19,6 @@
"build:cjs": "tsc --project tsconfig-cjs.json",
"build:esm": "tsc --project tsconfig-esm.json",
"build": "rm -rf _build && npm run build:esm && npm run build:cjs && node ./scripts/prepare-package-json",
- "dev": "vite",
"pack": "npm run build && npm pack --pack-destination ../graphql-mesh/local-pkg",
"test": "vitest"
},
diff --git a/packages/directive-spl/src/index.ts b/packages/directive-spl/src/index.ts
index 400455d..4d26017 100644
--- a/packages/directive-spl/src/index.ts
+++ b/packages/directive-spl/src/index.ts
@@ -52,3 +52,10 @@ export default class SplDirectiveTransform implements MeshTransform {
})
}
}
+
+export const splDirectiveTypeDef: string = /* GraphQL */ `
+ """
+ This is a very small, lightweight, straightforward and non-evaluated expression language to sort, filter and paginate arrays of maps.
+ """
+ directive @SPL(query: String) on FIELD
+`
diff --git a/packages/directive-spl/vite.config.ts b/packages/directive-spl/vite.config.ts
index 2abcb3e..0324084 100644
--- a/packages/directive-spl/vite.config.ts
+++ b/packages/directive-spl/vite.config.ts
@@ -1,16 +1,4 @@
///
// Configure Vitest (https://vitest.dev/config/)
-import { resolve } from 'path'
import { defineConfig } from 'vite'
-import dts from 'vite-plugin-dts'
-// https://vitejs.dev/guide/build.html#library-mode
-export default defineConfig({
- build: {
- lib: {
- entry: resolve(__dirname, 'src/index.ts'),
- name: 'my-lib',
- fileName: 'my-lib'
- }
- },
- plugins: [dts()]
-})
+export default defineConfig({})
diff --git a/packages/graphql-mesh/.meshrc.ts b/packages/graphql-mesh/.meshrc.ts
index 21ada87..2784d42 100644
--- a/packages/graphql-mesh/.meshrc.ts
+++ b/packages/graphql-mesh/.meshrc.ts
@@ -1,5 +1,8 @@
import { YamlConfig } from '@graphql-mesh/types'
import ConfigFromSwaggers from './utils/ConfigFromSwaggers'
+import { splDirectiveTypeDef } from 'directive-spl'
+import { headersDirectiveTypeDef } from 'directive-headers'
+import { noAuthDirectiveTypeDef } from 'directive-no-auth'
const configFromSwaggers = new ConfigFromSwaggers()
const { defaultConfig, additionalTypeDefs, sources } =
@@ -17,13 +20,19 @@ const config = {
}
},
...(defaultConfig.transforms || [])
- ],
+ ].filter(Boolean),
sources: [...sources],
- additionalTypeDefs: [defaultConfig.additionalTypeDefs || '', ...additionalTypeDefs],
+ additionalTypeDefs: [
+ splDirectiveTypeDef,
+ headersDirectiveTypeDef,
+ noAuthDirectiveTypeDef,
+ additionalTypeDefs,
+ defaultConfig.additionalTypeDefs || ''
+ ].filter(Boolean),
additionalResolvers: [
...(defaultConfig.additionalResolvers || []),
'./utils/additionalResolvers.ts'
- ]
+ ].filter(Boolean)
}
export default config
diff --git a/packages/graphql-mesh/Dockerfile b/packages/graphql-mesh/Dockerfile
index 276d6f2..e2a7129 100644
--- a/packages/graphql-mesh/Dockerfile
+++ b/packages/graphql-mesh/Dockerfile
@@ -31,5 +31,6 @@ VOLUME /app/sources
VOLUME /app/config.yaml
VOLUME /app/transforms
VOLUME /app/plugins
+VOLUME /app/resolvers
CMD [ "npm", "run", "serve" ]
diff --git a/packages/graphql-mesh/local-pkg/directive-headers-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-headers-1.0.0.tgz
index 4618084..e0ed99a 100644
Binary files a/packages/graphql-mesh/local-pkg/directive-headers-1.0.0.tgz and b/packages/graphql-mesh/local-pkg/directive-headers-1.0.0.tgz differ
diff --git a/packages/graphql-mesh/local-pkg/directive-no-auth-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-no-auth-1.0.0.tgz
index 7667a24..23c6179 100644
Binary files a/packages/graphql-mesh/local-pkg/directive-no-auth-1.0.0.tgz and b/packages/graphql-mesh/local-pkg/directive-no-auth-1.0.0.tgz differ
diff --git a/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz b/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz
index 4c91b36..506efb6 100644
Binary files a/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz and b/packages/graphql-mesh/local-pkg/directive-spl-1.0.0.tgz differ
diff --git a/packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz b/packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz
index 0c456c0..93b007f 100644
Binary files a/packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz and b/packages/graphql-mesh/local-pkg/inject-additional-transforms-1.0.0.tgz differ
diff --git a/packages/graphql-mesh/scripts/download-sources.ts b/packages/graphql-mesh/scripts/download-sources.ts
index 31bad97..89a975b 100644
--- a/packages/graphql-mesh/scripts/download-sources.ts
+++ b/packages/graphql-mesh/scripts/download-sources.ts
@@ -10,20 +10,34 @@ let config = getConfig()
const sources = config?.sources?.filter((source) => source?.handler?.openapi) || []
const swaggers = sources.map((source) => source?.handler?.openapi?.source) || []
+/**
+ * Get the name of generated from the source object
+ * @param {Record} source
+ * @returns {string | undefined}
+ */
+const getFileName = (url: string): string | undefined => {
+ return sources.find((source) => source?.handler?.openapi?.source === url)?.name
+}
+
/**
* Download the swagger from the given URL and save it to the sources folder
* @param {string} url
*/
-const downSwaggerFromUrl = async (url: string): Promise => {
+const downSwaggerFromUrl = async (url: string | undefined, index: string): Promise => {
+ if (!url) return Promise.resolve()
try {
const content: Record = await readFileOrUrl(url, {
allowUnknownExtensions: true,
cwd: '.',
fetch: fetch,
- importFn: null,
+ importFn: (mod) => import(mod),
logger: logger
})
- const fileName = url.split('/').pop()
+ let fileName = getFileName(url) || `${index}-${url.split('/').pop()}`
+ if (!fileName.endsWith('.json')) {
+ fileName += '.json'
+ }
+
if (fileName) {
const filePath = `./sources/${fileName}`
writeFileSync(filePath, JSON.stringify(content, null, 2), 'utf8')
@@ -37,7 +51,7 @@ const downSwaggerFromUrl = async (url: string): Promise => {
* Download all the swaggers from the given URLs
* @param {string[]} swaggers
*/
-const downloadSwaggers = (swaggers: string[]) => {
+const downloadSwaggers = (swaggers: (string | undefined)[]) => {
logger.info(`Downloading ${swaggers.length} swaggers sources...`)
// Create the sources folder if it doesn't exist
@@ -46,7 +60,7 @@ const downloadSwaggers = (swaggers: string[]) => {
}
if (swaggers.length) {
- swaggers.forEach(downSwaggerFromUrl)
+ swaggers.forEach((file, index) => downSwaggerFromUrl(file, index.toString()))
}
}
diff --git a/packages/graphql-mesh/utils/ConfigFromSwaggers.ts b/packages/graphql-mesh/utils/ConfigFromSwaggers.ts
index 2082f37..2f822c9 100644
--- a/packages/graphql-mesh/utils/ConfigFromSwaggers.ts
+++ b/packages/graphql-mesh/utils/ConfigFromSwaggers.ts
@@ -1,11 +1,10 @@
import { globSync } from 'glob'
import { readFileSync } from 'node:fs'
import { Catalog, Spec, SwaggerName, ConfigExtension } from '../types'
-import { getConfig, getSourceOpenapiEnpoint } from './config'
+import { getConfig, getSourceName, getSourceOpenapiEnpoint } from './config'
import { getAvailableTypes } from './swaggers'
import { mergeObjects } from './helpers'
import { generateTypeDefsAndResolversFromSwagger } from './swaggers'
-import { directiveTypeDefs } from './directive-typedefs'
export default class ConfigFromSwaggers {
swaggers: SwaggerName[] = []
@@ -28,7 +27,7 @@ export default class ConfigFromSwaggers {
content?.['application/json']?.schema['$ref'] ?? content?.['*/*']?.schema['$ref']
const schema = ref?.replace('#/components/schemas/', '')
if (schema) {
- acc[path] = [query?.operationId, schema, this.swaggers[i]]
+ acc[path] = [query?.operationId || '', schema, this.swaggers[i]]
}
})
return acc
@@ -37,18 +36,20 @@ export default class ConfigFromSwaggers {
getInterfacesWithChildren() {
this.specs.forEach((s) => {
- const { schemas } = s.components
- const entries = Object.entries(schemas).filter(([_, value]) =>
+ const { schemas } = s.components || {}
+ const entries = Object.entries(schemas || {}).filter(([_, value]) =>
Object.keys(value).includes('discriminator')
)
for (const [schemaKey, schemaValue] of entries) {
- const mapping = schemaValue['discriminator']['mapping'] ?? {}
- const mappingTypes = []
- mappingTypes.push(
- ...Object.keys(mapping)
- .filter((k) => k !== schemaKey)
- .map((k) => mapping[k].replace('#/components/schemas/', ''))
- )
+ const mapping: { [key: string]: string } = schemaValue['discriminator']['mapping'] ?? {}
+ const mappingTypes: string[] = []
+ if (Object.keys(mapping).length > 0) {
+ mappingTypes.push(
+ ...Object.keys(mapping)
+ .filter((k) => k !== schemaKey)
+ .map((k) => mapping[k].replace('#/components/schemas/', ''))
+ )
+ }
if (this.interfacesWithChildren[schemaKey] === undefined) {
this.interfacesWithChildren[schemaKey] = mappingTypes
} else {
@@ -71,7 +72,8 @@ export default class ConfigFromSwaggers {
spec,
availableTypes,
this.getInterfacesWithChildren(),
- this.catalog
+ this.catalog,
+ this.config
)
acc.typeDefs += typeDefs
acc.resolvers = mergeObjects(acc.resolvers, resolvers)
@@ -84,14 +86,16 @@ export default class ConfigFromSwaggers {
getOpenApiSources() {
return (
this.swaggers.map((source) => ({
- name: source,
+ name: getSourceName(source, this.config),
handler: {
openapi: {
source,
endpoint: getSourceOpenapiEnpoint(source, this.config) || '{env.ENDPOINT}',
ignoreErrorResponses: true,
operationHeaders: {
- Authorization: `{context.headers["authorization"]}`
+ Authorization: `{context.headers["authorization"]}`,
+ ...(this.config.sources?.find((item) => source.includes(item.name))?.handler?.openapi
+ ?.operationHeaders || {})
}
}
}
@@ -118,7 +122,7 @@ export default class ConfigFromSwaggers {
getMeshConfigFromSwaggers(): {
defaultConfig: any
- additionalTypeDefs: string[]
+ additionalTypeDefs: string
additionalResolvers: any
sources: any[]
} {
@@ -126,7 +130,7 @@ export default class ConfigFromSwaggers {
return {
defaultConfig: this.config,
- additionalTypeDefs: [typeDefs, directiveTypeDefs],
+ additionalTypeDefs: typeDefs,
additionalResolvers: resolvers,
sources: [...this.getOpenApiSources(), ...this.getOtherSources()]
}
diff --git a/packages/graphql-mesh/utils/config/index.ts b/packages/graphql-mesh/utils/config/index.ts
index 4859499..edd32d7 100644
--- a/packages/graphql-mesh/utils/config/index.ts
+++ b/packages/graphql-mesh/utils/config/index.ts
@@ -43,8 +43,17 @@ export const getSourceOpenapiEnpoint = (
source: string,
config: YamlConfig.Config
): string | undefined => {
- const data = config.sources?.find((item) =>
- item?.handler?.openapi?.source?.includes(source.split('/').pop())
- )
+ const data = config.sources?.find((item) => source.includes(item.name))
return data?.handler.openapi?.endpoint
}
+
+/** Get source name from config
+ * @param source {string} - source name
+ * @param config {YamlConfig.Config} - config object
+ * @returns {string} - source name
+ *
+ */
+export const getSourceName = (source: string, config: YamlConfig.Config): string => {
+ const data = config.sources?.find((item) => source.includes(item.name))
+ return data?.name || source
+}
diff --git a/packages/graphql-mesh/utils/directive-typedefs/index.ts b/packages/graphql-mesh/utils/directive-typedefs/index.ts
deleted file mode 100644
index 6c92b9a..0000000
--- a/packages/graphql-mesh/utils/directive-typedefs/index.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export const directiveTypeDefs = /* GraphQL */ `
- """
- This is a very small, lightweight, straightforward and non-evaluated expression language to sort, filter and paginate arrays of maps.
- """
- directive @SPL(query: String) on FIELD
-
- """
- This directive is used to disable authentication for a specific operation.
- """
- directive @noAuth on FIELD
-
- type LinkItem {
- rel: String
- href: String
- }
-
- input Header {
- key: String
- value: String
- }
-
- """
- This directive is used to add headers to the request.
- """
- directive @headers(input: [Header]) on FIELD
-`
diff --git a/packages/graphql-mesh/utils/swaggers/index.ts b/packages/graphql-mesh/utils/swaggers/index.ts
index 5bc1a04..66b0f40 100644
--- a/packages/graphql-mesh/utils/swaggers/index.ts
+++ b/packages/graphql-mesh/utils/swaggers/index.ts
@@ -1,4 +1,5 @@
import { Spec, ConfigExtension, Resolvers } from '../../types'
+import { getSourceName } from '../config'
import { trimLinks, anonymizePathAndGetParams } from '../helpers'
/**
* This function creates, for a Swagger file, the additional typeDefs for each schema having at least one x-link, and one resolver for each x-link
@@ -10,7 +11,8 @@ export const generateTypeDefsAndResolversFromSwagger = (
spec: Spec,
availableTypes: string[],
interfacesWithChildren: { [key: string]: string[] },
- catalog: { [key: string]: [string, string, string] }
+ catalog: { [key: string]: [string, string, string] },
+ config: any
): ConfigExtension => {
if (!spec.components) {
return {
@@ -30,7 +32,15 @@ export const generateTypeDefsAndResolversFromSwagger = (
}
}
+ const linkItemTypeDef = /* GraphQL */ `
+ type LinkItem {
+ rel: String
+ href: String
+ }
+ `
+
let typeDefs = ''
+ typeDefs += linkItemTypeDef
const resolvers: Resolvers = {}
@@ -85,7 +95,7 @@ export const generateTypeDefsAndResolversFromSwagger = (
const query = targetedOperationName
const type = targetedOperationType
- const source = targetedSwaggerName
+ const source = getSourceName(targetedSwaggerName, config)
if (
targetedOperationType !== 'TYPE_NOT_FOUND' &&
@@ -118,7 +128,8 @@ export const generateTypeDefsAndResolversFromSwagger = (
if (paramsToSend.length) {
paramsToSend.forEach((param, i) => {
- args[param] = root[param] || root[paramsFromLink[i]] || ''
+ // To avoid params validation error in case of missing params or type mismatch we set default value to '0'
+ args[param] = root[param] || root[paramsFromLink[i]] || '0'
})
}
diff --git a/test/integration/camouflage.yaml b/test/integration/camouflage.yaml
new file mode 100644
index 0000000..84df4c0
--- /dev/null
+++ b/test/integration/camouflage.yaml
@@ -0,0 +1,65 @@
+loglevel: info
+cpus: 1
+monitoring:
+ port: 5555
+ssl:
+ cert: "./certs/server.cert"
+ key: "./certs/server.key"
+ root_cert: "./certs/root.cert"
+protocols:
+ http:
+ enable: true
+ mocks_dir: "./mocks"
+ port: 8080
+ https:
+ enable: false
+ port: 8443
+ http2:
+ enable: false
+ port: 8081
+ ws:
+ enable: false
+ mocks_dir: "./ws_mocks"
+ port: 8082
+ grpc:
+ enable: false
+ host: 0.0.0.0
+ port: 4312
+ mocks_dir: "./grpc/mocks"
+ protos_dir: "./grpc/protos"
+ grpc_tls: false
+ thrift:
+ enable: false
+ mocks_dir: "./thrift/mocks"
+ services:
+ - port: 9999
+ service: "/opt/gen-nodejs/Calculator"
+ handlers:
+ - add
+ - substract
+ - ping
+backup:
+ enable: false
+ cron: "0 * * * *" # Hourly Backup
+cache:
+ enable: false
+ ttl_seconds: 300
+injection:
+ enable: true
+origins:
+ - http://localhost:3000
+ - http://localhost:3001
+ - http://localhost:5000
+validation:
+ enable: true
+ schemas:
+ - type: OpenApi
+ url: https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json
+# ext_helpers: "./custom_handlebar.json"
+# ext_data_source:
+# pg:
+# host: localhost
+# port: 5432
+# user: root
+# password: password
+# database: postgres
diff --git a/test/integration/compose.yaml b/test/integration/compose.yaml
new file mode 100644
index 0000000..ac9c740
--- /dev/null
+++ b/test/integration/compose.yaml
@@ -0,0 +1,35 @@
+services:
+ #
+ api-products:
+ image: shubhendumadhukar/camouflage
+ ports:
+ - 45537:8080
+ volumes:
+ - ./camouflage.yaml:/app/config.yml:ro
+ - ./mocks:/app/mocks:ro
+
+ healthcheck:
+ # https://stackoverflow.com/questions/72708667/use-wget-instead-of-curl-for-healthchecks-in-asp-net-core-docker-images
+ test: wget --spider --tries=1 --no-verbose http://localhost:8080/products || exit 1
+ interval: 10s
+ timeout: 10s
+ retries: 3
+ restart: unless-stopped
+
+ #
+ graphql-mesh:
+ depends_on:
+ api-products:
+ condition: service_healthy
+ links:
+ - api-products
+ image: ${IMAGE_TAG:-graphql-mesh}
+ environment:
+ - DEBUG=1
+ ports:
+ - 45538:3000
+ volumes:
+ - ./transforms:/app/transforms:ro
+ - ./plugins:/app/plugins:ro
+ - ./config.yaml:/app/config.yaml:ro
+ restart: unless-stopped
diff --git a/test/integration/config.yaml b/test/integration/config.yaml
new file mode 100644
index 0000000..9bb03b7
--- /dev/null
+++ b/test/integration/config.yaml
@@ -0,0 +1,33 @@
+sources:
+ - name: Products
+ handler:
+ openapi:
+ source: http://api-products:8080/api-docs/products
+ endpoint: http://api-products:8080
+ - name: Suppliers
+ handler:
+ openapi:
+ source: http://api-products:8080/api-docs/suppliers
+ endpoint: http://api-products:8080
+
+ - name: Authentication
+ handler:
+ openapi:
+ source: http://api-products:8080/api-docs/authenticate
+ endpoint: http://api-products:8080
+
+additionalEnvelopPlugins: "./plugins"
+additionalTransforms: [{ "./transforms/index.ts": {} }]
+additionalTypeDefs: |
+ """
+ This directive is used to convert the result to uppercase.
+ """
+ directive @lower on FIELD
+skipSSLValidation: true
+serve:
+ hostname: 0.0.0.0
+ port: 3000
+ cors:
+ origin: "*"
+ playground: true
+ playgroundTitle: Console GraphQL
diff --git a/test/integration/mocks/api-docs/authenticate.yml b/test/integration/mocks/api-docs/authenticate.yml
new file mode 100644
index 0000000..4198109
--- /dev/null
+++ b/test/integration/mocks/api-docs/authenticate.yml
@@ -0,0 +1,20 @@
+openapi: 3.0.0
+info:
+ title: Authentication API
+ version: 1.0.0
+paths:
+ /authenticate:
+ post:
+ summary: Authenticate user
+ operationId: isAuthenticated
+ responses:
+ '200':
+ description: Authentication successful
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ authenticate:
+ type: boolean
+ description: Yes or No based on authentication header
diff --git a/test/integration/mocks/api-docs/authenticate/GET.mock b/test/integration/mocks/api-docs/authenticate/GET.mock
new file mode 100644
index 0000000..a7adc8e
--- /dev/null
+++ b/test/integration/mocks/api-docs/authenticate/GET.mock
@@ -0,0 +1,36 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Authentication API",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/authenticate": {
+ "post": {
+ "summary": "Authenticate user",
+ "operationId": "isAuthenticated",
+ "responses": {
+ "200": {
+ "description": "Authentication successful",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "authenticate": {
+ "type": "boolean",
+ "description": "Yes or No based on authentication header"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/integration/mocks/api-docs/products.yml b/test/integration/mocks/api-docs/products.yml
new file mode 100644
index 0000000..1b8f5a4
--- /dev/null
+++ b/test/integration/mocks/api-docs/products.yml
@@ -0,0 +1,82 @@
+openapi: 3.0.0
+info:
+ title: Products API
+ description: API to manage products with HATEOAS.
+ version: "1.0"
+servers:
+ - url: http://localhost:3000/
+paths:
+ /products:
+ get:
+ operationId: getProducts
+ summary: List all products
+ responses:
+ "200":
+ description: A list of products with HATEOAS links
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Products"
+ /products/{id}:
+ get:
+ operationId: getProductById
+ summary: Get a product by ID
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ type: integer
+ description: The product ID
+ responses:
+ "200":
+ description: A list of products with HATEOAS links
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Product"
+components:
+ schemas:
+ Product:
+ type: object
+ properties:
+ _links:
+ $ref: "#/components/schemas/ProductLinks"
+ id:
+ type: integer
+ name:
+ type: string
+ price:
+ type: number
+ supplierId:
+ type: integer
+ ProductLinks:
+ type: object
+ required:
+ - self
+ - supplier
+ properties:
+ self:
+ $ref: "#/components/schemas/Link"
+ supplier:
+ $ref: "#/components/schemas/Link"
+ x-links:
+ - rel: self
+ hrefPattern: "/products/{id}"
+ - rel: supplier
+ hrefPattern: "/suppliers/{id}"
+
+ Products:
+ type: object
+ properties:
+ items:
+ type: array
+ items:
+ $ref: "#/components/schemas/Product"
+ Link:
+ type: object
+ required:
+ - href
+ properties:
+ href:
+ type: string
diff --git a/test/integration/mocks/api-docs/products/GET.mock b/test/integration/mocks/api-docs/products/GET.mock
new file mode 100644
index 0000000..8648768
--- /dev/null
+++ b/test/integration/mocks/api-docs/products/GET.mock
@@ -0,0 +1,136 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Products API",
+ "description": "API to manage products with HATEOAS.",
+ "version": "1.0"
+ },
+ "servers": [
+ {
+ "url": "http://localhost:3000/"
+ }
+ ],
+ "paths": {
+ "/products": {
+ "get": {
+ "operationId": "getProducts",
+ "summary": "List all products",
+ "responses": {
+ "200": {
+ "description": "A list of products with HATEOAS links",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Products"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/products/{id}": {
+ "get": {
+ "operationId": "getProductById",
+ "summary": "Get a product by ID",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ },
+ "description": "The product ID"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A list of products with HATEOAS links",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Product"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Product": {
+ "type": "object",
+ "properties": {
+ "_links": {
+ "$ref": "#/components/schemas/ProductLinks"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "price": {
+ "type": "number"
+ },
+ "supplierId": {
+ "type": "integer"
+ }
+ }
+ },
+ "ProductLinks": {
+ "type": "object",
+ "required": [
+ "self",
+ "supplier"
+ ],
+ "properties": {
+ "self": {
+ "$ref": "#/components/schemas/Link"
+ },
+ "supplier": {
+ "$ref": "#/components/schemas/Link"
+ }
+ },
+ "x-links": [
+ {
+ "rel": "self",
+ "hrefPattern": "/products/{id}"
+ },
+ {
+ "rel": "supplier",
+ "hrefPattern": "/suppliers/{id}"
+ }
+ ]
+ },
+ "Products": {
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Product"
+ }
+ }
+ }
+ },
+ "Link": {
+ "type": "object",
+ "required": [
+ "href"
+ ],
+ "properties": {
+ "href": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/integration/mocks/api-docs/suppliers.yml b/test/integration/mocks/api-docs/suppliers.yml
new file mode 100644
index 0000000..7bfefa0
--- /dev/null
+++ b/test/integration/mocks/api-docs/suppliers.yml
@@ -0,0 +1,60 @@
+openapi: 3.0.0
+info:
+ title: Suppliers API
+ description: API to manage suppliers with HATEOAS.
+ version: "1.0"
+servers:
+ - url: http://localhost:3000/
+paths:
+ /suppliers:
+ get:
+ operationId: getSuppliers
+ summary: List all suppliers
+ responses:
+ "200":
+ description: A list of suppliers with HATEOAS links
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Suppliers"
+ /suppliers/{id}:
+ get:
+ operationId: getSupplierById
+ summary: Get a supplier by ID
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ type: integer
+ description: The supplier ID
+ responses:
+ "200":
+ description: A list of suppliers with HATEOAS links
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Supplier"
+components:
+ schemas:
+ Supplier:
+ type: object
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ Link:
+ type: object
+ required:
+ - href
+ properties:
+ href:
+ type: string
+ Suppliers:
+ type: object
+ properties:
+ items:
+ type: array
+ items:
+ $ref: "#/components/schemas/Supplier"
diff --git a/test/integration/mocks/api-docs/suppliers/GET.mock b/test/integration/mocks/api-docs/suppliers/GET.mock
new file mode 100644
index 0000000..cdbdd65
--- /dev/null
+++ b/test/integration/mocks/api-docs/suppliers/GET.mock
@@ -0,0 +1,102 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Suppliers API",
+ "description": "API to manage suppliers with HATEOAS.",
+ "version": "1.0"
+ },
+ "servers": [
+ {
+ "url": "http://localhost:3000/"
+ }
+ ],
+ "paths": {
+ "/suppliers": {
+ "get": {
+ "operationId": "getSuppliers",
+ "summary": "List all suppliers",
+ "responses": {
+ "200": {
+ "description": "A list of suppliers with HATEOAS links",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Suppliers"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/suppliers/{id}": {
+ "get": {
+ "operationId": "getSupplierById",
+ "summary": "Get a supplier by ID",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ },
+ "description": "The supplier ID"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A list of suppliers with HATEOAS links",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Supplier"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Supplier": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "Link": {
+ "type": "object",
+ "required": [
+ "href"
+ ],
+ "properties": {
+ "href": {
+ "type": "string"
+ }
+ }
+ },
+ "Suppliers": {
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Supplier"
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/integration/mocks/authenticate/POST.mock b/test/integration/mocks/authenticate/POST.mock
new file mode 100644
index 0000000..deb6879
--- /dev/null
+++ b/test/integration/mocks/authenticate/POST.mock
@@ -0,0 +1,16 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{{#code}}
+((request) => {
+ // logger.info(JSON.stringify(request.headers))
+ console.log(request.headers['authorization'])
+ const supplierId = 1
+ return {
+ status: 200,
+ body: JSON.stringify({
+ "authenticate": request.headers['authorization'] === "Bearer token"
+ }, null, 2)
+ }
+})(request);
+{{/code}}
diff --git a/test/integration/mocks/products/GET.mock b/test/integration/mocks/products/GET.mock
new file mode 100644
index 0000000..1c317f8
--- /dev/null
+++ b/test/integration/mocks/products/GET.mock
@@ -0,0 +1,32 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{{#code}}
+(() => {
+ const products = []
+ for (let i = 1; i <= 50; i++) {
+ const supplierId = (i % 10) + 1
+ products.push({
+ id: i,
+ name: `Product ${i}`,
+ price: Math.floor(Math.random() * 100),
+ supplierId,
+ _links: {
+ self: {
+ href: `/products/${i}`
+ },
+ supplier: {
+ href: `/suppliers/${supplierId}`
+ }
+ }
+ })
+ }
+
+ return {
+ status: 200,
+ body: JSON.stringify({
+ "items": products
+ }, null, 2)
+ }
+})();
+{{/code}}
diff --git a/test/integration/mocks/products/__/GET.mock b/test/integration/mocks/products/__/GET.mock
new file mode 100644
index 0000000..dec062d
--- /dev/null
+++ b/test/integration/mocks/products/__/GET.mock
@@ -0,0 +1,27 @@
+{{assign name='id' value=(capture from='path' regex='\/products\/(.+)?') }}
+
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{{#code}}
+((id) => {
+ const supplierId = (parseInt(id) % 10) + 1
+ return {
+ status: 200,
+ body: JSON.stringify({
+ "id": 1,
+ "name": `Product ${id}`,
+ "price": `${Math.floor(Math.random() * 100)}`,
+ "supplierId": `${supplierId}`,
+ "_links": {
+ "self": {
+ "href": `/products/${id}`
+ },
+ "supplier": {
+ "href": `/suppliers/${supplierId}`
+ }
+ }
+ }, null, 2)
+ }
+})(request.params[0].split('/').reverse()[0]);
+{{/code}}
diff --git a/test/integration/mocks/suppliers/GET.mock b/test/integration/mocks/suppliers/GET.mock
new file mode 100644
index 0000000..56e6033
--- /dev/null
+++ b/test/integration/mocks/suppliers/GET.mock
@@ -0,0 +1,21 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{{#code}}
+(() => {
+ const suppliers = []
+ for (let i = 1; i <= 10; i++) {
+ suppliers.push({
+ id: i,
+ name: `Supplier ${i}`
+ })
+ }
+
+ return {
+ status: 200,
+ body: JSON.stringify({
+ "items": suppliers
+ }, null, 2)
+ }
+})();
+{{/code}}
diff --git a/test/integration/mocks/suppliers/__/GET.mock b/test/integration/mocks/suppliers/__/GET.mock
new file mode 100644
index 0000000..d6afe99
--- /dev/null
+++ b/test/integration/mocks/suppliers/__/GET.mock
@@ -0,0 +1,9 @@
+{{assign name='id' value=(capture from='path' regex='\/suppliers\/(.+)?') }}
+
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "id": "{{id}}",
+ "name": "Supplier {{id}}"
+}
diff --git a/test/integration/plugins/index.ts b/test/integration/plugins/index.ts
new file mode 100644
index 0000000..829007d
--- /dev/null
+++ b/test/integration/plugins/index.ts
@@ -0,0 +1,45 @@
+import { type Plugin } from '@envelop/core';
+
+/**
+ * This plugin auto-populates the Server-Timing header to the response.
+ *
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing
+ */
+export default () => {
+ return {
+ onFetch({ context, info }) {
+ if (!info) {
+ return;
+ }
+
+ const start = Date.now();
+
+ return () => {
+ const duration = Date.now() - start;
+ const timing = `${info.fieldName};desc="${info.fieldName} (${info.sourceName})";dur=${duration}`;
+ if (!context.timings) {
+ context.timings = [];
+ }
+ context.timings.push(timing);
+ };
+ },
+
+ onExecute() {
+ return {
+ onExecuteDone({ args }) {
+ // @ts-ignore
+ const { timings } = args.contextValue;
+ if (!timings) {
+ return;
+ }
+
+ // @ts-ignore
+ args.contextValue.res.setHeader?.(
+ 'Server-Timing',
+ timings.join(', ')
+ );
+ },
+ };
+ },
+ };
+};
diff --git a/test/integration/tests/.gitignore b/test/integration/tests/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/test/integration/tests/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/test/integration/tests/cases/directive-headers.test.ts b/test/integration/tests/cases/directive-headers.test.ts
new file mode 100644
index 0000000..d3e6894
--- /dev/null
+++ b/test/integration/tests/cases/directive-headers.test.ts
@@ -0,0 +1,21 @@
+import { test, expect } from 'vitest'
+import axios from 'axios'
+import { url, headers } from '../config'
+
+/* SPL filter */
+const isAuthenticatedMutation = /* GraphQL */ `
+ mutation isAuthenticated {
+ isAuthenticated @headers(input: [{ key: "Authorization", value: "Bearer token" }]) {
+ authenticate
+ }
+ }
+`
+
+test('isAuthenticatedMutation: test directive @headers work properly', async () => {
+ const response = await axios.post(url, { query: isAuthenticatedMutation }, { headers })
+
+ const result = response.data
+ expect(response.status).toBe(200)
+ expect(result.errors).toBeUndefined()
+ expect(result.data.isAuthenticated.authenticate).toBe(true)
+})
diff --git a/test/integration/tests/cases/directive-no-auth.test.ts b/test/integration/tests/cases/directive-no-auth.test.ts
new file mode 100644
index 0000000..fd9b59e
--- /dev/null
+++ b/test/integration/tests/cases/directive-no-auth.test.ts
@@ -0,0 +1,26 @@
+import { test, expect } from 'vitest'
+import axios from 'axios'
+import { url, headers } from '../config'
+
+/* SPL filter */
+const isAuthenticatedMutation = /* GraphQL */ `
+ mutation isAuthenticated {
+ isAuthenticated @noAuth {
+ authenticate
+ }
+ }
+`
+
+// Add Authorization header
+const _headers = {
+ ...headers,
+ Authorization: 'Bearer token'
+}
+test('isAuthenticatedMutation: test directive @noAuth properly remove authorization header', async () => {
+ const response = await axios.post(url, { query: isAuthenticatedMutation }, { headers: _headers })
+
+ const result = response.data
+ expect(response.status).toBe(200)
+ expect(result.errors).toBeUndefined()
+ expect(result.data.isAuthenticated.authenticate).toBe(false)
+})
diff --git a/test/integration/tests/cases/directive-spl.test.ts b/test/integration/tests/cases/directive-spl.test.ts
new file mode 100644
index 0000000..c6c62b2
--- /dev/null
+++ b/test/integration/tests/cases/directive-spl.test.ts
@@ -0,0 +1,26 @@
+import { test, expect } from 'vitest'
+import axios from 'axios'
+import { url, headers } from '../config'
+
+/* SPL filter */
+const getTenFirstProductsQuery = /* GraphQL */ `
+ query getTenFirstProducts {
+ getProducts {
+ items @SPL(query: "id <= 10") {
+ name
+ id
+ price
+ supplierId
+ }
+ }
+ }
+`
+
+test('getTenFirstProductsQuery: test SPL filter inside query', async () => {
+ const response = await axios.post(url, { query: getTenFirstProductsQuery }, { headers })
+
+ const result = response.data
+ expect(response.status).toBe(200)
+ expect(result.errors).toBeUndefined()
+ expect(result.data.getProducts.items.length).toEqual(10)
+})
diff --git a/test/integration/tests/cases/graphql-mesh.test.ts b/test/integration/tests/cases/graphql-mesh.test.ts
new file mode 100644
index 0000000..3e475be
--- /dev/null
+++ b/test/integration/tests/cases/graphql-mesh.test.ts
@@ -0,0 +1,83 @@
+import { test, expect } from 'vitest'
+import axios from 'axios'
+import { url, headers } from '../config'
+
+/* Get all products */
+const getAllProductsQuery = /* GraphQL */ `
+ query getAllProducts {
+ getProducts {
+ items {
+ name
+ id
+ price
+ supplierId
+ }
+ }
+ }
+`
+
+test('getAllProductsQuery: query working properly', async () => {
+ const response = await axios.post(url, { query: getAllProductsQuery }, { headers })
+
+ const result = response.data
+ expect(response.status).toBe(200)
+ expect(result).toHaveProperty('data')
+ expect(result.errors).toBeUndefined()
+ expect(result.data).toHaveProperty('getProducts')
+ expect(result.data.getProducts.items.length).toEqual(50)
+})
+
+/* Hateoas link */
+const getProductAndSupplierInfo = /* GraphQL */ `
+ query getProductAndSupplierInfo {
+ getProductById(id: 1) {
+ id
+ name
+ price
+ supplier {
+ name
+ id
+ }
+ }
+ }
+`
+
+test('getProductAndSupplierInfo: follow hateoas link', async () => {
+ const response = await axios.post(url, { query: getProductAndSupplierInfo }, { headers })
+
+ const result = response.data
+ expect(result.errors).toBeUndefined()
+ expect(result.data.getProductById.supplier.name).contains('Supplier 2')
+})
+
+/* Linklist property */
+
+const getProductwithLinkList = /* GraphQL */ `
+ query getProductWithLinkList {
+ getProductById(id: 1) {
+ supplierId
+ _linksList {
+ rel
+ href
+ }
+ }
+ }
+`
+
+test('getProductAndSupplierInfo: Get "_linksList" attributes', async () => {
+ const response = await axios.post(url, { query: getProductwithLinkList }, { headers })
+
+ const result = response.data
+ expect(result.errors).toBeUndefined()
+ expect(result.data.getProductById._linksList.length).toEqual(2)
+ expect(result.data.getProductById._linksList).toEqual([
+ {
+ rel: 'self',
+ href: '/products/1'
+ },
+ {
+ rel: 'supplier',
+ href: '/suppliers/2'
+ }
+ ])
+})
diff --git a/test/integration/tests/cases/inject-additionnal-transforms.test.ts b/test/integration/tests/cases/inject-additionnal-transforms.test.ts
new file mode 100644
index 0000000..0617130
--- /dev/null
+++ b/test/integration/tests/cases/inject-additionnal-transforms.test.ts
@@ -0,0 +1,23 @@
+import { test, expect } from 'vitest'
+import axios from 'axios'
+import { url, headers } from '../config'
+
+/* Get all products */
+const getProductById = /* GraphQL */ `
+ query getProduct {
+ getProductById(id: 1) {
+ name @lower
+ price
+ supplierId
+ }
+ }
+`
+
+test('getProductById: @lower directive added throw transform', async () => {
+ const response = await axios.post(url, { query: getProductById }, { headers })
+
+ const result = response.data
+ expect(result.errors).toBeUndefined()
+ expect(result).toHaveProperty('data')
+ expect(result.data.getProductById.name).toEqual('product 1')
+})
diff --git a/test/integration/tests/cases/plugins.test.ts b/test/integration/tests/cases/plugins.test.ts
new file mode 100644
index 0000000..f2a756a
--- /dev/null
+++ b/test/integration/tests/cases/plugins.test.ts
@@ -0,0 +1,22 @@
+import { test, expect } from 'vitest'
+import axios from 'axios'
+import { url, headers } from '../config'
+
+/* Get all products */
+const getProductById = /* GraphQL */ `
+ query getProduct {
+ getProductById(id: 1) {
+ name
+ price
+ supplierId
+ }
+ }
+`
+
+test('getProductById: Server timing plugin', async () => {
+ const response = await axios.post(url, { query: getProductById }, { headers })
+
+ expect(response.data.errors).toBeUndefined()
+ const serverTiming = response.headers['server-timing']
+ expect(serverTiming).contains('getProductById;desc="getProductById (Products)";dur')
+})
diff --git a/test/integration/tests/config.ts b/test/integration/tests/config.ts
new file mode 100644
index 0000000..48a36bc
--- /dev/null
+++ b/test/integration/tests/config.ts
@@ -0,0 +1,3 @@
+export const url = 'http://0.0.0.0:45538/graphql'
+// export const url = 'http://0.0.0.0:4000/graphql'
+export const headers = { 'Content-Type': 'application/json' }
diff --git a/test/integration/tests/package-lock.json b/test/integration/tests/package-lock.json
new file mode 100644
index 0000000..3957168
--- /dev/null
+++ b/test/integration/tests/package-lock.json
@@ -0,0 +1,1515 @@
+{
+ "name": "tests",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "tests",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^1.6.8",
+ "vitest": "^1.5.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+ "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+ "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+ "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+ "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+ "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+ "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+ "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+ "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+ "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+ "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+ "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+ "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+ "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+ "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+ "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+ "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+ "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+ "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+ "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+ "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jest/schemas": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+ "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+ "dependencies": {
+ "@sinclair/typebox": "^0.27.8"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz",
+ "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz",
+ "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz",
+ "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz",
+ "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz",
+ "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz",
+ "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz",
+ "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz",
+ "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz",
+ "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz",
+ "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz",
+ "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz",
+ "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz",
+ "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz",
+ "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz",
+ "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@sinclair/typebox": {
+ "version": "0.27.8",
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+ },
+ "node_modules/@vitest/expect": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz",
+ "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==",
+ "dependencies": {
+ "@vitest/spy": "1.5.0",
+ "@vitest/utils": "1.5.0",
+ "chai": "^4.3.10"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz",
+ "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==",
+ "dependencies": {
+ "@vitest/utils": "1.5.0",
+ "p-limit": "^5.0.0",
+ "pathe": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz",
+ "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==",
+ "dependencies": {
+ "magic-string": "^0.30.5",
+ "pathe": "^1.1.1",
+ "pretty-format": "^29.7.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz",
+ "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==",
+ "dependencies": {
+ "tinyspy": "^2.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz",
+ "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==",
+ "dependencies": {
+ "diff-sequences": "^29.6.3",
+ "estree-walker": "^3.0.3",
+ "loupe": "^2.3.7",
+ "pretty-format": "^29.7.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "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-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/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chai": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz",
+ "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==",
+ "dependencies": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.3",
+ "deep-eql": "^4.1.3",
+ "get-func-name": "^2.0.2",
+ "loupe": "^2.3.6",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/check-error": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+ "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
+ "dependencies": {
+ "get-func-name": "^2.0.2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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==",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/diff-sequences": {
+ "version": "29.6.3",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+ "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
+ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.20.2",
+ "@esbuild/android-arm": "0.20.2",
+ "@esbuild/android-arm64": "0.20.2",
+ "@esbuild/android-x64": "0.20.2",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@esbuild/freebsd-arm64": "0.20.2",
+ "@esbuild/freebsd-x64": "0.20.2",
+ "@esbuild/linux-arm": "0.20.2",
+ "@esbuild/linux-arm64": "0.20.2",
+ "@esbuild/linux-ia32": "0.20.2",
+ "@esbuild/linux-loong64": "0.20.2",
+ "@esbuild/linux-mips64el": "0.20.2",
+ "@esbuild/linux-ppc64": "0.20.2",
+ "@esbuild/linux-riscv64": "0.20.2",
+ "@esbuild/linux-s390x": "0.20.2",
+ "@esbuild/linux-x64": "0.20.2",
+ "@esbuild/netbsd-x64": "0.20.2",
+ "@esbuild/openbsd-x64": "0.20.2",
+ "@esbuild/sunos-x64": "0.20.2",
+ "@esbuild/win32-arm64": "0.20.2",
+ "@esbuild/win32-ia32": "0.20.2",
+ "@esbuild/win32-x64": "0.20.2"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-func-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/js-tokens": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
+ "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ=="
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA=="
+ },
+ "node_modules/local-pkg": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",
+ "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
+ "dependencies": {
+ "mlly": "^1.4.2",
+ "pkg-types": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/loupe": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
+ "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
+ "dependencies": {
+ "get-func-name": "^2.0.1"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.9",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
+ "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mlly": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz",
+ "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==",
+ "dependencies": {
+ "acorn": "^8.11.3",
+ "pathe": "^1.1.2",
+ "pkg-types": "^1.0.3",
+ "ufo": "^1.3.2"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz",
+ "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==",
+ "dependencies": {
+ "yocto-queue": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
+ },
+ "node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/pkg-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz",
+ "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+ "dependencies": {
+ "jsonc-parser": "^3.2.0",
+ "mlly": "^1.2.0",
+ "pathe": "^1.1.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/pretty-format": {
+ "version": "29.7.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+ "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+ "dependencies": {
+ "@jest/schemas": "^29.6.3",
+ "ansi-styles": "^5.0.0",
+ "react-is": "^18.0.0"
+ },
+ "engines": {
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "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=="
+ },
+ "node_modules/rollup": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz",
+ "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==",
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.14.2",
+ "@rollup/rollup-android-arm64": "4.14.2",
+ "@rollup/rollup-darwin-arm64": "4.14.2",
+ "@rollup/rollup-darwin-x64": "4.14.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.14.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.14.2",
+ "@rollup/rollup-linux-arm64-musl": "4.14.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.14.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.14.2",
+ "@rollup/rollup-linux-x64-gnu": "4.14.2",
+ "@rollup/rollup-linux-x64-musl": "4.14.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.14.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.14.2",
+ "@rollup/rollup-win32-x64-msvc": "4.14.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+ "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="
+ },
+ "node_modules/std-env": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
+ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg=="
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz",
+ "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==",
+ "dependencies": {
+ "js-tokens": "^9.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.7.0.tgz",
+ "integrity": "sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA=="
+ },
+ "node_modules/tinypool": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz",
+ "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz",
+ "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz",
+ "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw=="
+ },
+ "node_modules/vite": {
+ "version": "5.2.8",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
+ "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
+ "dependencies": {
+ "esbuild": "^0.20.1",
+ "postcss": "^8.4.38",
+ "rollup": "^4.13.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-node": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz",
+ "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.4",
+ "pathe": "^1.1.1",
+ "picocolors": "^1.0.0",
+ "vite": "^5.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz",
+ "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==",
+ "dependencies": {
+ "@vitest/expect": "1.5.0",
+ "@vitest/runner": "1.5.0",
+ "@vitest/snapshot": "1.5.0",
+ "@vitest/spy": "1.5.0",
+ "@vitest/utils": "1.5.0",
+ "acorn-walk": "^8.3.2",
+ "chai": "^4.3.10",
+ "debug": "^4.3.4",
+ "execa": "^8.0.1",
+ "local-pkg": "^0.5.0",
+ "magic-string": "^0.30.5",
+ "pathe": "^1.1.1",
+ "picocolors": "^1.0.0",
+ "std-env": "^3.5.0",
+ "strip-literal": "^2.0.0",
+ "tinybench": "^2.5.1",
+ "tinypool": "^0.8.3",
+ "vite": "^5.0.0",
+ "vite-node": "1.5.0",
+ "why-is-node-running": "^2.2.2"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "@vitest/browser": "1.5.0",
+ "@vitest/ui": "1.5.0",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
+ "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
+ "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/test/integration/tests/package.json b/test/integration/tests/package.json
new file mode 100644
index 0000000..702a1e7
--- /dev/null
+++ b/test/integration/tests/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "tests",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "vitest"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^1.6.8",
+ "vitest": "^1.5.0"
+ }
+}
diff --git a/test/integration/tests/vite.config.ts b/test/integration/tests/vite.config.ts
new file mode 100644
index 0000000..0324084
--- /dev/null
+++ b/test/integration/tests/vite.config.ts
@@ -0,0 +1,4 @@
+///
+// Configure Vitest (https://vitest.dev/config/)
+import { defineConfig } from 'vite'
+export default defineConfig({})
diff --git a/test/integration/transforms/index.ts b/test/integration/transforms/index.ts
new file mode 100644
index 0000000..a4acf47
--- /dev/null
+++ b/test/integration/transforms/index.ts
@@ -0,0 +1,51 @@
+import { defaultFieldResolver, GraphQLSchema } from 'graphql'
+import { MeshTransform } from '@graphql-mesh/types'
+import { MapperKind, mapSchema } from '@graphql-tools/utils'
+
+export default class LowerDirectiveTransform implements MeshTransform {
+ noWrap = true
+
+ transformSchema(schema: GraphQLSchema) {
+ return mapSchema(schema, {
+ [MapperKind.OBJECT_FIELD]: (fieldConfig) => {
+ const originalResolver =
+ fieldConfig.resolve != null ? fieldConfig.resolve : defaultFieldResolver
+
+ const resolver = async (next: any, _source: any, _args: any, context: any, info: any) => {
+ const { directives } = info.fieldNodes[0]
+ const upperDirective = directives.find(
+ (directive: { name: { value: string } }) => directive.name.value === 'lower'
+ )
+
+ let result = await next(context)
+
+ if (upperDirective) {
+ if (typeof result === 'string') {
+ result = result.toLowerCase()
+ }
+ }
+
+ return result
+ }
+
+ fieldConfig.resolve = (source, originalArgs, context, info) => {
+ return resolver(
+ (context: unknown) =>
+ new Promise((resolve, reject) => {
+ const result = originalResolver(source, originalArgs, context, info)
+ if (result instanceof Error) {
+ reject(result)
+ }
+ resolve(result)
+ }),
+ source,
+ originalArgs,
+ context,
+ info
+ )
+ }
+ return fieldConfig
+ }
+ })
+ }
+}