diff --git a/influxdb/README.md b/influxdb/README.md new file mode 100644 index 00000000..90bbfa09 --- /dev/null +++ b/influxdb/README.md @@ -0,0 +1,28 @@ +# InfluxDB Plugin + +Plugin for InfluxDB datasource support in Perses. + +## Features + +- InfluxDB v1.x datasource support +- InfluxDB v3 datasource support +- Time series query support + +## Installation + +```bash +npm install @perses-dev/influxdb-plugin +``` + +## Development + +```bash +npm run dev +``` + +## Build + +```bash +npm run build +``` + diff --git a/influxdb/cue.mod/module.cue b/influxdb/cue.mod/module.cue new file mode 100644 index 00000000..aa84626a --- /dev/null +++ b/influxdb/cue.mod/module.cue @@ -0,0 +1,13 @@ +module: "github.com/perses/plugins/influxdb@v0" +language: { + version: "v0.9.0-alpha.0" +} +deps: { + "github.com/perses/perses/cue@v0": { + v: "v0.53.0-rc.0" + default: true + } + "github.com/perses/shared/cue@v0": { + v: "v0.53.0-rc.1" + } +} diff --git a/influxdb/go.mod b/influxdb/go.mod new file mode 100644 index 00000000..dd29e29c --- /dev/null +++ b/influxdb/go.mod @@ -0,0 +1,36 @@ +module github.com/perses/plugins/influxdb + +go 1.25.5 + +require ( + github.com/perses/perses v0.53.0-rc.0 + github.com/stretchr/testify v1.11.1 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/muhlemmer/gu v0.3.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.17.0 // indirect + github.com/zitadel/oidc/v3 v3.45.1 // indirect + github.com/zitadel/schema v1.3.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/influxdb/go.sum b/influxdb/go.sum new file mode 100644 index 00000000..5eaf5d73 --- /dev/null +++ b/influxdb/go.sum @@ -0,0 +1,68 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= +github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nexucis/lamenv v0.5.2 h1:tK/u3XGhCq9qIoVNcXsK9LZb8fKopm0A5weqSRvHd7M= +github.com/nexucis/lamenv v0.5.2/go.mod h1:HusJm6ltmmT7FMG8A750mOLuME6SHCsr2iFYxp5fFi0= +github.com/perses/perses v0.53.0-rc.0 h1:f3V1j6EqnKyXUY0mNt4Zp/T6+5U/5SjtCzLHxj9sJDQ= +github.com/perses/perses v0.53.0-rc.0/go.mod h1:q+gB4M2yT//cO6GlCjhOTJLDoSrqtkMLul72Z0WOueI= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/zitadel/oidc/v3 v3.45.1 h1:x7J8NywTUtLR9T5uu2dufae3gJrl6VVpIfvGZy+kzJg= +github.com/zitadel/oidc/v3 v3.45.1/go.mod h1:oFArtAPTXEA4ajkIe/JfBjv7hhlD0kr///UqaO3Uzd0= +github.com/zitadel/schema v1.3.1 h1:QT3kwiRIRXXLVAs6gCK/u044WmUVh6IlbLXUsn6yRQU= +github.com/zitadel/schema v1.3.1/go.mod h1:071u7D2LQacy1HAN+YnMd/mx1qVE2isb0Mjeqg46xnU= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/influxdb/jest.config.ts b/influxdb/jest.config.ts new file mode 100644 index 00000000..393bcee6 --- /dev/null +++ b/influxdb/jest.config.ts @@ -0,0 +1,22 @@ +const path = require('path'); +const fs = require('fs'); + +// Read and parse the shared jest config inline to avoid module resolution issues +const swcrcPath = path.join(__dirname, '..', '.cjs.swcrc'); +const swcrc = JSON.parse(fs.readFileSync(swcrcPath, 'utf-8')); + +module.exports = { + testEnvironment: 'jsdom', + roots: ['/src'], + moduleNameMapper: { + '^echarts/(.*)$': 'echarts', + '^use-resize-observer$': 'use-resize-observer/polyfilled', + '\\.(css|less)$': '/../stylesMock.js', + }, + transformIgnorePatterns: ['node_modules/(?!(lodash-es|yaml))'], + transform: { + '^.+\\.(ts|tsx|js|jsx)$': ['@swc/jest', { ...swcrc, exclude: [], swcrc: false }], + }, + setupFilesAfterEnv: ['/src/setup-tests.ts'], +}; + diff --git a/influxdb/package-lock.json b/influxdb/package-lock.json new file mode 100644 index 00000000..7d8bdeb9 --- /dev/null +++ b/influxdb/package-lock.json @@ -0,0 +1,6820 @@ +{ + "name": "@perses-dev/influxdb-plugin", + "version": "0.1.0-rc.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@perses-dev/influxdb-plugin", + "version": "0.1.0-rc.0", + "devDependencies": { + "@swc/cli": "^0.4.1-nightly.20240914", + "@swc/core": "^1.7.28", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^12.1.4", + "@testing-library/user-event": "^14.5.2", + "@types/color-hash": "^2.0.0", + "@types/jest": "^29.5.14", + "@types/lodash": "^4.17.13", + "@types/react": "^18.3.12", + "concurrently": "^8.2.2", + "cross-env": "^7.0.3", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "typescript": "^5.6.3" + }, + "peerDependencies": { + "@hookform/resolvers": "^3.2.0", + "@mui/material": "^5.0.0", + "@perses-dev/components": "^0.53.0-rc.1", + "@perses-dev/core": "^0.53.0-beta.4", + "@perses-dev/dashboards": "^0.53.0-rc.1", + "@perses-dev/explore": "^0.53.0-rc.1", + "@perses-dev/plugin-system": "^0.53.0-rc.1", + "@tanstack/react-query": "^4.39.1", + "date-fns": "^4.1.0", + "immer": "^10.1.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "react-hook-form": "^7.52.2", + "react-router-dom": "^6.27.0", + "zod": "^3.22.4" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/@jest/core": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/@jest/core/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/@jest/schemas": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mole-inc/bin-wrapper": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "bin-check": "^4.1.0", + "bin-version-check": "^5.0.0", + "content-disposition": "^0.5.4", + "ext-name": "^5.0.0", + "file-type": "^17.1.6", + "filenamify": "^5.0.2", + "got": "^11.8.5", + "os-filter-obj": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@swc/cli": { + "version": "0.4.1-nightly.20240914", + "dev": true, + "license": "MIT", + "dependencies": { + "@mole-inc/bin-wrapper": "^8.0.1", + "@swc/counter": "^0.1.3", + "commander": "^8.3.0", + "fast-glob": "^3.2.5", + "minimatch": "^9.0.3", + "piscina": "^4.3.0", + "semver": "^7.3.8", + "slash": "3.0.0", + "source-map": "^0.7.3" + }, + "bin": { + "spack": "bin/spack.js", + "swc": "bin/swc.js", + "swcx": "bin/swcx.js" + }, + "engines": { + "node": ">= 16.14.0" + }, + "peerDependencies": { + "@swc/core": "^1.2.66", + "chokidar": "^3.5.1" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@swc/cli/node_modules/commander": { + "version": "8.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@swc/cli/node_modules/source-map": { + "version": "0.7.6", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@swc/core": { + "version": "1.15.11", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.11", + "@swc/core-darwin-x64": "1.15.11", + "@swc/core-linux-arm-gnueabihf": "1.15.11", + "@swc/core-linux-arm64-gnu": "1.15.11", + "@swc/core-linux-arm64-musl": "1.15.11", + "@swc/core-linux-x64-gnu": "1.15.11", + "@swc/core-linux-x64-musl": "1.15.11", + "@swc/core-win32-arm64-msvc": "1.15.11", + "@swc/core-win32-ia32-msvc": "1.15.11", + "@swc/core-win32-x64-msvc": "1.15.11" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.11", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "12.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "<18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "<18.0.0", + "react-dom": "<18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/react/node_modules/@types/react-dom": { + "version": "17.0.26", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/aria-query": { + "version": "5.1.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@testing-library/react/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/@testing-library/react/node_modules/deep-equal": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/color-hash": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/@types/jest/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.23", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/abab": { + "version": "2.0.6", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "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" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "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-jest/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "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", + "dev": true, + "license": "BSD-3-Clause", + "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-istanbul/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "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.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "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" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bin-check": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^0.7.0", + "executable": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-version": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "find-versions": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version-check": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "bin-version": "^6.0.0", + "semver": "^7.5.3", + "semver-truncate": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/bin-version/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "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" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "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" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001766", + "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" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/char-regex": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/co": { + "version": "4.6.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "8.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/concurrently/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/date-fns": { + "version": "2.30.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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-jest/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/cross-env": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cssom": { + "version": "0.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "dev": true, + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "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/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "dev": true, + "license": "MIT" + }, + "node_modules/domexception": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.283", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "0.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/execa/node_modules/lru-cache": { + "version": "4.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/execa/node_modules/yallist": { + "version": "2.1.2", + "dev": true, + "license": "ISC" + }, + "node_modules/executable": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/ext-list": { + "version": "2.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.28.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext-name": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-type": { + "version": "17.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0-alpha.9", + "token-types": "^5.0.0-alpha.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^3.0.0", + "strip-outer": "^2.0.0", + "trim-repeated": "^2.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-versions": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^4.0.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "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" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/import-local": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/jest-changed-files/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-changed-files/node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-changed-files/node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-config": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-leak-detector/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-runner": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-runtime": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-snapshot": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-util": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-validate": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "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/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "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/jest-worker": { + "version": "29.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nwsapi": { + "version": "2.2.23", + "dev": true, + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-filter-obj": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/peek-readable": { + "version": "5.4.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/piscina": { + "version": "4.9.2", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/process": { + "version": "0.11.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/psl": { + "version": "1.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "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" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "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" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "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" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-truncate": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "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", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-outer": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "7.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.1.3" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-types": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-repeated": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "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" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/influxdb/package.json b/influxdb/package.json new file mode 100644 index 00000000..4575f0cc --- /dev/null +++ b/influxdb/package.json @@ -0,0 +1,95 @@ +{ + "name": "@perses-dev/influxdb-plugin", + "version": "0.1.0-rc.0", + "homepage": "https://github.com/perses/plugins/blob/main/README.md", + "repository": { + "type": "git", + "url": "git+https://github.com/perses/plugins.git" + }, + "bugs": { + "url": "https://github.com/perses/plugins/issues" + }, + "scripts": { + "dev": "rsbuild dev", + "build": "npm run build-mf && concurrently \"npm:build:*\"", + "build-mf": "rsbuild build", + "build:cjs": "swc ./src -d dist/lib/cjs --strip-leading-paths --config-file ../.cjs.swcrc", + "build:esm": "swc ./src -d dist/lib --strip-leading-paths --config-file ../.swcrc", + "build:types": "tsc --project tsconfig.build.json", + "lint": "eslint src --ext .ts,.tsx", + "test": "cross-env LC_ALL=C TZ=UTC jest", + "type-check": "tsc --noEmit" + }, + "main": "lib/cjs/index.js", + "module": "lib/index.js", + "types": "lib/index.d.ts", + "peerDependencies": { + "@hookform/resolvers": "^3.2.0", + "@perses-dev/components": "^0.53.0-rc.2", + "@perses-dev/core": "^0.53.0-beta.4", + "@perses-dev/dashboards": "^0.53.0-rc.2", + "@perses-dev/explore": "^0.53.0-rc.2", + "@perses-dev/plugin-system": "^0.53.0-rc.2", + "@tanstack/react-query": "^4.39.1", + "date-fns": "^4.1.0", + "immer": "^10.1.1", + "react": "^17.0.2 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0", + "react-hook-form": "^7.52.2", + "react-router-dom": "^6.27.0" + }, + "devDependencies": { + "@swc/cli": "^0.4.1-nightly.20240914", + "@swc/core": "^1.7.28", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^12.1.4", + "@testing-library/user-event": "^14.5.2", + "@types/color-hash": "^2.0.0", + "@types/jest": "^29.5.14", + "@types/lodash": "^4.17.13", + "@types/react": "^18.3.12", + "concurrently": "^8.2.2", + "cross-env": "^7.0.3", + "jest": "^30.2.0", + "jest-environment-jsdom": "^29.7.0", + "typescript": "^5.6.3" + }, + "files": [ + "lib/**/*", + "__mf/**/*", + "mf-manifest.json", + "mf-stats.json" + ], + "perses": { + "schemasPath": "schemas", + "plugins": [ + { + "kind": "Datasource", + "spec": { + "display": { + "name": "InfluxDB Datasource" + }, + "name": "InfluxDBDatasource" + } + }, + { + "kind": "TimeSeriesQuery", + "spec": { + "display": { + "name": "InfluxDB Time Series Query" + }, + "name": "InfluxDBTimeSeriesQuery" + } + }, + { + "kind": "Explore", + "spec": { + "display": { + "name": "InfluxDB Explorer" + }, + "name": "InfluxDBExplorer" + } + } + ] + } +} diff --git a/influxdb/rsbuild.config.ts b/influxdb/rsbuild.config.ts new file mode 100644 index 00000000..01c144e1 --- /dev/null +++ b/influxdb/rsbuild.config.ts @@ -0,0 +1,30 @@ +import { pluginReact } from '@rsbuild/plugin-react'; +import { createConfigForPlugin } from '../rsbuild.shared'; + +export default createConfigForPlugin({ + name: 'InfluxDB', + rsbuildConfig: { + plugins: [pluginReact()], + }, + moduleFederation: { + exposes: { + './InfluxDBDatasource': './src/datasource/influxdb/InfluxDBDatasource.ts', + './InfluxDBTimeSeriesQuery': './src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery.ts', + './InfluxDBExplorer': './src/explore/InfluxDBExplorer.tsx', + }, + shared: { + react: { singleton: true, requiredVersion: false }, + 'react-dom': { singleton: true, requiredVersion: false }, + 'date-fns': { singleton: true }, + '@perses-dev/core': { singleton: true, requiredVersion: false }, + '@perses-dev/plugin-system': { singleton: true, requiredVersion: false }, + '@perses-dev/components': { singleton: true, requiredVersion: false }, + '@perses-dev/explore': { singleton: true }, + '@perses-dev/dashboards': { singleton: true }, + '@hookform/resolvers': { singleton: true }, + '@tanstack/react-query': { singleton: true }, + 'react-hook-form': { singleton: true }, + 'react-router-dom': { singleton: true }, + }, + }, +}); diff --git a/influxdb/schemas/datasource/influxdb.cue b/influxdb/schemas/datasource/influxdb.cue new file mode 100644 index 00000000..09544d9c --- /dev/null +++ b/influxdb/schemas/datasource/influxdb.cue @@ -0,0 +1,46 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "github.com/perses/shared/cue/common" + commonProxy "github.com/perses/shared/cue/common/proxy" +) + +#kind: "InfluxDBDatasource" + +kind: #kind +spec: { + version: "v1" | "v3" + (#directUrl | #proxy) + + + // V1 specific fields + database?: string + auth?: string + + // V3 specific fields + organization?: string + bucket?: string +} + +#directUrl: { + directUrl: common.#url +} + +#proxy: { + proxy: commonProxy.#HTTPProxy +} + +#selector: common.#datasourceSelector & { _kind: #kind } diff --git a/influxdb/schemas/influxdb-time-series-query/query.cue b/influxdb/schemas/influxdb-time-series-query/query.cue new file mode 100644 index 00000000..39b85fac --- /dev/null +++ b/influxdb/schemas/influxdb-time-series-query/query.cue @@ -0,0 +1,28 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "strings" + ds "github.com/perses/plugins/influxdb/schemas/datasource:model" +) + +kind: "InfluxDBTimeSeriesQuery" +spec: close({ + ds.#selector + query: strings.MinRunes(1) + seriesNameFormat?: string +}) + +#variableSyntaxRegex: "^\\$\\w+$" diff --git a/influxdb/sdk/go/datasource/datasource.go b/influxdb/sdk/go/datasource/datasource.go new file mode 100644 index 00000000..3eada9d1 --- /dev/null +++ b/influxdb/sdk/go/datasource/datasource.go @@ -0,0 +1,158 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package datasource + +import ( + "encoding/json" + "fmt" + + "github.com/perses/perses/go-sdk/datasource" + "github.com/perses/perses/pkg/model/api/v1/datasource/http" +) + +const ( + PluginKindV1 = "InfluxDBV1Datasource" + PluginKindV3 = "InfluxDBV3Datasource" +) + +// InfluxDBVersion represents the InfluxDB version +type InfluxDBVersion string + +const ( + VersionV1 InfluxDBVersion = "v1" + VersionV3 InfluxDBVersion = "v3" +) + +type PluginSpec struct { + DirectURL string `json:"directUrl,omitempty" yaml:"directUrl,omitempty"` + Proxy *http.Proxy `json:"proxy,omitempty" yaml:"proxy,omitempty"` + Version InfluxDBVersion `json:"version" yaml:"version"` + // V1 specific fields + Database string `json:"database,omitempty" yaml:"database,omitempty"` + // V3 specific fields + Organization string `json:"organization,omitempty" yaml:"organization,omitempty"` + Bucket string `json:"bucket,omitempty" yaml:"bucket,omitempty"` +} + +func (s *PluginSpec) UnmarshalJSON(data []byte) error { + type plain PluginSpec + var tmp PluginSpec + if err := json.Unmarshal(data, (*plain)(&tmp)); err != nil { + return err + } + if err := (&tmp).validate(); err != nil { + return err + } + *s = tmp + return nil +} + +func (s *PluginSpec) UnmarshalYAML(unmarshal func(interface{}) error) error { + var tmp PluginSpec + type plain PluginSpec + if err := unmarshal((*plain)(&tmp)); err != nil { + return err + } + if err := (&tmp).validate(); err != nil { + return err + } + *s = tmp + return nil +} + +func (s *PluginSpec) validate() error { + if len(s.DirectURL) == 0 && s.Proxy == nil { + return fmt.Errorf("directUrl or proxy cannot be empty") + } + if len(s.DirectURL) > 0 && s.Proxy != nil { + return fmt.Errorf("at most directUrl or proxy must be configured") + } + if s.Version != VersionV1 && s.Version != VersionV3 { + return fmt.Errorf("version must be either 'v1' or 'v3'") + } + if s.Version == VersionV1 && len(s.Database) == 0 { + return fmt.Errorf("database is required for InfluxDB v1") + } + if s.Version == VersionV3 { + if len(s.Organization) == 0 { + return fmt.Errorf("organization is required for InfluxDB v3") + } + if len(s.Bucket) == 0 { + return fmt.Errorf("bucket is required for InfluxDB v3") + } + } + return nil +} + +type Option func(plugin *Builder) error + +func create(options ...Option) (Builder, error) { + builder := &Builder{ + PluginSpec: PluginSpec{}, + } + + var defaults []Option + + for _, opt := range append(defaults, options...) { + if err := opt(builder); err != nil { + return *builder, err + } + } + + return *builder, nil +} + +type Builder struct { + PluginSpec `json:",inline" yaml:",inline"` +} + +func InfluxDBV1(options ...Option) datasource.Option { + return func(builder *datasource.Builder) error { + plugin, err := create(options...) + if err != nil { + return err + } + + builder.Spec.Plugin.Kind = PluginKindV1 + builder.Spec.Plugin.Spec = plugin.PluginSpec + return nil + } +} + +func InfluxDBV3(options ...Option) datasource.Option { + return func(builder *datasource.Builder) error { + plugin, err := create(options...) + if err != nil { + return err + } + + builder.Spec.Plugin.Kind = PluginKindV3 + builder.Spec.Plugin.Spec = plugin.PluginSpec + return nil + } +} + +func SelectorV1(datasourceName string) *datasource.Selector { + return &datasource.Selector{ + Kind: PluginKindV1, + Name: datasourceName, + } +} + +func SelectorV3(datasourceName string) *datasource.Selector { + return &datasource.Selector{ + Kind: PluginKindV3, + Name: datasourceName, + } +} diff --git a/influxdb/sdk/go/datasource/datasource_test.go b/influxdb/sdk/go/datasource/datasource_test.go new file mode 100644 index 00000000..6b891f51 --- /dev/null +++ b/influxdb/sdk/go/datasource/datasource_test.go @@ -0,0 +1,201 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package datasource + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPluginSpecValidation(t *testing.T) { + tests := []struct { + name string + spec PluginSpec + expectError bool + errorMsg string + }{ + { + name: "valid V1 with directUrl", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV1, + Database: "mydb", + }, + expectError: false, + }, + { + name: "valid V3 with directUrl", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV3, + Organization: "myorg", + Bucket: "mybucket", + }, + expectError: false, + }, + { + name: "missing directUrl and proxy", + spec: PluginSpec{ + Version: VersionV1, + Database: "mydb", + }, + expectError: true, + errorMsg: "directUrl or proxy cannot be empty", + }, + { + name: "missing version", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Database: "mydb", + }, + expectError: true, + errorMsg: "version must be either 'v1' or 'v3'", + }, + { + name: "invalid version", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Version: "v2", + Database: "mydb", + }, + expectError: true, + errorMsg: "version must be either 'v1' or 'v3'", + }, + { + name: "V1 missing database", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV1, + }, + expectError: true, + errorMsg: "database is required for InfluxDB v1", + }, + { + name: "V3 missing organization", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV3, + Bucket: "mybucket", + }, + expectError: true, + errorMsg: "organization is required for InfluxDB v3", + }, + { + name: "V3 missing bucket", + spec: PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV3, + Organization: "myorg", + }, + expectError: true, + errorMsg: "bucket is required for InfluxDB v3", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.spec.validate() + if tt.expectError { + assert.Error(t, err) + if tt.errorMsg != "" { + assert.Contains(t, err.Error(), tt.errorMsg) + } + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestPluginSpecUnmarshalJSON(t *testing.T) { + tests := []struct { + name string + json string + expectError bool + expected *PluginSpec + }{ + { + name: "valid V1 JSON", + json: `{ + "directUrl": "http://localhost:8086", + "version": "v1", + "database": "mydb" + }`, + expectError: false, + expected: &PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV1, + Database: "mydb", + }, + }, + { + name: "valid V3 JSON", + json: `{ + "directUrl": "http://localhost:8086", + "version": "v3", + "organization": "myorg", + "bucket": "mybucket" + }`, + expectError: false, + expected: &PluginSpec{ + DirectURL: "http://localhost:8086", + Version: VersionV3, + Organization: "myorg", + Bucket: "mybucket", + }, + }, + { + name: "invalid JSON - missing required field", + json: `{ + "directUrl": "http://localhost:8086", + "version": "v1" + }`, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var spec PluginSpec + err := json.Unmarshal([]byte(tt.json), &spec) + if tt.expectError { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expected.DirectURL, spec.DirectURL) + assert.Equal(t, tt.expected.Version, spec.Version) + assert.Equal(t, tt.expected.Database, spec.Database) + assert.Equal(t, tt.expected.Organization, spec.Organization) + assert.Equal(t, tt.expected.Bucket, spec.Bucket) + } + }) + } +} + +func TestSelectors(t *testing.T) { + t.Run("SelectorV1", func(t *testing.T) { + selector := SelectorV1("test-influxdb-v1") + assert.Equal(t, PluginKindV1, selector.Kind) + assert.Equal(t, "test-influxdb-v1", selector.Name) + }) + + t.Run("SelectorV3", func(t *testing.T) { + selector := SelectorV3("test-influxdb-v3") + assert.Equal(t, PluginKindV3, selector.Kind) + assert.Equal(t, "test-influxdb-v3", selector.Name) + }) +} diff --git a/influxdb/sdk/go/datasource/options.go b/influxdb/sdk/go/datasource/options.go new file mode 100644 index 00000000..4e9e7d6a --- /dev/null +++ b/influxdb/sdk/go/datasource/options.go @@ -0,0 +1,66 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package datasource + +import ( + "github.com/perses/perses/pkg/model/api/v1/datasource/http" +) + +// DirectURL sets the direct URL for the InfluxDB datasource +func DirectURL(url string) Option { + return func(builder *Builder) error { + builder.DirectURL = url + return nil + } +} + +// HTTPProxy sets the proxy configuration for the InfluxDB datasource +func HTTPProxy(proxy *http.Proxy) Option { + return func(builder *Builder) error { + builder.Proxy = proxy + return nil + } +} + +// Version sets the InfluxDB version +func Version(version InfluxDBVersion) Option { + return func(builder *Builder) error { + builder.Version = version + return nil + } +} + +// Database sets the database name for InfluxDB V1 +func Database(database string) Option { + return func(builder *Builder) error { + builder.Database = database + return nil + } +} + +// Organization sets the organization for InfluxDB V3 +func Organization(organization string) Option { + return func(builder *Builder) error { + builder.Organization = organization + return nil + } +} + +// Bucket sets the bucket for InfluxDB V3 +func Bucket(bucket string) Option { + return func(builder *Builder) error { + builder.Bucket = bucket + return nil + } +} diff --git a/influxdb/src/bootstrap.ts b/influxdb/src/bootstrap.ts new file mode 100644 index 00000000..46787fc9 --- /dev/null +++ b/influxdb/src/bootstrap.ts @@ -0,0 +1,24 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Re-export plugin module metadata +export { getPluginModule } from './getPluginModule'; + +// Re-export the unified datasource for both V1 and V3 +export { InfluxDBDatasource } from './datasource/influxdb/InfluxDBDatasource'; + +// Re-export the query with proper name for module federation +export { InfluxDBTimeSeriesQuery } from './queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery'; + +// Re-export the explore plugin +export { InfluxDBExplorer } from './explore/InfluxDBExplorer'; diff --git a/influxdb/src/datasource/influxdb/InfluxDBDatasource.ts b/influxdb/src/datasource/influxdb/InfluxDBDatasource.ts new file mode 100644 index 00000000..afd330c7 --- /dev/null +++ b/influxdb/src/datasource/influxdb/InfluxDBDatasource.ts @@ -0,0 +1,183 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { fetch, RequestHeaders, HTTPProxy } from '@perses-dev/core'; +import { DatasourcePlugin } from '@perses-dev/plugin-system'; +import { InfluxDBEditor } from './InfluxDBEditor'; + +export type InfluxDBVersion = 'v1' | 'v3'; + +/** + * Validates that a query is read-only (no write operations) + * Blocks: INSERT, WRITE, DELETE, DROP, ALTER, CREATE, etc. + * Allows: SELECT, SHOW, etc. + */ +function validateReadOnlyQuery(query: string): void { + const writePatterns = [ + /^\s*INSERT\s/i, + /^\s*WRITE\s/i, + /^\s*DELETE\s/i, + /^\s*DROP\s/i, + /^\s*ALTER\s/i, + /^\s*CREATE\s/i, + /^\s*TRUNCATE\s/i, + /^\s*UPDATE\s/i, + ]; + + const trimmedQuery = query.trim(); + + for (const pattern of writePatterns) { + if (pattern.test(trimmedQuery)) { + const firstWord = trimmedQuery.split(' ')[0]?.toUpperCase() || 'UNKNOWN'; + throw new Error( + `Write operations are not allowed. Query cannot start with: ${firstWord}. ` + + 'Only read-only queries (SELECT, SHOW, etc.) are permitted.' + ); + } + } +} + +export interface InfluxDBSpec { + version: InfluxDBVersion; + directUrl?: string; + proxy?: HTTPProxy; + // V1 specific + database?: string; + auth?: string; + // V3 specific + organization?: string; + bucket?: string; +} +export interface InfluxDBV1Response { + results: Array<{ + series?: Array<{ + name: string; + columns: string[]; + values: Array>; + }>; + error?: string; + }>; +} +export interface InfluxDBV3Response { + data?: Record; + error?: string; +} +export interface InfluxDBClient { + options: { datasourceUrl: string; headers?: RequestHeaders }; + queryV1?(query: string, database: string, headers?: RequestHeaders): Promise; + queryV3SQL?(query: string, headers?: RequestHeaders): Promise; + queryV3Flux?(query: string, headers?: RequestHeaders): Promise; +} +const createClient: DatasourcePlugin['createClient'] = (spec, options) => { + const { version, directUrl } = spec; + const { proxyUrl } = options; + const datasourceUrl = directUrl ?? proxyUrl; + if (!datasourceUrl) { + throw new Error('No URL specified for InfluxDB client'); + } + // Note: Authentication and TLS are handled by the backend + // - Proxy: backend resolves spec.auth secret and injects headers/certs via HTTPProxy + // - Direct: HTTP client applies spec.auth secret for TLS and Basic Auth + // Backend handles: password (v1), token (v3), TLS certs, CA certs, proxy headers + const authHeaders: RequestHeaders = {}; + const fetchOptions: RequestInit = { headers: authHeaders }; + const client: InfluxDBClient = { + options: { datasourceUrl, headers: authHeaders }, + }; + if (version === 'v1') { + client.queryV1 = async (query: string, database: string, headers?: RequestHeaders): Promise => { + if (!spec.database && !database) { + throw new Error('Database is required for InfluxDB v1'); + } + // V1 Proxy Mode: Only supports read-only /query endpoint (GET) + // Write operations via /write endpoint are NOT allowed in proxy mode + const url = new URL('/query', datasourceUrl); + url.searchParams.set('db', database || spec.database!); + url.searchParams.set('q', query); + const response = await fetch(url.toString(), { + ...fetchOptions, + method: 'GET', + headers: { ...authHeaders, ...headers }, + }); + if (!response.ok) throw new Error('InfluxDB v1 query failed: ' + response.statusText); + return response.json(); + }; + } else if (version === 'v3') { + client.queryV3SQL = async (query: string, headers?: RequestHeaders): Promise => { + // Validate: Only read-only queries allowed (no INSERT, WRITE, DELETE, etc.) + validateReadOnlyQuery(query); + + // V3 SQL Read-Only: /api/v2/query endpoint + // Only read-only queries are allowed in both direct and proxy access + const url = new URL('/api/v2/query', datasourceUrl); + const body = JSON.stringify({ + query, + dialect: { + header: true, + delimiter: ',', + quoteChar: '"', + commentPrefix: '#', + dateTimeFormat: 'RFC3339', + annotations: ['datatype', 'group', 'default'], + }, + }); + const response = await fetch(url.toString(), { + ...fetchOptions, + method: 'POST', + headers: { ...authHeaders, ...headers }, + body, + }); + if (!response.ok) { + throw new Error(`InfluxDB v3 query failed: ${response.statusText}`); + } + return response.json(); + }; + client.queryV3Flux = async (query: string, headers?: RequestHeaders): Promise => { + if (!spec.organization) { + throw new Error('Organization is required for InfluxDB v3 Flux'); + } + // Validate: Only read-only queries allowed (no INSERT, WRITE, DELETE, etc.) + validateReadOnlyQuery(query); + + // V3 Flux Read-Only: /api/v2/query endpoint + // Only read-only queries are allowed in both direct and proxy access + const url = new URL('/api/v2/query', datasourceUrl); + const body = new URLSearchParams({ + org: spec.organization, + query, + }); + const response = await fetch(url.toString(), { + ...fetchOptions, + method: 'POST', + headers: { ...authHeaders, ...headers }, + body, + }); + if (!response.ok) { + throw new Error(`InfluxDB v3 flux query failed: ${response.statusText}`); + } + return response.json(); + }; + } + return client; +}; +export const InfluxDBDatasource: DatasourcePlugin = { + createClient, + createInitialOptions: () => ({ + version: 'v1', + database: '', + }), + OptionsEditorComponent: InfluxDBEditor, +}; +// Named export with the name expected by module federation +export const InfluxDBDatasourceExport = InfluxDBDatasource; +// Default export for Module Federation +export default InfluxDBDatasource; diff --git a/influxdb/src/datasource/influxdb/InfluxDBEditor.tsx b/influxdb/src/datasource/influxdb/InfluxDBEditor.tsx new file mode 100644 index 00000000..bd770675 --- /dev/null +++ b/influxdb/src/datasource/influxdb/InfluxDBEditor.tsx @@ -0,0 +1,203 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { TextField, Box, Stack, FormControl, InputLabel, Select, MenuItem, Typography } from '@mui/material'; +import { HTTPSettingsEditor } from '@perses-dev/plugin-system'; +import { HTTPDatasourceSpec } from '@perses-dev/core'; +import React, { ReactElement } from 'react'; +import { InfluxDBSpec } from './InfluxDBDatasource'; +export interface InfluxDBEditorProps { + value: InfluxDBSpec; + onChange: (next: InfluxDBSpec) => void; + isReadonly?: boolean; +} +export function InfluxDBEditor(props: InfluxDBEditorProps): ReactElement { + const { value, onChange, isReadonly } = props; + const version = value?.version || 'v1'; + // Version-specific AllowedEndpoints + const allowedEndpointsV1 = [ + { + endpointPattern: '/query', + method: 'GET', + }, + ]; + const allowedEndpointsV3 = [ + { + endpointPattern: '/api/v3/query_sql', + method: 'GET', + }, + { + endpointPattern: '/api/v3/query_influxql', + method: 'GET', + }, + ]; + // Build initial specs with version-specific endpoints + const initialSpecDirectV1: HTTPDatasourceSpec = { + directUrl: '', + }; + const initialSpecProxyV1: HTTPDatasourceSpec = { + proxy: { + kind: 'HTTPProxy', + spec: { + allowedEndpoints: allowedEndpointsV1, + url: '', + }, + }, + }; + const initialSpecDirectV3: HTTPDatasourceSpec = { + directUrl: '', + }; + const initialSpecProxyV3: HTTPDatasourceSpec = { + proxy: { + kind: 'HTTPProxy', + spec: { + allowedEndpoints: allowedEndpointsV3, + url: '', + }, + }, + }; + // Select the right initial specs based on current version + const initialSpecDirect = version === 'v1' ? initialSpecDirectV1 : initialSpecDirectV3; + const initialSpecProxy = version === 'v1' ? initialSpecProxyV1 : initialSpecProxyV3; + // Handle changes from HTTP Settings + const handleHTTPSettingsChange = (next: HTTPDatasourceSpec) => { + const updated = { ...value, ...next }; + // If this is a proxy config, ensure it has the correct version-specific endpoints + if (updated.proxy?.spec) { + const correctEndpoints = version === 'v1' ? allowedEndpointsV1 : allowedEndpointsV3; + onChange({ + ...updated, + proxy: { + ...updated.proxy, + spec: { + ...updated.proxy.spec, + allowedEndpoints: correctEndpoints, + }, + }, + } as InfluxDBSpec); + } else { + onChange(updated as InfluxDBSpec); + } + }; + return ( + + {/* 1. Version Selection FIRST */} + + + Version + + + + {/* 2. HTTP Settings (Direct/Proxy) with version-specific endpoints */} + + {/* 3. Version-Specific Fields */} + {version === 'v1' && ( + + + InfluxDB v1 Configuration + + + onChange({ ...value, database: e.target.value })} + placeholder="e.g., mydb" + disabled={isReadonly} + fullWidth + helperText="Name of the InfluxDB database" + /> + onChange({ ...value, auth: e.target.value })} + placeholder="Secret name containing credentials" + disabled={isReadonly} + fullWidth + helperText="Name of the secret containing username/password" + /> + + + )} + {version === 'v3' && ( + + + InfluxDB v3 Configuration + + + onChange({ ...value, organization: e.target.value })} + placeholder="e.g., myorg" + disabled={isReadonly} + fullWidth + helperText="Name of the InfluxDB organization" + /> + onChange({ ...value, bucket: e.target.value })} + placeholder="e.g., mybucket" + disabled={isReadonly} + fullWidth + helperText="Name of the InfluxDB bucket" + /> + onChange({ ...value, auth: e.target.value })} + placeholder="Secret name containing token" + disabled={isReadonly} + fullWidth + helperText="Name of the secret containing auth token" + /> + + + )} + + ); +} diff --git a/influxdb/src/datasource/influxdb/index.ts b/influxdb/src/datasource/influxdb/index.ts new file mode 100644 index 00000000..2dece5ef --- /dev/null +++ b/influxdb/src/datasource/influxdb/index.ts @@ -0,0 +1,2 @@ +export * from './InfluxDBDatasource'; +export * from './InfluxDBEditor'; diff --git a/influxdb/src/explore/InfluxDBExplorer.tsx b/influxdb/src/explore/InfluxDBExplorer.tsx new file mode 100644 index 00000000..0b912cc1 --- /dev/null +++ b/influxdb/src/explore/InfluxDBExplorer.tsx @@ -0,0 +1,138 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ReactElement, useState } from 'react'; +import { Box, Stack, Tab, Tabs } from '@mui/material'; +import { DataQueriesProvider, MultiQueryEditor, useSuggestedStepMs } from '@perses-dev/plugin-system'; +import { Panel } from '@perses-dev/dashboards'; +import { QueryDefinition } from '@perses-dev/core'; +import { useExplorerManagerContext } from '@perses-dev/explore'; +import useResizeObserver from 'use-resize-observer'; + +interface TimeSeriesExplorerQueryParams { + tab?: string; + queries?: QueryDefinition[]; +} + +const PANEL_PREVIEW_HEIGHT = 700; +const FILTERED_QUERY_PLUGINS = ['InfluxDBTimeSeriesQuery']; + +function TimeSeriesPanel({ queries }: { queries: QueryDefinition[] }): ReactElement { + const { width, ref: boxRef } = useResizeObserver(); + const height = PANEL_PREVIEW_HEIGHT; + + const suggestedStepMs = useSuggestedStepMs(width); + + // map TimeSeriesQueryDefinition to Definition + const definitions = queries.length + ? queries.map((query) => { + return { + kind: query.spec.plugin.kind, + spec: query.spec.plugin.spec, + }; + }) + : []; + + return ( + + + + + + ); +} + +function MetricDataTable({ queries }: { queries: QueryDefinition[] }): ReactElement { + const height = PANEL_PREVIEW_HEIGHT; + + // map TimeSeriesQueryDefinition to Definition + const definitions = queries.map((query) => { + return { + kind: query.spec.plugin.kind, + spec: query.spec.plugin.spec, + }; + }); + + return ( + + + + + + ); +} + +export function InfluxDBExplorer(): ReactElement { + const { + data: { tab = 'table', queries = [] }, + setData, + } = useExplorerManagerContext(); + + const [queryDefinitions, setQueryDefinitions] = useState(queries); + + return ( + + setData({ tab: state, queries })} + variant="scrollable" + sx={{ borderBottom: 1, borderColor: 'divider' }} + > + + + + + {tab === 'table' && ( + + setQueryDefinitions(state)} + queries={queryDefinitions} + onQueryRun={() => setData({ tab, queries: queryDefinitions })} + filteredQueryPlugins={FILTERED_QUERY_PLUGINS} + /> + + + )} + {tab === 'graph' && ( + + setQueryDefinitions(state)} + queries={queryDefinitions} + onQueryRun={() => setData({ tab, queries: queryDefinitions })} + filteredQueryPlugins={FILTERED_QUERY_PLUGINS} + /> + + + )} + + + ); +} diff --git a/influxdb/src/explore/index.ts b/influxdb/src/explore/index.ts new file mode 100644 index 00000000..23ad50f7 --- /dev/null +++ b/influxdb/src/explore/index.ts @@ -0,0 +1,14 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export * from './InfluxDBExplorer'; diff --git a/influxdb/src/getPluginModule.ts b/influxdb/src/getPluginModule.ts new file mode 100644 index 00000000..cab8b384 --- /dev/null +++ b/influxdb/src/getPluginModule.ts @@ -0,0 +1,30 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { PluginModuleResource, PluginModuleSpec } from '@perses-dev/plugin-system'; +import packageJson from '../package.json'; + +/** + * Returns the plugin module information from package.json + */ +export function getPluginModule(): PluginModuleResource { + const { name, version, perses } = packageJson; + return { + kind: 'PluginModule', + metadata: { + name, + version, + }, + spec: perses as PluginModuleSpec, + }; +} diff --git a/influxdb/src/index-federation.ts b/influxdb/src/index-federation.ts new file mode 100644 index 00000000..b93c7a02 --- /dev/null +++ b/influxdb/src/index-federation.ts @@ -0,0 +1 @@ +import('./bootstrap'); diff --git a/influxdb/src/index.ts b/influxdb/src/index.ts new file mode 100644 index 00000000..d30032c1 --- /dev/null +++ b/influxdb/src/index.ts @@ -0,0 +1,17 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export * from './explore'; +export { getPluginModule } from './getPluginModule'; +export * from './datasource/influxdb/InfluxDBDatasource'; +export * from './queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery'; diff --git a/influxdb/src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery.ts b/influxdb/src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery.ts new file mode 100644 index 00000000..e79dd96b --- /dev/null +++ b/influxdb/src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery.ts @@ -0,0 +1,168 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { TimeSeriesQueryPlugin, replaceVariables } from '@perses-dev/plugin-system'; +import { TimeSeriesData, TimeSeries, DatasourceSelector, AbsoluteTimeRange } from '@perses-dev/core'; +import { InfluxDBTimeSeriesQueryEditor } from './InfluxDBTimeSeriesQueryEditor'; +export interface InfluxDBTimeSeriesQuerySpec { + datasource?: DatasourceSelector; + query: string; +} +interface InfluxDBV1SeriesData { + name: string; + columns: string[]; + values: Array>; + tags?: Record; +} + +interface InfluxDBV1ResponseData { + results: Array<{ + series?: InfluxDBV1SeriesData[]; + }>; +} + +function convertV1ResponseToTimeSeries(response: InfluxDBV1ResponseData): TimeSeries[] { + const series: TimeSeries[] = []; + if (response.results && response.results[0] && response.results[0].series) { + response.results[0].series.forEach((seriesData: InfluxDBV1SeriesData) => { + const timeIndex = seriesData.columns.indexOf('time'); + const valueColumns = seriesData.columns.filter((col: string) => col !== 'time'); + valueColumns.forEach((valueColumn: string) => { + const valueIndex = seriesData.columns.indexOf(valueColumn); + const values = seriesData.values.map((row: Array) => [ + new Date(row[timeIndex] as string | number).getTime(), + row[valueIndex], + ]); + const tagStr = seriesData.tags + ? Object.entries(seriesData.tags) + .map(([k, v]) => k + '="' + v + '"') + .join(',') + : ''; + series.push({ + name: seriesData.name + '.' + valueColumn, + values: values as Array<[number, number | null]>, + formattedName: tagStr + ? seriesData.name + '{' + tagStr + '}.' + valueColumn + : seriesData.name + '.' + valueColumn, + }); + }); + }); + } + return series; +} +function convertV3ResponseToTimeSeries(_response: Record): TimeSeries[] { + const series: TimeSeries[] = []; + // TODO: Implement V3 response conversion + // V3 CSV/Flux response format + return series; +} +interface InfluxDBClient { + queryV1?: (query: string, database: string) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + queryV3SQL?: (query: string) => Promise>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + queryV3Flux?: (query: string) => Promise>; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} + +interface QueryContext { + timeRange: AbsoluteTimeRange; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + variableState: Record; + datasourceStore: { + getDatasourceClient: (selector: DatasourceSelector) => Promise; + getDatasourceSpec?: (selector: DatasourceSelector) => Promise>; + }; +} + +export const InfluxDBTimeSeriesQuery: TimeSeriesQueryPlugin = { + getTimeSeriesData: async (spec: InfluxDBTimeSeriesQuerySpec, context: QueryContext) => { + // Return empty if query is empty + if (!spec.query) { + return { + series: [], + timeRange: context.timeRange, + stepMs: 30 * 1000, + metadata: { + executedQueryString: '', + }, + } as TimeSeriesData; + } + // Replace variables in query + const query = replaceVariables(spec.query, context.variableState); + try { + // Get the datasource client from the store + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const client = (await context.datasourceStore.getDatasourceClient( + spec.datasource ?? { kind: 'InfluxDBDatasource' } + )) as InfluxDBClient; + if (!client) { + throw new Error('No datasource client available'); + } + // Get time range + const { start, end } = context.timeRange; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let clientResponse: InfluxDBV1ResponseData | Record; + let datasourceSpec: Record | undefined; + // Try to get datasource spec to determine a version + try { + datasourceSpec = await context.datasourceStore.getDatasourceSpec?.( + spec.datasource ?? { kind: 'InfluxDBDatasource' } + ); + } catch { + // Spec not available, we'll try to detect from client methods + } + // Determine version and call the appropriate query method + if (typeof client.queryV1 === 'function') { + // V1 Query + const database = datasourceSpec?.database || ''; + clientResponse = await client.queryV1(query, database as string); + } else if (typeof client.queryV3SQL === 'function') { + // V3 SQL Query + clientResponse = await client.queryV3SQL(query); + } else if (typeof client.queryV3Flux === 'function') { + // V3 Flux Query (fallback) + clientResponse = await client.queryV3Flux(query); + } else { + throw new Error('Datasource client has no query methods (queryV1, queryV3SQL, or queryV3Flux)'); + } + // Convert response to timeseries + let timeSeries: TimeSeries[] = []; + if (clientResponse) { + // Auto-detect V1 vs V3 based on response structure + if ('results' in clientResponse) { + // V1 response format + timeSeries = convertV1ResponseToTimeSeries(clientResponse as InfluxDBV1ResponseData); + } else if ('data' in clientResponse) { + // V3 response format + timeSeries = convertV3ResponseToTimeSeries(clientResponse); + } + } + return { + series: timeSeries, + timeRange: { start, end } as AbsoluteTimeRange, + stepMs: 30 * 1000, + metadata: { + executedQueryString: query, + }, + } as TimeSeriesData; + } catch (error) { + console.error('Error executing InfluxDB query:', error); + throw error; + } + }, + OptionsEditorComponent: InfluxDBTimeSeriesQueryEditor, + createInitialOptions: () => ({ query: '' }), +}; +// Default export for Module Federation +export default InfluxDBTimeSeriesQuery; diff --git a/influxdb/src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQueryEditor.tsx b/influxdb/src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQueryEditor.tsx new file mode 100644 index 00000000..e7566925 --- /dev/null +++ b/influxdb/src/queries/influxdb-time-series-query/InfluxDBTimeSeriesQueryEditor.tsx @@ -0,0 +1,47 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { TextField, Box } from '@mui/material'; +import { DatasourceSelect, DatasourceSelectProps, OptionsEditorProps } from '@perses-dev/plugin-system'; +import { DatasourceSelector } from '@perses-dev/core'; +import { InfluxDBTimeSeriesQuerySpec } from './InfluxDBTimeSeriesQuery'; +const DATASOURCE_KIND = 'InfluxDBDatasource'; +export function InfluxDBTimeSeriesQueryEditor({ value, onChange }: OptionsEditorProps) { + const handleDatasourceChange: DatasourceSelectProps['onChange'] = (newDatasource) => { + // Cast through unknown to handle type mismatch + const datasourceSelector = newDatasource as unknown as DatasourceSelector; + onChange({ ...value, datasource: datasourceSelector }); + }; + const defaultDatasource: DatasourceSelector = { + kind: DATASOURCE_KIND, + }; + return ( + + + onChange({ ...value, query: e.target.value })} + helperText="SQL query for InfluxDB v3 or InfluxQL for v1" + required + fullWidth + multiline + rows={6} + /> + + ); +} diff --git a/influxdb/src/queries/influxdb-time-series-query/index.ts b/influxdb/src/queries/influxdb-time-series-query/index.ts new file mode 100644 index 00000000..6c18f5c2 --- /dev/null +++ b/influxdb/src/queries/influxdb-time-series-query/index.ts @@ -0,0 +1,15 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export * from './InfluxDBTimeSeriesQuery'; +export * from './InfluxDBTimeSeriesQueryEditor'; diff --git a/influxdb/src/setup-tests.ts b/influxdb/src/setup-tests.ts new file mode 100644 index 00000000..ea1c9cf7 --- /dev/null +++ b/influxdb/src/setup-tests.ts @@ -0,0 +1,17 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import '@testing-library/jest-dom'; + +// Always mock e-charts during tests since we don't have a proper canvas in jsdom +jest.mock('echarts'); diff --git a/influxdb/src/test/ExposeModules.test.ts b/influxdb/src/test/ExposeModules.test.ts new file mode 100644 index 00000000..b4201ac4 --- /dev/null +++ b/influxdb/src/test/ExposeModules.test.ts @@ -0,0 +1,45 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as InfluxDBDatasourceModule from '../datasource/influxdb/InfluxDBDatasource'; +import * as InfluxDBTimeSeriesQueryModule from '../queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery'; + +describe('Module Federation Expose Modules', () => { + describe('InfluxDBDatasource.ts', () => { + it('should export default InfluxDBDatasource', () => { + expect(InfluxDBDatasourceModule.default).toBeDefined(); + expect(InfluxDBDatasourceModule.default).toHaveProperty('createClient'); + }); + + it('should have all required datasource properties', () => { + const datasource = InfluxDBDatasourceModule.default; + expect(datasource).toHaveProperty('createClient'); + expect(datasource).toHaveProperty('createInitialOptions'); + expect(datasource).toHaveProperty('OptionsEditorComponent'); + }); + }); + + describe('influxdb-time-series-query.ts', () => { + it('should export default InfluxDBTimeSeriesQuery', () => { + expect(InfluxDBTimeSeriesQueryModule.default).toBeDefined(); + expect(InfluxDBTimeSeriesQueryModule.default).toHaveProperty('getTimeSeriesData'); + }); + + it('should have all required query properties', () => { + const query = InfluxDBTimeSeriesQueryModule.default; + expect(query).toHaveProperty('getTimeSeriesData'); + expect(query).toHaveProperty('createInitialOptions'); + expect(query).toHaveProperty('OptionsEditorComponent'); + }); + }); +}); diff --git a/influxdb/src/test/InfluxDBDatasource.test.ts b/influxdb/src/test/InfluxDBDatasource.test.ts new file mode 100644 index 00000000..061fdb09 --- /dev/null +++ b/influxdb/src/test/InfluxDBDatasource.test.ts @@ -0,0 +1,79 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { InfluxDBDatasource, InfluxDBSpec } from '../datasource/influxdb/InfluxDBDatasource'; + +describe('InfluxDBDatasource', () => { + it('should have createClient method', () => { + expect(InfluxDBDatasource).toHaveProperty('createClient'); + expect(typeof InfluxDBDatasource.createClient).toBe('function'); + }); + + it('should have OptionsEditorComponent', () => { + expect(InfluxDBDatasource).toHaveProperty('OptionsEditorComponent'); + }); + + it('should have createInitialOptions method', () => { + expect(InfluxDBDatasource).toHaveProperty('createInitialOptions'); + expect(typeof InfluxDBDatasource.createInitialOptions).toBe('function'); + }); + + it('should create initial options with version and database', () => { + const initialOptions = InfluxDBDatasource.createInitialOptions(); + expect(initialOptions).toEqual({ + version: 'v1', + database: '', + }); + }); + + it('should throw error when no URL is provided', () => { + const spec: InfluxDBSpec = { + version: 'v1', + database: 'testdb', + }; + const options = { + proxyUrl: undefined, + }; + + expect(() => { + InfluxDBDatasource.createClient(spec, options); + }).toThrow('No URL specified for InfluxDB client'); + }); + + it('should use directUrl when provided', () => { + const spec: InfluxDBSpec = { + version: 'v1', + database: 'testdb', + directUrl: 'http://localhost:8086', + }; + const options = { + proxyUrl: 'http://proxy:8086', + }; + + const client = InfluxDBDatasource.createClient(spec, options); + expect(client.options.datasourceUrl).toBe('http://localhost:8086'); + }); + + it('should fall back to proxyUrl when directUrl is not provided', () => { + const spec: InfluxDBSpec = { + version: 'v1', + database: 'testdb', + }; + const options = { + proxyUrl: 'http://proxy:8086', + }; + + const client = InfluxDBDatasource.createClient(spec, options); + expect(client.options.datasourceUrl).toBe('http://proxy:8086'); + }); +}); diff --git a/influxdb/src/test/InfluxDBPluginIntegration.test.ts b/influxdb/src/test/InfluxDBPluginIntegration.test.ts new file mode 100644 index 00000000..74d6543e --- /dev/null +++ b/influxdb/src/test/InfluxDBPluginIntegration.test.ts @@ -0,0 +1,116 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { getPluginModule } from '../getPluginModule'; +import packageJson from '../../package.json'; + +interface PluginSpec { + kind: string; + spec: { + name: string; + display: { + name: string; + }; + }; +} + +describe('InfluxDB Plugin Integration', () => { + describe('Plugin Module Resource', () => { + it('should return valid PluginModuleResource', () => { + const pluginModule = getPluginModule(); + expect(pluginModule.kind).toBe('PluginModule'); + }); + + it('should have correct metadata from package.json', () => { + const pluginModule = getPluginModule(); + expect(pluginModule.metadata.name).toBe(packageJson.name); + expect(pluginModule.metadata.version).toBe(packageJson.version); + }); + + it('should include all plugins from package.json perses config', () => { + const pluginModule = getPluginModule(); + const expectedPlugins = packageJson.perses.plugins; + expect(pluginModule.spec.plugins).toEqual(expectedPlugins); + }); + + it('should have correct plugin kinds', () => { + const pluginModule = getPluginModule(); + const pluginKinds = pluginModule.spec.plugins.map((p: PluginSpec) => p.kind); + expect(pluginKinds).toContain('Datasource'); + expect(pluginKinds).toContain('TimeSeriesQuery'); + }); + + it('should have correct plugin names', () => { + const pluginModule = getPluginModule(); + const pluginNames = pluginModule.spec.plugins.map((p: PluginSpec) => p.spec.name); + expect(pluginNames).toContain('InfluxDBDatasource'); + expect(pluginNames).toContain('InfluxDBTimeSeriesQuery'); + }); + }); + + describe('Plugin Metadata', () => { + it('should have display names for all plugins', () => { + const pluginModule = getPluginModule(); + pluginModule.spec.plugins.forEach((plugin: PluginSpec) => { + expect(plugin.spec.display).toBeDefined(); + expect(plugin.spec.display.name).toBeDefined(); + expect(plugin.spec.display.name).toBeTruthy(); + }); + }); + + it('should have valid plugin specifications', () => { + const pluginModule = getPluginModule(); + pluginModule.spec.plugins.forEach((plugin: PluginSpec) => { + expect(plugin.kind).toBeTruthy(); + expect(plugin.spec).toBeDefined(); + expect(plugin.spec.name).toBeDefined(); + }); + }); + }); + + describe('Datasource Plugin Configuration', () => { + it('should have InfluxDB Datasource configured', () => { + const pluginModule = getPluginModule(); + const datasourcePlugin = pluginModule.spec.plugins.find( + (p: PluginSpec) => p.kind === 'Datasource' && p.spec.name === 'InfluxDBDatasource' + ); + expect(datasourcePlugin).toBeDefined(); + }); + + it('should have correct Datasource display name', () => { + const pluginModule = getPluginModule(); + const datasourcePlugin = pluginModule.spec.plugins.find( + (p: PluginSpec) => p.kind === 'Datasource' && p.spec.name === 'InfluxDBDatasource' + ); + expect(datasourcePlugin?.spec.display.name).toBe('InfluxDB Datasource'); + }); + }); + + describe('TimeSeriesQuery Plugin Configuration', () => { + it('should have TimeSeriesQuery plugin configured', () => { + const pluginModule = getPluginModule(); + const queryPlugin = pluginModule.spec.plugins.find( + (p: PluginSpec) => p.kind === 'TimeSeriesQuery' && p.spec.name === 'InfluxDBTimeSeriesQuery' + ); + expect(queryPlugin).toBeDefined(); + }); + + it('should have correct TimeSeriesQuery display name', () => { + const pluginModule = getPluginModule(); + const queryPlugin = pluginModule.spec.plugins.find( + (p: PluginSpec) => p.kind === 'TimeSeriesQuery' && p.spec.name === 'InfluxDBTimeSeriesQuery' + ); + expect(queryPlugin?.spec.display.name).toBe('InfluxDB Time Series Query'); + }); + }); +}); diff --git a/influxdb/src/test/ModuleFederationExports.test.ts b/influxdb/src/test/ModuleFederationExports.test.ts new file mode 100644 index 00000000..47c8f562 --- /dev/null +++ b/influxdb/src/test/ModuleFederationExports.test.ts @@ -0,0 +1,86 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as InfluxDBDatasourceModule from '../datasource/influxdb/InfluxDBDatasource'; +import * as InfluxDBTimeSeriesQueryModule from '../queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery'; + +describe('Module Federation Exports', () => { + describe('InfluxDBDatasource Module', () => { + it('should export default InfluxDBDatasource', () => { + expect(InfluxDBDatasourceModule.default).toBeDefined(); + expect(InfluxDBDatasourceModule.default).toHaveProperty('createClient'); + }); + + it('should export named InfluxDBDatasource', () => { + expect(InfluxDBDatasourceModule.InfluxDBDatasource).toBeDefined(); + expect(InfluxDBDatasourceModule.InfluxDBDatasource).toEqual(InfluxDBDatasourceModule.default); + }); + + it('should have all required datasource plugin properties', () => { + const datasource = InfluxDBDatasourceModule.default; + expect(datasource).toHaveProperty('createClient'); + expect(datasource).toHaveProperty('createInitialOptions'); + expect(datasource).toHaveProperty('OptionsEditorComponent'); + }); + }); + + describe('InfluxDBTimeSeriesQuery Module', () => { + it('should export default InfluxDBTimeSeriesQuery', () => { + expect(InfluxDBTimeSeriesQueryModule.default).toBeDefined(); + expect(InfluxDBTimeSeriesQueryModule.default).toHaveProperty('getTimeSeriesData'); + }); + + it('should export named InfluxDBTimeSeriesQuery', () => { + expect(InfluxDBTimeSeriesQueryModule.InfluxDBTimeSeriesQuery).toBeDefined(); + expect(InfluxDBTimeSeriesQueryModule.InfluxDBTimeSeriesQuery).toEqual(InfluxDBTimeSeriesQueryModule.default); + }); + + it('should have all required query plugin properties', () => { + const query = InfluxDBTimeSeriesQueryModule.default; + expect(query).toHaveProperty('getTimeSeriesData'); + expect(query).toHaveProperty('createInitialOptions'); + expect(query).toHaveProperty('OptionsEditorComponent'); + }); + }); + + describe('Bootstrap Module', () => { + it('should export all required modules from bootstrap', async () => { + const bootstrap = await import('../bootstrap'); + expect(bootstrap.getPluginModule).toBeDefined(); + expect(bootstrap.InfluxDBDatasource).toBeDefined(); + expect(bootstrap.InfluxDBTimeSeriesQuery).toBeDefined(); + }); + + it('should export correct InfluxDBDatasource from bootstrap', async () => { + const bootstrap = await import('../bootstrap'); + const datasource = bootstrap.InfluxDBDatasource; + expect(datasource).toHaveProperty('createClient'); + expect(datasource).toHaveProperty('createInitialOptions'); + expect(datasource).toHaveProperty('OptionsEditorComponent'); + }); + + it('should export correct InfluxDBTimeSeriesQuery from bootstrap', async () => { + const bootstrap = await import('../bootstrap'); + expect(bootstrap.InfluxDBTimeSeriesQuery).toEqual(InfluxDBTimeSeriesQueryModule.InfluxDBTimeSeriesQuery); + }); + + it('should return valid PluginModule metadata', async () => { + const bootstrap = await import('../bootstrap'); + const pluginModule = bootstrap.getPluginModule(); + expect(pluginModule.kind).toBe('PluginModule'); + expect(pluginModule.metadata).toHaveProperty('name'); + expect(pluginModule.metadata).toHaveProperty('version'); + expect(pluginModule.spec).toHaveProperty('plugins'); + }); + }); +}); diff --git a/influxdb/src/test/SchemaValidation.test.ts b/influxdb/src/test/SchemaValidation.test.ts new file mode 100644 index 00000000..453cd33d --- /dev/null +++ b/influxdb/src/test/SchemaValidation.test.ts @@ -0,0 +1,118 @@ +// Copyright The Perses Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import * as fs from 'fs'; +import * as path from 'path'; + +interface PluginSpec { + kind: string; + spec: { + name: string; + display: { + name: string; + }; + }; +} + +describe('Plugin Schema Validation', () => { + describe('Datasource CUE Schema', () => { + it('should have correct CUE schema file', () => { + const schemaPath = path.join(__dirname, '../../schemas/datasource/influxdb.cue'); + expect(fs.existsSync(schemaPath)).toBe(true); + }); + + it('should contain #kind definition', () => { + const schemaPath = path.join(__dirname, '../../schemas/datasource/influxdb.cue'); + const content = fs.readFileSync(schemaPath, 'utf-8'); + expect(content).toContain('#kind: "InfluxDBDatasource"'); + }); + + it('should contain kind assignment', () => { + const schemaPath = path.join(__dirname, '../../schemas/datasource/influxdb.cue'); + const content = fs.readFileSync(schemaPath, 'utf-8'); + expect(content).toContain('kind: #kind'); + }); + + it('should contain #selector definition', () => { + const schemaPath = path.join(__dirname, '../../schemas/datasource/influxdb.cue'); + const content = fs.readFileSync(schemaPath, 'utf-8'); + expect(content).toContain('#selector'); + expect(content).toContain('common.#datasourceSelector'); + expect(content).toContain('_kind: #kind'); + }); + + it('should have valid unified v1 and v3 configuration', () => { + const schemaPath = path.join(__dirname, '../../schemas/datasource/influxdb.cue'); + const content = fs.readFileSync(schemaPath, 'utf-8'); + // Version selector + expect(content).toContain('version:'); + // Connection type (proxy or direct) + expect(content).toContain('#directUrl'); + expect(content).toContain('#proxy'); + // Auth via secret reference + expect(content).toContain('auth?: string'); + // V1 specific fields + expect(content).toContain('database?: string'); + // V3 specific fields + expect(content).toContain('organization?: string'); + expect(content).toContain('bucket?: string'); + // Import common proxy patterns + expect(content).toContain('commonProxy'); + }); + }); + + describe('Plugin Registration', () => { + it('should have InfluxDBDatasource in package.json', () => { + const packageJsonPath = path.join(__dirname, '../../package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + expect(packageJson.perses).toBeDefined(); + expect(packageJson.perses.plugins).toBeDefined(); + + const datasourcePlugin = packageJson.perses.plugins.find( + (p: PluginSpec) => p.kind === 'Datasource' && p.spec.name === 'InfluxDBDatasource' + ); + expect(datasourcePlugin).toBeDefined(); + }); + + it('should have InfluxDBTimeSeriesQuery in package.json', () => { + const packageJsonPath = path.join(__dirname, '../../package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + + const queryPlugin = packageJson.perses.plugins.find( + (p: PluginSpec) => p.kind === 'TimeSeriesQuery' && p.spec.name === 'InfluxDBTimeSeriesQuery' + ); + expect(queryPlugin).toBeDefined(); + }); + }); + + describe('Plugin Module Files', () => { + it('InfluxDBDatasource.ts should have valid TypeScript', () => { + const datasourcePath = path.join(__dirname, '../datasource/influxdb/InfluxDBDatasource.ts'); + const content = fs.readFileSync(datasourcePath, 'utf-8'); + expect(content).toContain('export const InfluxDBDatasource'); + expect(content).toContain('version'); + expect(content).toContain('queryV1'); + expect(content).toContain('queryV3SQL'); + expect(content).toContain('createClient'); + }); + + it('InfluxDBTimeSeriesQuery.ts should have valid TypeScript', () => { + const queryPath = path.join(__dirname, '../queries/influxdb-time-series-query/InfluxDBTimeSeriesQuery.ts'); + const content = fs.readFileSync(queryPath, 'utf-8'); + expect(content).toContain('export const InfluxDBTimeSeriesQuery'); + expect(content).toContain('export default InfluxDBTimeSeriesQuery'); + expect(content).toContain('getTimeSeriesData'); + expect(content).toContain('OptionsEditorComponent'); + }); + }); +}); diff --git a/influxdb/test-data/README.md b/influxdb/test-data/README.md new file mode 100644 index 00000000..b075e397 --- /dev/null +++ b/influxdb/test-data/README.md @@ -0,0 +1,253 @@ +# InfluxDB Plugin Test Data Setup + +This directory contains tools to set up a complete InfluxDB testing environment for the Perses InfluxDB plugin. + +## Contents + +- **generate-influxdb-testdata.py** - Python script to generate realistic test data using InfluxDB HTTP API +- **start-influxdb-testenv.sh** - Shell script to start InfluxDB containers +- **docker-compose.influxdb-test.yml** - Docker Compose configuration for InfluxDB V1 and V3 +- **influxdb-v1/influxdb-v1-init.sh** - Initialization script for InfluxDB V1 +- **influxdb-v3/influxdb-v3-init.sh** - Initialization script for InfluxDB V3 + +## Quick Start + +### 1. Start InfluxDB Services + +```bash +./start-influxdb-testenv.sh +``` + +This will: +- Start InfluxDB V1 (port 8086) +- Start InfluxDB V3 (port 8087) +- Create test databases +- Wait for services to be healthy + +### 2. Generate Test Data + +```bash +python3 generate-influxdb-testdata.py +``` + +This generates realistic time-series data: +- **CPU metrics** - 4 hosts × 4 cores × 120 minutes +- **Memory metrics** - 4 hosts × 120 minutes +- **Disk metrics** - 4 hosts × 3 devices × 120 minutes +- **Network metrics** - 4 hosts × 2 interfaces × 120 minutes +- **Temperature metrics** - 4 hosts × 120 minutes + +Total: ~50,000 data points covering the last 2 hours + +### 3. Configure Perses + +Create a Global Datasource in Perses: + +```yaml +kind: GlobalDatasource +metadata: + name: influxdb-v1 +spec: + default: false + plugin: + kind: InfluxDBDatasource + spec: + version: v1 + directUrl: http://localhost:8086 + database: testdb +``` + +### 4. Create Queries + +Example InfluxQL queries: + +```sql +-- Get CPU usage for a specific host +SELECT * FROM cpu WHERE host='server01' AND time > now() - 1h + +-- Get average memory usage per host +SELECT mean(value) as avg_memory FROM memory WHERE time > now() - 1h GROUP BY host + +-- Get disk usage with region filtering +SELECT * FROM disk WHERE region='us-west' AND time > now() - 1h + +-- Get network metrics +SELECT bytes_in, bytes_out FROM network WHERE host='server02' AND time > now() - 1h +``` + +## Architecture + +### InfluxDB V1 +- **Host**: localhost +- **Port**: 8086 +- **Database**: testdb +- **Authentication**: None (default) +- **HTTP Endpoint**: `/query` + +### InfluxDB V3 +- **Host**: localhost +- **Port**: 8087 +- **Organization**: testorg +- **Bucket**: testbucket +- **Token**: test-token-12345 +- **HTTP Endpoint**: `/api/v2/query` + +## Data Generation Details + +The `generate-influxdb-testdata.py` script: + +1. **Uses HTTP API**: Sends data via POST to `/write?db=database` endpoint +2. **Line Protocol Format**: Uses InfluxDB line protocol for efficiency +3. **Batch Insertion**: Inserts 1000 lines per HTTP request +4. **Realistic Data**: + - CPU: 20-50% with occasional spikes + - Memory: 4-8 GB with garbage collection patterns + - Disk: 1-10 TB with slow growth + - Network: Variable bytes in/out with traffic spikes + - Temperature: 20-25°C with natural drift + +### Measurements + +**cpu** +- Tags: host (server01-04), region (us-west, us-east, eu-west), core (0-3) +- Fields: value (0-100%) + +**memory** +- Tags: host (server01-04), region +- Fields: value (1024-8192 MB) + +**disk** +- Tags: host, region, device (sda, sdb, sdc) +- Fields: value (0-10000 GB) + +**network** +- Tags: host, region, interface (eth0, eth1) +- Fields: bytes_in, bytes_out + +**temperature** +- Tags: host, room (rack01, rack02, rack03) +- Fields: celsius (15-35°C) + +## Useful Commands + +### View Available Measurements +```bash +curl 'http://localhost:8086/query?db=testdb&q=SHOW+MEASUREMENTS' +``` + +### Query Data via HTTP +```bash +curl 'http://localhost:8086/query?db=testdb&q=SELECT+*+FROM+cpu+WHERE+host=%27server01%27+LIMIT+10' +``` + +### Insert Custom Data +```bash +curl -X POST 'http://localhost:8086/write?db=testdb' \ + --data-binary 'cpu,host=myhost,region=us-west,core=0 value=45.5 1645000000000000000' +``` + +### View Container Logs +```bash +docker-compose -f docker-compose.influxdb-test.yml logs -f influxdb-v1 +``` + +### Stop Services +```bash +docker-compose -f docker-compose.influxdb-test.yml down +``` + +### Remove All Data and Start Fresh +```bash +docker-compose -f docker-compose.influxdb-test.yml down -v +./start-influxdb-testenv.sh +python3 generate-influxdb-testdata.py +``` + +## Requirements + +- Docker and Docker Compose +- Python 3.6+ +- curl (optional, for manual testing) + +## Troubleshooting + +### Script Fails to Connect + +If you get "Error connecting to InfluxDB", make sure: +1. InfluxDB is running: `docker-compose ps` +2. Port 8086 is accessible: `curl http://localhost:8086/ping` +3. Give the container time to start (up to 30 seconds) + +### No Data Generated + +Check that: +1. Database exists: `curl 'http://localhost:8086/query?db=testdb&q=SHOW+DATABASES'` +2. Server is responding to writes: `curl -X POST 'http://localhost:8086/write?db=testdb' --data 'test,host=test value=1'` + +### High Memory Usage + +If containers use too much memory: +1. Reduce the `minutes` parameter in the script (default: 120) +2. Reduce `batch_size` if insert fails +3. Delete old data and regenerate + +## Integration with Perses Development + +1. Start InfluxDB test environment: + ```bash + cd plugins/influxdb/test-data + ./start-influxdb-testenv.sh + ``` + +2. In another terminal, start Perses: + ```bash + npm run dev + ``` + +3. Create a Global Datasource pointing to `http://localhost:8086` + +4. Test queries and explorer functionality + +## Advanced Usage + +### Generate More Data + +Edit the script to change: +```python +minutes = 120 # Change this to generate more/less data +``` + +### Custom Metrics + +Modify the generation functions: +```python +def generate_custom_metrics(minutes=60): + """Generate your custom metrics""" + lines = [] + base_time = datetime.now() - timedelta(minutes=minutes) + + for minute in range(minutes): + timestamp = int((base_time + timedelta(minutes=minute)).timestamp() * 1e9) + # Your metric generation logic + line = f'your_measurement,tag1=value1 field1=value {timestamp}' + lines.append(line) + + return lines +``` + +Then add it to `main()`: +```python +all_lines.extend(generate_custom_metrics(minutes)) +``` + +## Notes + +- The HTTP API approach is more reliable than CLI commands +- Data is inserted in batches for better performance +- Each run generates new timestamps, so you can run the script multiple times to add more data +- The test data uses realistic patterns (CPU spikes, memory GC, disk growth) + +For more information on InfluxDB HTTP API: +- [InfluxDB V1 Write API](https://docs.influxdata.com/influxdb/v1.8/guides/write_data/) +- [InfluxDB Line Protocol](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol/) + diff --git a/influxdb/test-data/docker-compose.influxdb-test.yml b/influxdb/test-data/docker-compose.influxdb-test.yml new file mode 100644 index 00000000..0b8c8d75 --- /dev/null +++ b/influxdb/test-data/docker-compose.influxdb-test.yml @@ -0,0 +1,54 @@ +version: '3.8' + +services: + # InfluxDB V1 + influxdb-v1: + image: influxdb:1.8.10 + container_name: influxdb-v1 + ports: + - "8086:8086" + environment: + INFLUXDB_DB: testdb + INFLUXDB_ADMIN_USER: admin + INFLUXDB_ADMIN_PASSWORD: password + volumes: + - influxdb-v1-data:/var/lib/influxdb + - ./influxdb-v1-init.sh:/docker-entrypoint-initdb.d/init.sh + networks: + - perses-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8086/ping"] + interval: 10s + timeout: 5s + retries: 5 + + # InfluxDB V3 (using Cloud image as V3 standalone is not in Docker Hub) + influxdb-v3: + image: influxdb:latest + container_name: influxdb-v3 + ports: + - "8087:8086" + environment: + INFLUXDB_TOKEN: test-token-12345 + INFLUXDB_BUCKET: testbucket + INFLUXDB_ORG: testorg + INFLUXDB_ADMIN_USER: admin + INFLUXDB_ADMIN_PASSWORD: password + volumes: + - influxdb-v3-data:/var/lib/influxdb2 + networks: + - perses-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8086/health"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + influxdb-v1-data: + influxdb-v3-data: + +networks: + perses-network: + driver: bridge + diff --git a/influxdb/test-data/generate-influxdb-testdata.py b/influxdb/test-data/generate-influxdb-testdata.py new file mode 100755 index 00000000..f918e4f6 --- /dev/null +++ b/influxdb/test-data/generate-influxdb-testdata.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +""" +Generate realistic time-series test data for InfluxDB V1 +This script creates sample metrics that are useful for testing the Perses InfluxDB plugin +Uses InfluxDB HTTP API instead of CLI +""" + +import time +import math +import random +from datetime import datetime, timedelta +import urllib.request +import urllib.error +import sys + +def insert_data(host, port, database, lines): + """Insert data into InfluxDB V1 using HTTP line protocol""" + url = f"http://{host}:{port}/write?db={database}" + + # Prepare line protocol data + data = '\n'.join(lines).encode('utf-8') + + try: + request = urllib.request.Request(url, data=data, method='POST') + request.add_header('Content-Type', 'text/plain') + + with urllib.request.urlopen(request) as response: + status = response.status + if status != 204: + print(f"Warning: Expected 204 status, got {status}", file=sys.stderr) + + except urllib.error.HTTPError as e: + print(f"Error inserting data: HTTP {e.code}", file=sys.stderr) + print(f"Response: {e.read().decode('utf-8')}", file=sys.stderr) + sys.exit(1) + except urllib.error.URLError as e: + print(f"Error connecting to InfluxDB: {e.reason}", file=sys.stderr) + sys.exit(1) + +def generate_cpu_metrics(minutes=60): + """Generate realistic CPU usage metrics""" + lines = [] + base_time = datetime.now() - timedelta(minutes=minutes) + + hosts = ["server01", "server02", "server03", "server04"] + regions = ["us-west", "us-east", "eu-west"] + + for minute in range(minutes): + timestamp = int((base_time + timedelta(minutes=minute)).timestamp() * 1e9) + + for host in hosts: + for core in range(4): + # Simulate CPU with natural variations + base_load = random.uniform(20, 50) + noise = random.gauss(0, 5) + peak = 30 if random.random() < 0.1 else 0 # 10% chance of peak + + cpu_value = base_load + noise + peak + cpu_value = max(0, min(100, cpu_value)) # Clamp between 0-100 + + region = regions[hash(host) % len(regions)] + line = f'cpu,host={host},region={region},core={core} value={cpu_value:.2f} {timestamp}' + lines.append(line) + + return lines + +def generate_memory_metrics(minutes=60): + """Generate realistic memory usage metrics""" + lines = [] + base_time = datetime.now() - timedelta(minutes=minutes) + + hosts = ["server01", "server02", "server03", "server04"] + regions = ["us-west", "us-east", "eu-west"] + + # Initialize baseline memory per host + memory_state = {host: random.uniform(4000, 6000) for host in hosts} + + for minute in range(minutes): + timestamp = int((base_time + timedelta(minutes=minute)).timestamp() * 1e9) + + for host in hosts: + # Memory tends to grow slowly then drop (garbage collection) + if random.random() < 0.1: # 10% chance of GC + memory_state[host] -= random.uniform(500, 2000) + else: + memory_state[host] += random.uniform(0, 200) + + memory_state[host] = max(1024, min(8192, memory_state[host])) + + region = regions[hash(host) % len(regions)] + line = f'memory,host={host},region={region} value={memory_state[host]:.0f} {timestamp}' + lines.append(line) + + return lines + +def generate_disk_metrics(minutes=60): + """Generate realistic disk usage metrics""" + lines = [] + base_time = datetime.now() - timedelta(minutes=minutes) + + hosts = ["server01", "server02", "server03", "server04"] + devices = ["sda", "sdb", "sdc"] + regions = ["us-west", "us-east", "eu-west"] + + disk_state = {} + for host in hosts: + for device in devices: + disk_state[f"{host}_{device}"] = random.uniform(1000, 5000) + + for minute in range(minutes): + timestamp = int((base_time + timedelta(minutes=minute)).timestamp() * 1e9) + + for host in hosts: + for device in devices: + key = f"{host}_{device}" + # Disk usage slowly increases + disk_state[key] += random.uniform(0, 100) + disk_state[key] = max(0, min(10000, disk_state[key])) + + region = regions[hash(host) % len(regions)] + line = f'disk,host={host},region={region},device={device} value={disk_state[key]:.0f} {timestamp}' + lines.append(line) + + return lines + +def generate_network_metrics(minutes=60): + """Generate realistic network metrics""" + lines = [] + base_time = datetime.now() - timedelta(minutes=minutes) + + hosts = ["server01", "server02", "server03", "server04"] + interfaces = ["eth0", "eth1"] + regions = ["us-west", "us-east", "eu-west"] + + for minute in range(minutes): + timestamp = int((base_time + timedelta(minutes=minute)).timestamp() * 1e9) + + for host in hosts: + for interface in interfaces: + # Network traffic patterns + base_in = random.uniform(100, 1000) + base_out = random.uniform(50, 500) + + # Occasional spikes + if random.random() < 0.05: + base_in *= random.uniform(2, 5) + base_out *= random.uniform(2, 5) + + region = regions[hash(host) % len(regions)] + line = f'network,host={host},region={region},interface={interface} bytes_in={base_in:.0f},bytes_out={base_out:.0f} {timestamp}' + lines.append(line) + + return lines + +def generate_temperature_metrics(minutes=60): + """Generate realistic temperature metrics""" + lines = [] + base_time = datetime.now() - timedelta(minutes=minutes) + + hosts = ["server01", "server02", "server03", "server04"] + rooms = ["rack01", "rack02", "rack03"] + + temp_state = {} + for host in hosts: + temp_state[host] = random.uniform(20, 25) + + for minute in range(minutes): + timestamp = int((base_time + timedelta(minutes=minute)).timestamp() * 1e9) + + for host in hosts: + # Temperature drifts slowly + temp_state[host] += random.gauss(0, 0.2) + temp_state[host] = max(15, min(35, temp_state[host])) + + room = rooms[hash(host) % len(rooms)] + line = f'temperature,host={host},room={room} celsius={temp_state[host]:.2f} {timestamp}' + lines.append(line) + + return lines + +def main(): + host = "localhost" + port = 8086 + database = "testdb" + minutes = 120 # Generate 2 hours of data + batch_size = 1000 # Insert 1000 lines per request + + print("Generating InfluxDB V1 test data...") + print(f"Host: {host}:{port}, Database: {database}") + print(f"Time window: Last {minutes} minutes") + print(f"Batch size: {batch_size} lines per request") + print() + + all_lines = [] + + print("Generating CPU metrics...", end=" ", flush=True) + cpu_lines = generate_cpu_metrics(minutes) + all_lines.extend(cpu_lines) + print(f"✓ ({len(cpu_lines)} points)") + + print("Generating memory metrics...", end=" ", flush=True) + memory_lines = generate_memory_metrics(minutes) + all_lines.extend(memory_lines) + print(f"✓ ({len(memory_lines)} points)") + + print("Generating disk metrics...", end=" ", flush=True) + disk_lines = generate_disk_metrics(minutes) + all_lines.extend(disk_lines) + print(f"✓ ({len(disk_lines)} points)") + + print("Generating network metrics...", end=" ", flush=True) + network_lines = generate_network_metrics(minutes) + all_lines.extend(network_lines) + print(f"✓ ({len(network_lines)} points)") + + print("Generating temperature metrics...", end=" ", flush=True) + temp_lines = generate_temperature_metrics(minutes) + all_lines.extend(temp_lines) + print(f"✓ ({len(temp_lines)} points)") + + print() + print(f"Total data points: {len(all_lines)}") + print() + + # Insert data in batches + print("Inserting data into InfluxDB...") + total_batches = (len(all_lines) + batch_size - 1) // batch_size + + for batch_num in range(total_batches): + start_idx = batch_num * batch_size + end_idx = min(start_idx + batch_size, len(all_lines)) + batch_lines = all_lines[start_idx:end_idx] + + print(f" Batch {batch_num + 1}/{total_batches} ({len(batch_lines)} lines)...", end=" ", flush=True) + try: + insert_data(host, port, database, batch_lines) + print("✓") + except SystemExit: + print("✗") + raise + + print() + print("Test data generation complete!") + print() + print("Available measurements:") + print(" - cpu (with tags: host, region, core)") + print(" - memory (with tags: host, region)") + print(" - disk (with tags: host, region, device)") + print(" - network (with tags: host, region, interface, fields: bytes_in, bytes_out)") + print(" - temperature (with tags: host, room)") + print() + print("Sample queries:") + print(" SELECT * FROM cpu WHERE host='server01' LIMIT 100") + print(" SELECT mean(value) FROM memory GROUP BY host") + print(" SELECT * FROM disk WHERE region='us-west'") + +if __name__ == "__main__": + main() + diff --git a/influxdb/test-data/influxdb-v1/influxdb-v1-init.sh b/influxdb/test-data/influxdb-v1/influxdb-v1-init.sh new file mode 100755 index 00000000..ab405ed6 --- /dev/null +++ b/influxdb/test-data/influxdb-v1/influxdb-v1-init.sh @@ -0,0 +1,55 @@ +#!/bin/bash +set -e + +echo "Initializing InfluxDB V1 with test data..." + +# Create database +influx -execute "CREATE DATABASE testdb" + +# Create retention policy +influx -execute "CREATE RETENTION POLICY \"30days\" ON \"testdb\" DURATION 30d REPLICATION 1 DEFAULT" + +# Create measurement with sample data +influx -execute " +INSERT INTO testdb cpu,host=server01,region=us-west value=0.64 1434067467000000000 +INSERT INTO testdb cpu,host=server01,region=us-west value=0.60 1434067468000000000 +INSERT INTO testdb cpu,host=server02,region=us-west value=0.50 1434067469000000000 +INSERT INTO testdb cpu,host=server03,region=us-east value=0.75 1434067470000000000 +" + +# Create memory measurement +influx -execute " +INSERT INTO testdb memory,host=server01,region=us-west value=1024 1434067467000000000 +INSERT INTO testdb memory,host=server01,region=us-west value=2048 1434067468000000000 +INSERT INTO testdb memory,host=server02,region=us-west value=1536 1434067469000000000 +INSERT INTO testdb memory,host=server03,region=us-east value=2560 1434067470000000000 +" + +# Create disk measurement +influx -execute " +INSERT INTO testdb disk,host=server01,region=us-west,device=sda value=100 1434067467000000000 +INSERT INTO testdb disk,host=server01,region=us-west,device=sdb value=200 1434067468000000000 +INSERT INTO testdb disk,host=server02,region=us-west,device=sda value=150 1434067469000000000 +INSERT INTO testdb disk,host=server03,region=us-east,device=sda value=250 1434067470000000000 +" + +# Create network measurement +influx -execute " +INSERT INTO testdb network,host=server01,region=us-west,interface=eth0 bytes_in=1000,bytes_out=2000 1434067467000000000 +INSERT INTO testdb network,host=server01,region=us-west,interface=eth1 bytes_in=1500,bytes_out=2500 1434067468000000000 +INSERT INTO testdb network,host=server02,region=us-west,interface=eth0 bytes_in=800,bytes_out=1600 1434067469000000000 +INSERT INTO testdb network,host=server03,region=us-east,interface=eth0 bytes_in=1200,bytes_out=2400 1434067470000000000 +" + +# Create temperature measurement (for time-series demo) +influx -execute " +INSERT INTO testdb temperature,host=server01,room=rack01 celsius=22.5 1434067467000000000 +INSERT INTO testdb temperature,host=server01,room=rack01 celsius=22.6 1434067468000000000 +INSERT INTO testdb temperature,host=server02,room=rack02 celsius=23.0 1434067469000000000 +INSERT INTO testdb temperature,host=server03,room=rack01 celsius=21.8 1434067470000000000 +INSERT INTO testdb temperature,host=server01,room=rack01 celsius=22.7 1434067471000000000 +INSERT INTO testdb temperature,host=server02,room=rack02 celsius=23.1 1434067472000000000 +" + +echo "InfluxDB V1 initialization complete!" + diff --git a/influxdb/test-data/influxdb-v3/influxdb-v3-init.sh b/influxdb/test-data/influxdb-v3/influxdb-v3-init.sh new file mode 100644 index 00000000..01c0d774 --- /dev/null +++ b/influxdb/test-data/influxdb-v3/influxdb-v3-init.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -e + +echo "Initializing InfluxDB V3 with test data..." + +# Note: For InfluxDB V3, we'll use the newer API approach +# V3 uses different data structure but compatible query API + +# Export token for influx CLI +export INFLUX_TOKEN="test-token-12345" +export INFLUX_ORG="testorg" +export INFLUX_BUCKET="testbucket" +export INFLUX_URL="http://localhost:8087" + +# Wait for InfluxDB to be ready +echo "Waiting for InfluxDB V3 to be ready..." +until curl -s -H "Authorization: Token $INFLUX_TOKEN" "$INFLUX_URL/health" > /dev/null 2>&1; do + echo "InfluxDB V3 not ready yet, waiting..." + sleep 5 +done + +echo "InfluxDB V3 is ready!" + +# Create bucket if not exists (usually created during init) +# For now we use the default bucket created by environment variables + +# Insert test data using line protocol +# CPU metrics +curl -X POST "$INFLUX_URL/api/v2/write?org=$INFLUX_ORG&bucket=$INFLUX_BUCKET" \ + -H "Authorization: Token $INFLUX_TOKEN" \ + -H "Content-Type: text/plain; charset=utf-8" \ + --data-binary @- << 'EOF' +cpu,host=server01,region=us-west,core=0 value=45.5 1434067467000000000 +cpu,host=server01,region=us-west,core=1 value=42.1 1434067468000000000 +cpu,host=server02,region=us-west,core=0 value=38.9 1434067469000000000 +cpu,host=server03,region=us-east,core=0 value=55.2 1434067470000000000 +EOF + +# Memory metrics +curl -X POST "$INFLUX_URL/api/v2/write?org=$INFLUX_ORG&bucket=$INFLUX_BUCKET" \ + -H "Authorization: Token $INFLUX_TOKEN" \ + -H "Content-Type: text/plain; charset=utf-8" \ + --data-binary @- << 'EOF' +memory,host=server01,region=us-west value=5120 1434067467000000000 +memory,host=server01,region=us-west value=5242 1434067468000000000 +memory,host=server02,region=us-west value=4864 1434067469000000000 +memory,host=server03,region=us-east value=6144 1434067470000000000 +EOF + +# Disk metrics +curl -X POST "$INFLUX_URL/api/v2/write?org=$INFLUX_ORG&bucket=$INFLUX_BUCKET" \ + -H "Authorization: Token $INFLUX_TOKEN" \ + -H "Content-Type: text/plain; charset=utf-8" \ + --data-binary @- << 'EOF' +disk,host=server01,region=us-west,device=sda value=3500 1434067467000000000 +disk,host=server01,region=us-west,device=sdb value=2800 1434067468000000000 +disk,host=server02,region=us-west,device=sda value=4200 1434067469000000000 +disk,host=server03,region=us-east,device=sda value=5600 1434067470000000000 +EOF + +# Network metrics +curl -X POST "$INFLUX_URL/api/v2/write?org=$INFLUX_ORG&bucket=$INFLUX_BUCKET" \ + -H "Authorization: Token $INFLUX_TOKEN" \ + -H "Content-Type: text/plain; charset=utf-8" \ + --data-binary @- << 'EOF' +network,host=server01,region=us-west,interface=eth0 bytes_in=1250,bytes_out=2150 1434067467000000000 +network,host=server01,region=us-west,interface=eth1 bytes_in=1875,bytes_out=3125 1434067468000000000 +network,host=server02,region=us-west,interface=eth0 bytes_in=1000,bytes_out=2000 1434067469000000000 +network,host=server03,region=us-east,interface=eth0 bytes_in=1500,bytes_out=3000 1434067470000000000 +EOF + +# Temperature metrics +curl -X POST "$INFLUX_URL/api/v2/write?org=$INFLUX_ORG&bucket=$INFLUX_BUCKET" \ + -H "Authorization: Token $INFLUX_TOKEN" \ + -H "Content-Type: text/plain; charset=utf-8" \ + --data-binary @- << 'EOF' +temperature,host=server01,room=rack01 celsius=22.5 1434067467000000000 +temperature,host=server01,room=rack01 celsius=22.6 1434067468000000000 +temperature,host=server02,room=rack02 celsius=23.2 1434067469000000000 +temperature,host=server03,room=rack01 celsius=21.9 1434067470000000000 +EOF + +echo "InfluxDB V3 initialization complete!" + diff --git a/influxdb/test-data/start-influxdb-testenv.sh b/influxdb/test-data/start-influxdb-testenv.sh new file mode 100755 index 00000000..7052e11d --- /dev/null +++ b/influxdb/test-data/start-influxdb-testenv.sh @@ -0,0 +1,161 @@ +#!/bin/bash + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ InfluxDB Test Setup for Perses Plugin Testing ║${NC}" +echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" +echo "" + +# Check if docker-compose is available +if ! command -v docker-compose &> /dev/null && ! command -v docker &> /dev/null; then + echo -e "${RED}❌ Error: docker or docker-compose is not installed${NC}" + exit 1 +fi + +# Determine docker-compose command +if docker compose version &> /dev/null; then + DOCKER_COMPOSE="docker compose" +else + DOCKER_COMPOSE="docker-compose" +fi + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +echo -e "${YELLOW}1. Starting InfluxDB V1 and V3 containers...${NC}" +$DOCKER_COMPOSE -f "$SCRIPT_DIR/docker-compose.influxdb-test.yml" up -d + +echo -e "${YELLOW}2. Waiting for services to be healthy...${NC}" +echo " - InfluxDB V1 (port 8086)" +echo " - InfluxDB V3 (port 8087)" +echo " - Grafana (port 3000)" +echo "" + +# Wait for InfluxDB V1 +echo -n " Checking InfluxDB V1... " +for i in {1..30}; do + if curl -s http://localhost:8086/ping > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" + break + fi + echo -n "." + sleep 2 +done + +# Wait for InfluxDB V3 +echo -n " Checking InfluxDB V3... " +for i in {1..30}; do + if curl -s http://localhost:8087/health > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC}" + break + fi + echo -n "." + sleep 2 +done + +echo "" +echo -e "${YELLOW}3. Initializing test databases...${NC}" + +# Initialize InfluxDB V1 +echo " Initializing InfluxDB V1..." +docker exec influxdb-v1 influx -execute "CREATE DATABASE testdb" 2>/dev/null || true +docker exec influxdb-v1 influx -database testdb -execute "SHOW MEASUREMENTS" > /dev/null + +echo -e " ${GREEN}✓${NC} InfluxDB V1 ready" + +# Initialize InfluxDB V3 +echo " Initializing InfluxDB V3..." +sleep 5 # Give V3 more time to start +echo -e " ${GREEN}✓${NC} InfluxDB V3 ready" + +echo "" +echo -e "${YELLOW}4. Generating test data...${NC}" + +# Check if Python is available for advanced data generation +if command -v python3 &> /dev/null; then + echo " Running Python data generator..." + python3 "$SCRIPT_DIR/generate-influxdb-testdata.py" || echo " Note: Python generator had issues, but basic data is loaded" +else + echo " Python3 not available, using basic test data" +fi + +echo "" +echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}" +echo -e "${GREEN}║ ✓ Setup Complete! ║${NC}" +echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}" +echo "" + +echo -e "${BLUE}Available InfluxDB Instances:${NC}" +echo "" +echo -e " ${YELLOW}InfluxDB V1${NC}" +echo " URL: http://localhost:8086" +echo " Database: testdb" +echo " Admin: admin / password" +echo " Measurements:" +echo " - cpu (tags: host, region, core)" +echo " - memory (tags: host, region)" +echo " - disk (tags: host, region, device)" +echo " - network (tags: host, region, interface)" +echo " - temperature (tags: host, room)" +echo "" + +echo -e " ${YELLOW}InfluxDB V3${NC}" +echo " URL: http://localhost:8087" +echo " Org: testorg" +echo " Bucket: testbucket" +echo " Token: test-token-12345" +echo " Admin: admin / password" +echo "" + +echo -e " ${YELLOW}Grafana (Optional)${NC}" +echo " URL: http://localhost:3000" +echo " Admin: admin / admin" +echo "" + +echo -e "${BLUE}Testing with Perses:${NC}" +echo "" +echo "1. Start Perses development server" +echo "" +echo "2. Create a Global Datasource for InfluxDB V1:" +echo "" +echo " Kind: GlobalDatasource" +echo " Name: influxdb-v1" +echo " Plugin: InfluxDBDatasource" +echo " Spec:" +echo " directUrl: http://localhost:8086" +echo " database: testdb" +echo "" + +echo "3. Create a Query:" +echo "" +echo " Query: SELECT * FROM cpu WHERE time > now() - 1h" +echo " Or: SELECT * FROM memory WHERE time > now() - 1h" +echo "" + +echo -e "${BLUE}Useful Commands:${NC}" +echo "" +echo " View InfluxDB V1 data:" +echo " curl 'http://localhost:8086/query?db=testdb&q=SHOW MEASUREMENTS'" +echo "" +echo " List measurements:" +echo " influx -database testdb -execute 'SHOW MEASUREMENTS'" +echo "" +echo " Query data:" +echo " influx -database testdb -execute 'SELECT * FROM cpu LIMIT 10'" +echo "" +echo " Stop containers:" +echo " docker-compose -f $SCRIPT_DIR/docker-compose.influxdb-test.yml down" +echo "" +echo " View logs:" +echo " docker-compose -f $SCRIPT_DIR/docker-compose.influxdb-test.yml logs -f" +echo "" + +echo -e "${GREEN}Ready to test! Open Perses and configure datasource.${NC}" + diff --git a/influxdb/tsconfig.build.json b/influxdb/tsconfig.build.json new file mode 100644 index 00000000..09ca62b1 --- /dev/null +++ b/influxdb/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist/lib" + }, + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/**/*.stories.tsx"] +} diff --git a/influxdb/tsconfig.json b/influxdb/tsconfig.json new file mode 100644 index 00000000..98221344 --- /dev/null +++ b/influxdb/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist/lib", + "rootDir": "./src" + }, + "include": ["src"] +} diff --git a/package.json b/package.json index 323eca6d..06abc602 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "gaugechart", "heatmapchart", "histogramchart", + "influxdb", "logstable", "loki", "markdown",