From b9665662330c25702c562150dad9bf1e5c73d014 Mon Sep 17 00:00:00 2001 From: Spencer Torres Date: Tue, 5 Dec 2023 18:08:16 -0500 Subject: [PATCH 1/3] V4 Plugin Upgrades (#605) --- .gitignore | 1 + e2e/e2ek6.test.js | 70 +- package.json | 14 +- pkg/plugin/driver.go | 12 +- pkg/plugin/errors.go | 2 +- pkg/plugin/settings.go | 98 +- pkg/plugin/settings_test.go | 38 +- src/__mocks__/ConfigEditor.ts | 6 +- src/__mocks__/datasource.ts | 17 +- src/components/Divider.tsx | 2 +- src/components/FormatSelect.test.tsx | 11 - src/components/FormatSelect.tsx | 40 - src/components/QueryHeader.tsx | 60 - src/components/QueryTypeSwitcher.test.tsx | 46 - src/components/QueryTypeSwitcher.tsx | 94 - src/components/SQLEditor.tsx | 139 - ...{SQLEditor.test.tsx => SqlEditor.test.tsx} | 32 +- src/components/SqlEditor.tsx | 160 + .../DefaultDatabaseTableConfig.test.tsx | 37 + .../DefaultDatabaseTableConfig.tsx | 51 + .../configEditor/LabeledInput.test.tsx | 23 + src/components/configEditor/LabeledInput.tsx | 30 + .../configEditor/LogsConfig.test.tsx | 190 + src/components/configEditor/LogsConfig.tsx | 114 + .../configEditor/QuerySettingsConfig.test.tsx | 76 + .../configEditor/QuerySettingsConfig.tsx | 58 + .../configEditor/TracesConfig.test.tsx | 463 ++ src/components/configEditor/TracesConfig.tsx | 186 + src/components/editor.ts | 23 - .../queryBuilder/AggregateEditor.test.tsx | 61 + .../queryBuilder/AggregateEditor.tsx | 149 + .../queryBuilder/ColumnSelect.test.tsx | 63 + src/components/queryBuilder/ColumnSelect.tsx | 76 + .../queryBuilder/ColumnsEditor.test.tsx | 77 + src/components/queryBuilder/ColumnsEditor.tsx | 102 + .../queryBuilder/DatabaseSelect.tsx | 40 - .../queryBuilder/DatabaseTableSelect.test.tsx | 128 + .../queryBuilder/DatabaseTableSelect.tsx | 114 + .../queryBuilder/DurationUnitSelect.tsx | 41 + .../queryBuilder/EditorTypeSwitcher.test.tsx | 74 + .../queryBuilder/EditorTypeSwitcher.tsx | 107 + src/components/queryBuilder/Fields.test.tsx | 64 - src/components/queryBuilder/Fields.tsx | 79 - ...Filters.test.tsx => FilterEditor.test.tsx} | 58 +- .../{Filters.tsx => FilterEditor.tsx} | 142 +- src/components/queryBuilder/GroupBy.test.tsx | 10 - src/components/queryBuilder/GroupBy.tsx | 40 - .../queryBuilder/GroupByEditor.test.tsx | 37 + src/components/queryBuilder/GroupByEditor.tsx | 45 + src/components/queryBuilder/Limit.test.tsx | 10 - src/components/queryBuilder/Limit.tsx | 25 - .../queryBuilder/LimitEditor.test.tsx | 25 + src/components/queryBuilder/LimitEditor.tsx | 31 + src/components/queryBuilder/LogLevelField.tsx | 30 - src/components/queryBuilder/Metrics.test.tsx | 14 - src/components/queryBuilder/Metrics.tsx | 138 - .../queryBuilder/ModeEditor.test.tsx | 11 - src/components/queryBuilder/ModeEditor.tsx | 23 - .../queryBuilder/ModeSwitch.test.tsx | 64 + src/components/queryBuilder/ModeSwitch.tsx | 42 + src/components/queryBuilder/OrderBy.tsx | 159 - ...rderBy.test.tsx => OrderByEditor.test.tsx} | 249 +- src/components/queryBuilder/OrderByEditor.tsx | 168 + .../queryBuilder/OtelVersionSelect.test.tsx | 107 + .../queryBuilder/OtelVersionSelect.tsx | 60 + src/components/queryBuilder/Preview.tsx | 16 - .../queryBuilder/QueryBuilder.test.tsx | 15 +- src/components/queryBuilder/QueryBuilder.tsx | 321 +- .../queryBuilder/QueryTypeSwitcher.test.tsx | 44 + .../queryBuilder/QueryTypeSwitcher.tsx | 46 + .../{Preview.test.tsx => SqlPreview.test.tsx} | 6 +- src/components/queryBuilder/SqlPreview.tsx | 21 + src/components/queryBuilder/Switch.test.tsx | 54 + src/components/queryBuilder/Switch.tsx | 40 + .../queryBuilder/TableSelect.test.tsx | 17 - src/components/queryBuilder/TableSelect.tsx | 49 - .../queryBuilder/TimeField.test.tsx | 19 - src/components/queryBuilder/TimeField.tsx | 36 - .../{utils.spec.ts => utils.test.ts} | 156 +- src/components/queryBuilder/utils.ts | 123 +- .../queryBuilder/views/LogsQueryBuilder.tsx | 172 + .../queryBuilder/views/TableQueryBuilder.tsx | 91 + .../views/TimeSeriesQueryBuilder.tsx | 122 + .../queryBuilder/views/TraceQueryBuilder.tsx | 298 + .../views/logsQueryBuilderHooks.test.ts | 209 + .../views/logsQueryBuilderHooks.ts | 144 + .../views/timeSeriesQueryBuilderHooks.test.ts | 104 + .../views/timeSeriesQueryBuilderHooks.ts | 67 + .../views/traceQueryBuilderHooks.test.ts | 99 + .../views/traceQueryBuilderHooks.ts | 76 + src/data/CHDatasource.test.ts | 72 +- src/data/CHDatasource.ts | 198 +- src/data/adHocFilter.test.ts | 4 +- src/data/adHocFilter.ts | 3 +- src/data/columnFilters.test.ts | 71 + src/data/columnFilters.ts | 14 + src/data/migration.test.ts | 368 + src/data/migration.ts | 196 + src/data/sqlGenerator.test.ts | 218 + src/data/sqlGenerator.ts | 371 + src/data/utils.test.ts | 37 + src/data/utils.ts | 59 + src/hooks/useBuilderOptionChanges.test.ts | 26 + src/hooks/useBuilderOptionChanges.ts | 25 + src/hooks/useBuilderOptionsState.test.ts | 154 + src/hooks/useBuilderOptionsState.ts | 151 + src/hooks/useColumns.test.ts | 59 + src/hooks/useColumns.ts | 41 + src/hooks/useDatabases.test.ts | 29 + src/hooks/useDatabases.ts | 21 + src/hooks/useIsNewQuery.test.ts | 50 + src/hooks/useIsNewQuery.ts | 14 + src/hooks/useTables.test.ts | 42 + src/hooks/useTables.ts | 39 + src/labels.ts | 298 + src/module.ts | 3 +- src/otel.ts | 33 + src/plugin.json | 1 + src/selectors.ts | 36 +- src/styles.ts | 44 + src/tracking.test.ts | 58 +- src/tracking.ts | 25 +- src/types.ts | 272 - src/types/config.ts | 76 + src/types/queryBuilder.ts | 241 + src/types/sql.ts | 67 + src/utils/version.test.ts | 1 + src/utils/version.ts | 3 + src/views/CHConfigEditor.test.tsx | 24 +- src/views/CHConfigEditor.tsx | 295 +- src/views/CHConfigEditorHooks.test.ts | 64 + src/views/CHConfigEditorHooks.ts | 37 + src/views/CHQueryEditor.test.tsx | 4 +- src/views/CHQueryEditor.tsx | 158 +- yarn.lock | 6020 ++++++----------- 135 files changed, 10547 insertions(+), 6576 deletions(-) delete mode 100644 src/components/FormatSelect.test.tsx delete mode 100644 src/components/FormatSelect.tsx delete mode 100644 src/components/QueryHeader.tsx delete mode 100644 src/components/QueryTypeSwitcher.test.tsx delete mode 100644 src/components/QueryTypeSwitcher.tsx delete mode 100644 src/components/SQLEditor.tsx rename src/components/{SQLEditor.test.tsx => SqlEditor.test.tsx} (58%) create mode 100644 src/components/SqlEditor.tsx create mode 100644 src/components/configEditor/DefaultDatabaseTableConfig.test.tsx create mode 100644 src/components/configEditor/DefaultDatabaseTableConfig.tsx create mode 100644 src/components/configEditor/LabeledInput.test.tsx create mode 100644 src/components/configEditor/LabeledInput.tsx create mode 100644 src/components/configEditor/LogsConfig.test.tsx create mode 100644 src/components/configEditor/LogsConfig.tsx create mode 100644 src/components/configEditor/QuerySettingsConfig.test.tsx create mode 100644 src/components/configEditor/QuerySettingsConfig.tsx create mode 100644 src/components/configEditor/TracesConfig.test.tsx create mode 100644 src/components/configEditor/TracesConfig.tsx delete mode 100644 src/components/editor.ts create mode 100644 src/components/queryBuilder/AggregateEditor.test.tsx create mode 100644 src/components/queryBuilder/AggregateEditor.tsx create mode 100644 src/components/queryBuilder/ColumnSelect.test.tsx create mode 100644 src/components/queryBuilder/ColumnSelect.tsx create mode 100644 src/components/queryBuilder/ColumnsEditor.test.tsx create mode 100644 src/components/queryBuilder/ColumnsEditor.tsx delete mode 100644 src/components/queryBuilder/DatabaseSelect.tsx create mode 100644 src/components/queryBuilder/DatabaseTableSelect.test.tsx create mode 100644 src/components/queryBuilder/DatabaseTableSelect.tsx create mode 100644 src/components/queryBuilder/DurationUnitSelect.tsx create mode 100644 src/components/queryBuilder/EditorTypeSwitcher.test.tsx create mode 100644 src/components/queryBuilder/EditorTypeSwitcher.tsx delete mode 100644 src/components/queryBuilder/Fields.test.tsx delete mode 100644 src/components/queryBuilder/Fields.tsx rename src/components/queryBuilder/{Filters.test.tsx => FilterEditor.test.tsx} (87%) rename src/components/queryBuilder/{Filters.tsx => FilterEditor.tsx} (79%) delete mode 100644 src/components/queryBuilder/GroupBy.test.tsx delete mode 100644 src/components/queryBuilder/GroupBy.tsx create mode 100644 src/components/queryBuilder/GroupByEditor.test.tsx create mode 100644 src/components/queryBuilder/GroupByEditor.tsx delete mode 100644 src/components/queryBuilder/Limit.test.tsx delete mode 100644 src/components/queryBuilder/Limit.tsx create mode 100644 src/components/queryBuilder/LimitEditor.test.tsx create mode 100644 src/components/queryBuilder/LimitEditor.tsx delete mode 100644 src/components/queryBuilder/LogLevelField.tsx delete mode 100644 src/components/queryBuilder/Metrics.test.tsx delete mode 100644 src/components/queryBuilder/Metrics.tsx delete mode 100644 src/components/queryBuilder/ModeEditor.test.tsx delete mode 100644 src/components/queryBuilder/ModeEditor.tsx create mode 100644 src/components/queryBuilder/ModeSwitch.test.tsx create mode 100644 src/components/queryBuilder/ModeSwitch.tsx delete mode 100644 src/components/queryBuilder/OrderBy.tsx rename src/components/queryBuilder/{OrderBy.test.tsx => OrderByEditor.test.tsx} (55%) create mode 100644 src/components/queryBuilder/OrderByEditor.tsx create mode 100644 src/components/queryBuilder/OtelVersionSelect.test.tsx create mode 100644 src/components/queryBuilder/OtelVersionSelect.tsx delete mode 100644 src/components/queryBuilder/Preview.tsx create mode 100644 src/components/queryBuilder/QueryTypeSwitcher.test.tsx create mode 100644 src/components/queryBuilder/QueryTypeSwitcher.tsx rename src/components/queryBuilder/{Preview.test.tsx => SqlPreview.test.tsx} (58%) create mode 100644 src/components/queryBuilder/SqlPreview.tsx create mode 100644 src/components/queryBuilder/Switch.test.tsx create mode 100644 src/components/queryBuilder/Switch.tsx delete mode 100644 src/components/queryBuilder/TableSelect.test.tsx delete mode 100644 src/components/queryBuilder/TableSelect.tsx delete mode 100644 src/components/queryBuilder/TimeField.test.tsx delete mode 100644 src/components/queryBuilder/TimeField.tsx rename src/components/queryBuilder/{utils.spec.ts => utils.test.ts} (72%) create mode 100644 src/components/queryBuilder/views/LogsQueryBuilder.tsx create mode 100644 src/components/queryBuilder/views/TableQueryBuilder.tsx create mode 100644 src/components/queryBuilder/views/TimeSeriesQueryBuilder.tsx create mode 100644 src/components/queryBuilder/views/TraceQueryBuilder.tsx create mode 100644 src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts create mode 100644 src/components/queryBuilder/views/logsQueryBuilderHooks.ts create mode 100644 src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts create mode 100644 src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts create mode 100644 src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts create mode 100644 src/components/queryBuilder/views/traceQueryBuilderHooks.ts create mode 100644 src/data/columnFilters.test.ts create mode 100644 src/data/columnFilters.ts create mode 100644 src/data/migration.test.ts create mode 100644 src/data/migration.ts create mode 100644 src/data/sqlGenerator.test.ts create mode 100644 src/data/sqlGenerator.ts create mode 100644 src/data/utils.test.ts create mode 100644 src/data/utils.ts create mode 100644 src/hooks/useBuilderOptionChanges.test.ts create mode 100644 src/hooks/useBuilderOptionChanges.ts create mode 100644 src/hooks/useBuilderOptionsState.test.ts create mode 100644 src/hooks/useBuilderOptionsState.ts create mode 100644 src/hooks/useColumns.test.ts create mode 100644 src/hooks/useColumns.ts create mode 100644 src/hooks/useDatabases.test.ts create mode 100644 src/hooks/useDatabases.ts create mode 100644 src/hooks/useIsNewQuery.test.ts create mode 100644 src/hooks/useIsNewQuery.ts create mode 100644 src/hooks/useTables.test.ts create mode 100644 src/hooks/useTables.ts create mode 100644 src/labels.ts create mode 100644 src/otel.ts delete mode 100644 src/types.ts create mode 100644 src/types/config.ts create mode 100644 src/types/queryBuilder.ts create mode 100644 src/types/sql.ts create mode 100644 src/views/CHConfigEditorHooks.test.ts create mode 100644 src/views/CHConfigEditorHooks.ts diff --git a/.gitignore b/.gitignore index 456d18d8..11098e0e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ artifacts/ work/ ci/ e2e-results/ +test_summary.json # Editor .idea diff --git a/e2e/e2ek6.test.js b/e2e/e2ek6.test.js index 7a5fdca5..2834489a 100644 --- a/e2e/e2ek6.test.js +++ b/e2e/e2ek6.test.js @@ -134,43 +134,45 @@ export async function configurePanel(page) { let queryData = { "queries": [ - { - "datasource": { - "type": "grafana-clickhouse-datasource", - "uid": `${datasourceUID}` - }, - "refId": "A", - "queryType": "builder", - "rawSql": "SELECT \"schema_name\" FROM \"INFORMATION_SCHEMA\".\"SCHEMATA\" LIMIT 100", - "meta": { - "builderOptions": { - "mode": "list", - "fields": [], - "limit": 100 - }, - "timezone": "America/Denver" - }, - "format": 1, - "selectedFormat": 4, - "builderOptions": { - "mode": "list", - "fields": [ - "schema_name" - ], - "limit": 100, - "database": "INFORMATION_SCHEMA", - "table": "SCHEMATA", - "filters": [], - "orderBy": [] - }, - "datasourceId": 3568, - "intervalMs": 30000, - "maxDataPoints": 631 - } + { + "datasource": { + "type": "grafana-clickhouse-datasource", + "uid": `${datasourceUID}` + }, + "pluginVersion": "4.0.0", + "editorType": "builder", + "rawSql": "SELECT \"schema_name\" FROM \"information_schema\".\"schemata\" LIMIT 1000", + "builderOptions": { + "database": "information_schema", + "table": "schemata", + "queryType": "table", + "mode": "list", + "columns": [ + { + "name": "schema_name", + "type": "String", + "custom": false + } + ], + "meta": {}, + "limit": 1000, + "aggregates": [], + "groupBy": [], + "filters": [], + "orderBy": [] + }, + "format": 1, + "meta": { + "timezone": "America/New_York" + }, + "datasourceId": 1, + "intervalMs": 10000, + "maxDataPoints": 1920 + } ], "from": "1695121104422", "to": "1695142704422" - } + }; // ensures user is an admin to the org http.post(`http://admin:admin@${GRAFANA_HOST}:3000/api/user/using/1`, null); diff --git a/package.json b/package.json index f000152c..5248f67c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clickhouse-datasource", - "version": "3.3.0", + "version": "4.0.0-beta", "description": "Clickhouse Datasource", "engines": { "node": ">=16" @@ -28,10 +28,10 @@ "@grafana/eslint-config": "^6.0.0", "@grafana/tsconfig": "^1.2.0-rc1", "@swc/core": "1.3.75", - "@swc/helpers": "^0.5.0", + "@swc/helpers": "^0.5.3", "@swc/jest": "^0.2.26", "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^12.1.4", + "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.1", "@types/glob": "^8.0.0", "@types/jest": "^29.5.0", @@ -56,7 +56,7 @@ "swc-loader": "^0.2.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", - "typescript": "4.8.4", + "typescript": "^4.8.4", "webpack": "^5.86.0", "webpack-cli": "^5.1.4", "webpack-livereload-plugin": "^3.0.2" @@ -72,10 +72,10 @@ "@grafana/ui": "10.1.0", "js-sql-parser": "^1.5.0", "pgsql-ast-parser": "^11.1.0", - "react": "17.0.2", - "react-dom": "17.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", "react-router-dom": "^5.2.0", - "tslib": "2.5.3" + "tslib": "^2.5.3" }, "packageManager": "yarn@1.22.19" } diff --git a/pkg/plugin/driver.go b/pkg/plugin/driver.go index 5b3d452b..7ab93c54 100644 --- a/pkg/plugin/driver.go +++ b/pkg/plugin/driver.go @@ -35,7 +35,7 @@ type Clickhouse struct{} func getTLSConfig(settings Settings) (*tls.Config, error) { tlsConfig := &tls.Config{ InsecureSkipVerify: settings.InsecureSkipVerify, - ServerName: settings.Server, + ServerName: settings.Host, } if settings.TlsClientAuth || settings.TlsAuthWithCACert { if settings.TlsAuthWithCACert && len(settings.TlsCACert) > 0 { @@ -117,9 +117,9 @@ func (h *Clickhouse) Connect(config backend.DataSourceInstanceSettings, message InsecureSkipVerify: settings.InsecureSkipVerify, } } - t, err := strconv.Atoi(settings.Timeout) + t, err := strconv.Atoi(settings.DialTimeout) if err != nil { - return nil, errors.New(fmt.Sprintf("invalid timeout: %s", settings.Timeout)) + return nil, errors.New(fmt.Sprintf("invalid timeout: %s", settings.DialTimeout)) } qt, err := strconv.Atoi(settings.QueryTimeout) if err != nil { @@ -145,7 +145,7 @@ func (h *Clickhouse) Connect(config backend.DataSourceInstanceSettings, message Products: getClientInfoProducts(), }, TLS: tlsConfig, - Addr: []string{fmt.Sprintf("%s:%d", settings.Server, settings.Port)}, + Addr: []string{fmt.Sprintf("%s:%d", settings.Host, settings.Port)}, HttpUrlPath: settings.Path, Auth: clickhouse.Auth{ Username: settings.Username, @@ -266,8 +266,8 @@ func (h *Clickhouse) MutateQuery(ctx context.Context, req backend.DataQuery) (co // MutateResponse For any view other than traces we convert FieldTypeNullableJSON to string func (h *Clickhouse) MutateResponse(ctx context.Context, res data.Frames) (data.Frames, error) { for _, frame := range res { - if frame.Meta.PreferredVisualization != data.VisType(data.VisTypeTrace) && - frame.Meta.PreferredVisualization != data.VisType(data.VisTypeTable) { + if frame.Meta.PreferredVisualization != data.VisTypeTrace && + frame.Meta.PreferredVisualization != data.VisTypeTable { var fields []*data.Field for _, field := range frame.Fields { values := make([]*string, field.Len()) diff --git a/pkg/plugin/errors.go b/pkg/plugin/errors.go index 0053a5bb..98b1dcd9 100644 --- a/pkg/plugin/errors.go +++ b/pkg/plugin/errors.go @@ -4,7 +4,7 @@ import "github.com/pkg/errors" var ( ErrorMessageInvalidJSON = errors.New("could not parse json") - ErrorMessageInvalidServerName = errors.New("invalid server name. Either empty or not set") + ErrorMessageInvalidHost = errors.New("invalid server host. Either empty or not set") ErrorMessageInvalidPort = errors.New("invalid port") ErrorMessageInvalidUserName = errors.New("username is either empty or not set") ErrorMessageInvalidPassword = errors.New("password is either empty or not set") diff --git a/pkg/plugin/settings.go b/pkg/plugin/settings.go index 679fb8bf..e6c19854 100644 --- a/pkg/plugin/settings.go +++ b/pkg/plugin/settings.go @@ -13,24 +13,29 @@ import ( // Settings - data loaded from grafana settings database type Settings struct { - Server string `json:"server,omitempty"` - Port int64 `json:"port,omitempty"` - Path string `json:"path,omitempty"` - Username string `json:"username,omitempty"` - DefaultDatabase string `json:"defaultDatabase,omitempty"` - InsecureSkipVerify bool `json:"tlsSkipVerify,omitempty"` - TlsClientAuth bool `json:"tlsAuth,omitempty"` - TlsAuthWithCACert bool `json:"tlsAuthWithCACert,omitempty"` - Password string `json:"-,omitempty"` - TlsCACert string + Host string `json:"host,omitempty"` + Port int64 `json:"port,omitempty"` + Protocol string `json:"protocol"` + Secure bool `json:"secure,omitempty"` + Path string `json:"path,omitempty"` + + InsecureSkipVerify bool `json:"tlsSkipVerify,omitempty"` + TlsClientAuth bool `json:"tlsAuth,omitempty"` + TlsAuthWithCACert bool `json:"tlsAuthWithCACert,omitempty"` TlsClientCert string + TlsCACert string TlsClientKey string - Secure bool `json:"secure,omitempty"` - Timeout string `json:"timeout,omitempty"` - QueryTimeout string `json:"queryTimeout,omitempty"` - Protocol string `json:"protocol"` - CustomSettings []CustomSetting `json:"customSettings"` - ProxyOptions *proxy.Options + + Username string `json:"username,omitempty"` + Password string `json:"-,omitempty"` + + DefaultDatabase string `json:"defaultDatabase,omitempty"` + + DialTimeout string `json:"dialTimeout,omitempty"` + QueryTimeout string `json:"queryTimeout,omitempty"` + + CustomSettings []CustomSetting `json:"customSettings"` + ProxyOptions *proxy.Options } type CustomSetting struct { @@ -39,8 +44,8 @@ type CustomSetting struct { } func (settings *Settings) isValid() (err error) { - if settings.Server == "" { - return ErrorMessageInvalidServerName + if settings.Host == "" { + return ErrorMessageInvalidHost } if settings.Port == 0 { return ErrorMessageInvalidPort @@ -55,9 +60,14 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, return settings, fmt.Errorf("%s: %w", err.Error(), ErrorMessageInvalidJSON) } + // Deprecated: Replaced with Host for v4. Deserializes "server" field for old v3 configs. if jsonData["server"] != nil { - settings.Server = jsonData["server"].(string) + settings.Host = jsonData["server"].(string) } + if jsonData["host"] != nil { + settings.Host = jsonData["host"].(string) + } + if jsonData["port"] != nil { if port, ok := jsonData["port"].(string); ok { settings.Port, err = strconv.ParseInt(port, 0, 64) @@ -68,15 +78,22 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, settings.Port = int64(jsonData["port"].(float64)) } } - if jsonData["username"] != nil { - settings.Username = jsonData["username"].(string) + if jsonData["protocol"] != nil { + settings.Protocol = jsonData["protocol"].(string) + } + if jsonData["secure"] != nil { + if secure, ok := jsonData["secure"].(string); ok { + settings.Secure, err = strconv.ParseBool(secure) + if err != nil { + return settings, fmt.Errorf("could not parse secure value: %w", err) + } + } else { + settings.Secure = jsonData["secure"].(bool) + } } if jsonData["path"] != nil { settings.Path = jsonData["path"].(string) } - if jsonData["defaultDatabase"] != nil { - settings.DefaultDatabase = jsonData["defaultDatabase"].(string) - } if jsonData["tlsSkipVerify"] != nil { if tlsSkipVerify, ok := jsonData["tlsSkipVerify"].(string); ok { @@ -108,20 +125,22 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, settings.TlsAuthWithCACert = jsonData["tlsAuthWithCACert"].(bool) } } - if jsonData["secure"] != nil { - if secure, ok := jsonData["secure"].(string); ok { - settings.Secure, err = strconv.ParseBool(secure) - if err != nil { - return settings, fmt.Errorf("could not parse secure value: %w", err) - } - } else { - settings.Secure = jsonData["secure"].(bool) - } + + if jsonData["username"] != nil { + settings.Username = jsonData["username"].(string) + } + if jsonData["defaultDatabase"] != nil { + settings.DefaultDatabase = jsonData["defaultDatabase"].(string) } + // Deprecated: Replaced with DialTimeout for v4. Deserializes "timeout" field for old v3 configs. if jsonData["timeout"] != nil { - settings.Timeout = jsonData["timeout"].(string) + settings.DialTimeout = jsonData["timeout"].(string) } + if jsonData["dialTimeout"] != nil { + settings.DialTimeout = jsonData["dialTimeout"].(string) + } + if jsonData["queryTimeout"] != nil { if val, ok := jsonData["queryTimeout"].(string); ok { settings.QueryTimeout = val @@ -130,9 +149,6 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, settings.QueryTimeout = fmt.Sprintf("%d", int64(val)) } } - if jsonData["protocol"] != nil { - settings.Protocol = jsonData["protocol"].(string) - } if jsonData["customSettings"] != nil { customSettingsRaw := jsonData["customSettings"].([]interface{}) customSettings := make([]CustomSetting, len(customSettingsRaw)) @@ -148,8 +164,8 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, settings.CustomSettings = customSettings } - if strings.TrimSpace(settings.Timeout) == "" { - settings.Timeout = "10" + if strings.TrimSpace(settings.DialTimeout) == "" { + settings.DialTimeout = "10" } if strings.TrimSpace(settings.QueryTimeout) == "" { settings.QueryTimeout = "60" @@ -177,9 +193,9 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, if err == nil && proxyOpts != nil { // the sdk expects the timeout to not be a string - timeout, err := strconv.ParseFloat(settings.Timeout, 64) + timeout, err := strconv.ParseFloat(settings.DialTimeout, 64) if err == nil { - proxyOpts.Timeouts.Timeout = (time.Duration(timeout) * time.Second) + proxyOpts.Timeouts.Timeout = time.Duration(timeout) * time.Second } settings.ProxyOptions = proxyOpts diff --git a/pkg/plugin/settings_test.go b/pkg/plugin/settings_test.go index 76a63f1e..6805f3b9 100644 --- a/pkg/plugin/settings_test.go +++ b/pkg/plugin/settings_test.go @@ -24,16 +24,16 @@ func TestLoadSettings(t *testing.T) { wantErr error }{ { - name: "should parse and set all the json fields correctly", + name: "should parse and set all json fields correctly", args: args{ config: backend.DataSourceInstanceSettings{ UID: "ds-uid", - JSONData: []byte(`{ "server": "foo", "port": 443, "path": "custom-path", "username": "baz", "defaultDatabase":"example", "tlsSkipVerify": true, "tlsAuth" : true, "tlsAuthWithCACert": true, "timeout": "10", "enableSecureSocksProxy": true}`), + JSONData: []byte(`{ "host": "foo", "port": 443, "path": "custom-path", "username": "baz", "defaultDatabase":"example", "tlsSkipVerify": true, "tlsAuth" : true, "tlsAuthWithCACert": true, "dialTimeout": "10", "enableSecureSocksProxy": true}`), DecryptedSecureJSONData: map[string]string{"password": "bar", "tlsCACert": "caCert", "tlsClientCert": "clientCert", "tlsClientKey": "clientKey", "secureSocksProxyPassword": "test"}, }, }, wantSettings: Settings{ - Server: "foo", + Host: "foo", Port: 443, Path: "custom-path", Username: "baz", @@ -45,7 +45,7 @@ func TestLoadSettings(t *testing.T) { TlsCACert: "caCert", TlsClientCert: "clientCert", TlsClientKey: "clientKey", - Timeout: "10", + DialTimeout: "10", QueryTimeout: "60", ProxyOptions: &proxy.Options{ Enabled: true, @@ -62,26 +62,42 @@ func TestLoadSettings(t *testing.T) { wantErr: nil, }, { - name: "should converting string values to the correct type)", + name: "should convert string values to the correct type", args: args{ config: backend.DataSourceInstanceSettings{ - JSONData: []byte(`{"server": "test", "port": "443", "path": "custom-path", "tlsSkipVerify": "true", "tlsAuth" : "true", "tlsAuthWithCACert": "true"}`), + JSONData: []byte(`{"host": "test", "port": "443", "path": "custom-path", "tlsSkipVerify": "true", "tlsAuth" : "true", "tlsAuthWithCACert": "true"}`), DecryptedSecureJSONData: map[string]string{}, }, }, wantSettings: Settings{ - Server: "test", + Host: "test", Port: 443, Path: "custom-path", InsecureSkipVerify: true, TlsClientAuth: true, TlsAuthWithCACert: true, - Timeout: "10", + DialTimeout: "10", QueryTimeout: "60", ProxyOptions: nil, }, wantErr: nil, }, + { + name: "should parse v3 config fields into new fields", + args: args{ + config: backend.DataSourceInstanceSettings{ + JSONData: []byte(`{"server": "test", "port": 443, "timeout": "10"}`), + DecryptedSecureJSONData: map[string]string{}, + }, + }, + wantSettings: Settings{ + Host: "test", + Port: 443, + DialTimeout: "10", + QueryTimeout: "60", + }, + wantErr: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -100,9 +116,9 @@ func TestLoadSettings(t *testing.T) { wantErr error description string }{ - {jsonData: `{ "server": "", "port": 443 }`, password: "", wantErr: ErrorMessageInvalidServerName, description: "should capture empty server name"}, - {jsonData: `{ "server": "foo" }`, password: "", wantErr: ErrorMessageInvalidPort, description: "should capture nil port"}, - {jsonData: ` "server": "foo", "port": 443, "username" : "foo" }`, password: "", wantErr: ErrorMessageInvalidJSON, description: "should capture invalid json"}, + {jsonData: `{ "host": "", "port": 443 }`, password: "", wantErr: ErrorMessageInvalidHost, description: "should capture empty server name"}, + {jsonData: `{ "host": "foo" }`, password: "", wantErr: ErrorMessageInvalidPort, description: "should capture nil port"}, + {jsonData: ` "host": "foo", "port": 443, "username" : "foo" }`, password: "", wantErr: ErrorMessageInvalidJSON, description: "should capture invalid json"}, } for i, tc := range tests { t.Run(fmt.Sprintf("[%v/%v] %s", i+1, len(tests), tc.description), func(t *testing.T) { diff --git a/src/__mocks__/ConfigEditor.ts b/src/__mocks__/ConfigEditor.ts index a6d34b76..9f43d6f2 100644 --- a/src/__mocks__/ConfigEditor.ts +++ b/src/__mocks__/ConfigEditor.ts @@ -1,10 +1,10 @@ import * as fs from 'fs'; -import { Props } from '../views/CHConfigEditor'; -import { CHConfig } from 'types'; +import { ConfigEditorProps } from 'views/CHConfigEditor'; +import { CHConfig } from 'types/config'; const pluginJson = JSON.parse(fs.readFileSync('./src/plugin.json', 'utf-8')); -export const mockConfigEditorProps = (overrides?: Partial): Props => ({ +export const mockConfigEditorProps = (overrides?: Partial): ConfigEditorProps => ({ options: { ...pluginJson, jsonData: { diff --git a/src/__mocks__/datasource.ts b/src/__mocks__/datasource.ts index edf2aa83..f0d48cb7 100644 --- a/src/__mocks__/datasource.ts +++ b/src/__mocks__/datasource.ts @@ -1,5 +1,7 @@ import { PluginType } from '@grafana/data'; -import { CHQuery, Protocol, QueryType } from '../types'; +import { Protocol } from 'types/config'; +import { CHQuery, EditorType } from 'types/sql'; +import { QueryType } from 'types/queryBuilder'; import { Datasource } from '../data/CHDatasource'; export const mockDatasource = new Datasource({ @@ -8,12 +10,13 @@ export const mockDatasource = new Datasource({ type: 'grafana-clickhouse-datasource', name: 'ClickHouse', jsonData: { - server: 'foo.com', + host: 'foo.com', port: 443, path: '', username: 'user', defaultDatabase: 'foo', - protocol: Protocol.NATIVE, + defaultTable: 'bar', + protocol: Protocol.Native, }, readOnly: true, access: 'direct', @@ -39,11 +42,13 @@ export const mockDatasource = new Datasource({ }, }, }); + mockDatasource.adHocFiltersStatus = 1; // most tests should skip checking the CH version. We will set ad hoc filters to enabled to avoid running the CH version check + export const mockQuery: CHQuery = { + pluginVersion: '', rawSql: 'select * from foo', refId: '', - format: 1, - queryType: QueryType.SQL, - selectedFormat: 4, + editorType: EditorType.SQL, + queryType: QueryType.Table }; diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index 49caa6a5..17513c6d 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Divider as GrafanaDivider, useTheme2 } from '@grafana/ui'; import { config } from '@grafana/runtime'; -import { isVersionGtOrEq } from '../utils/version'; +import { isVersionGtOrEq } from 'utils/version'; export function Divider() { const theme = useTheme2(); diff --git a/src/components/FormatSelect.test.tsx b/src/components/FormatSelect.test.tsx deleted file mode 100644 index b12c23f9..00000000 --- a/src/components/FormatSelect.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { FormatSelect } from './FormatSelect'; -import { Format } from '../types'; - -describe('FormatSelect', () => { - it('renders a format', () => { - const result = render( {}} />); - expect(result.container.firstChild).not.toBeNull(); - }); -}); diff --git a/src/components/FormatSelect.tsx b/src/components/FormatSelect.tsx deleted file mode 100644 index c82ff6ea..00000000 --- a/src/components/FormatSelect.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import { selectors } from './../selectors'; -import { Format } from '../types'; -import { InlineSelect } from '@grafana/experimental'; - -export type Props = { format: Format; value?: string; onChange: (format: Format) => void }; - -export const FormatSelect = (props: Props) => { - const { onChange, format } = props; - const { options: formatLabels } = selectors.components.QueryEditor.Format; - return ( - onChange(e.value!)} - /> - ); -}; diff --git a/src/components/QueryHeader.tsx b/src/components/QueryHeader.tsx deleted file mode 100644 index 51eda9d7..00000000 --- a/src/components/QueryHeader.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { BuilderMode, CHQuery, Format, QueryType, CHBuilderQuery } from '../types'; -import { QueryTypeSwitcher } from 'components/QueryTypeSwitcher'; -import { FormatSelect } from '../components/FormatSelect'; -import { Button } from '@grafana/ui'; -import { getFormat } from 'components/editor'; -import { EditorHeader, FlexItem } from '@grafana/experimental'; - -interface QueryHeaderProps { - query: CHQuery; - onChange: (query: CHQuery) => void; - onRunQuery: () => void; -} - -export const QueryHeader = ({ query, onChange, onRunQuery }: QueryHeaderProps) => { - React.useEffect(() => { - if (typeof query.selectedFormat === 'undefined' && query.queryType === QueryType.SQL) { - const selectedFormat = Format.AUTO; - const format = getFormat(query.rawSql, selectedFormat); - onChange({ ...query, selectedFormat, format }); - } - }, [query, onChange]); - - const runQuery = () => { - if (query.queryType === QueryType.SQL) { - const format = getFormat(query.rawSql, query.selectedFormat); - if (format !== query.format) { - onChange({ ...query, format }); - } - } - onRunQuery(); - }; - - const onFormatChange = (selectedFormat: Format) => { - switch (query.queryType) { - case QueryType.SQL: - onChange({ ...query, format: getFormat(query.rawSql, selectedFormat), selectedFormat }); - case QueryType.Builder: - default: - if (selectedFormat === Format.AUTO) { - let builderOptions = (query as CHBuilderQuery).builderOptions; - const format = builderOptions && builderOptions.mode === BuilderMode.Trend ? Format.TIMESERIES : Format.TABLE; - onChange({ ...query, format, selectedFormat }); - } else { - onChange({ ...query, format: selectedFormat, selectedFormat }); - } - } - }; - - return ( - - - - - - - ); -}; diff --git a/src/components/QueryTypeSwitcher.test.tsx b/src/components/QueryTypeSwitcher.test.tsx deleted file mode 100644 index c875dd64..00000000 --- a/src/components/QueryTypeSwitcher.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { QueryTypeSwitcher } from './QueryTypeSwitcher'; -import { selectors } from '../selectors'; -import { QueryType, CHQuery, CHSQLQuery, Format } from '../types'; - -const { options } = selectors.components.QueryEditor.Types; - -describe('QueryTypeSwitcher', () => { - it('renders default query', () => { - const result = render( {}} />); - expect(result.container.firstChild).not.toBeNull(); - expect(result.getByLabelText(options.SQLEditor)).not.toBeChecked(); - expect(result.getByLabelText(options.QueryBuilder)).toBeChecked(); - }); - it('renders legacy query (query without query type)', () => { - const result = render( - {}} /> - ); - expect(result.container.firstChild).not.toBeNull(); - expect(result.getByLabelText(options.SQLEditor)).toBeChecked(); - expect(result.getByLabelText(options.QueryBuilder)).not.toBeChecked(); - }); - it('renders correctly SQL editor', () => { - const result = render( - {}} - /> - ); - expect(result.container.firstChild).not.toBeNull(); - expect(result.getByLabelText(options.SQLEditor)).toBeChecked(); - expect(result.getByLabelText(options.QueryBuilder)).not.toBeChecked(); - }); - it('renders correctly SQL Builder editor', () => { - const result = render( - {}} - /> - ); - expect(result.container.firstChild).not.toBeNull(); - expect(result.getByLabelText(options.SQLEditor)).not.toBeChecked(); - expect(result.getByLabelText(options.QueryBuilder)).toBeChecked(); - }); -}); diff --git a/src/components/QueryTypeSwitcher.tsx b/src/components/QueryTypeSwitcher.tsx deleted file mode 100644 index e802bf61..00000000 --- a/src/components/QueryTypeSwitcher.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useState } from 'react'; -import { SelectableValue } from '@grafana/data'; -import { RadioButtonGroup, ConfirmModal } from '@grafana/ui'; -import { getQueryOptionsFromSql, getSQLFromQueryOptions } from './queryBuilder/utils'; -import { selectors } from './../selectors'; -import { CHQuery, QueryType, defaultCHBuilderQuery, SqlBuilderOptions, CHSQLQuery } from 'types'; -import { isString } from 'lodash'; - -interface QueryTypeSwitcherProps { - query: CHQuery; - onChange: (query: CHQuery) => void; -} - -export const QueryTypeSwitcher = ({ query, onChange }: QueryTypeSwitcherProps) => { - const { options: queryTypeLabels, switcher, cannotConvert } = selectors.components.QueryEditor.Types; - let queryType: QueryType = - query.queryType || - ((query as CHSQLQuery).rawSql && !(query as CHQuery).queryType ? QueryType.SQL : QueryType.Builder); - const [editor, setEditor] = useState(queryType); - const [confirmModalState, setConfirmModalState] = useState(false); - const [cannotConvertModalState, setCannotConvertModalState] = useState(false); - const options: Array> = [ - { label: queryTypeLabels.SQLEditor, value: QueryType.SQL }, - { label: queryTypeLabels.QueryBuilder, value: QueryType.Builder }, - ]; - const [errorMessage, setErrorMessage] = useState(''); - const onQueryTypeChange = (queryType: QueryType, confirm = false) => { - if (query.queryType === QueryType.SQL && queryType === QueryType.Builder && !confirm) { - const queryOptionsFromSql = getQueryOptionsFromSql(query.rawSql); - if (isString(queryOptionsFromSql)) { - setCannotConvertModalState(true); - setErrorMessage(queryOptionsFromSql); - } else { - setConfirmModalState(true); - } - } else { - setEditor(queryType); - let builderOptions: SqlBuilderOptions; - switch (query.queryType) { - case QueryType.Builder: - builderOptions = query.builderOptions; - break; - case QueryType.SQL: - builderOptions = - (getQueryOptionsFromSql(query.rawSql) as SqlBuilderOptions) || defaultCHBuilderQuery.builderOptions; - break; - default: - builderOptions = defaultCHBuilderQuery.builderOptions; - break; - } - if (queryType === QueryType.SQL) { - onChange({ - ...query, - queryType, - rawSql: getSQLFromQueryOptions(builderOptions), - meta: { builderOptions }, - format: query.format, - selectedFormat: query.selectedFormat, - }); - } else if (queryType === QueryType.Builder) { - onChange({ ...query, queryType, rawSql: getSQLFromQueryOptions(builderOptions), builderOptions }); - } - } - }; - const onConfirmQueryTypeChange = () => { - onQueryTypeChange(QueryType.Builder, true); - setConfirmModalState(false); - setCannotConvertModalState(false); - }; - return ( - <> - onQueryTypeChange(e!)} /> - setConfirmModalState(false)} - /> - setCannotConvertModalState(false)} - /> - - ); -}; diff --git a/src/components/SQLEditor.tsx b/src/components/SQLEditor.tsx deleted file mode 100644 index aaf55dc5..00000000 --- a/src/components/SQLEditor.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useState } from 'react'; -import { QueryEditorProps } from '@grafana/data'; -import { CodeEditor } from '@grafana/ui'; -import { Datasource } from '../data/CHDatasource'; -import { registerSQL, Range, Fetcher } from './sqlProvider'; -import { CHQuery, CHConfig, QueryType, CHSQLQuery } from '../types'; -import { styles } from '../styles'; -import { fetchSuggestions as sugg, Schema } from './suggestions'; -import { selectors } from 'selectors'; -import { getFormat } from './editor'; -import { validate } from 'data/validate'; - -type SQLEditorProps = QueryEditorProps; - -interface Expand { - height: string; - icon: 'plus' | 'minus'; - on: boolean; -} - -export const SQLEditor = (props: SQLEditorProps) => { - const defaultHeight = '150px'; - const { query, onRunQuery, onChange, datasource } = props; - const [codeEditor, setCodeEditor] = useState(); - const [expand, setExpand] = useState({ - height: defaultHeight, - icon: 'plus', - on: (query as CHSQLQuery).expand || false, - }); - - const onSqlChange = (sql: string) => { - const format = getFormat(sql, query.selectedFormat); - onChange({ ...query, rawSql: sql, format, queryType: QueryType.SQL }); - onRunQuery(); - }; - - const onToggleExpand = () => { - const sqlQuery = query as CHSQLQuery; - const on = !expand.on; - const icon = on ? 'minus' : 'plus'; - onChange({ ...sqlQuery, expand: on }); - - if (!codeEditor) { - return; - } - if (on) { - codeEditor.expanded = true; - const height = getEditorHeight(codeEditor); - setExpand({ height: `${height}px`, on, icon }); - return; - } - - codeEditor.expanded = false; - setExpand({ height: defaultHeight, icon, on }); - }; - - const schema: Schema = { - databases: () => datasource.fetchDatabases(), - tables: (db?: string) => datasource.fetchTables(db), - fields: (db: string, table: string) => datasource.fetchFields(db, table), - defaultDatabase: datasource.getDefaultDatabase(), - }; - - const fetchSuggestions: Fetcher = async (text: string, range: Range) => { - const suggestions = await sugg(text, schema, range); - return Promise.resolve({ suggestions }); - }; - - const validateSql = (sql: string, model: any, me: any) => { - const v = validate(sql); - const errorSeverity = 8; - if (v.valid) { - me.setModelMarkers(model, 'clickhouse', []); - } else { - const err = v.error!; - me.setModelMarkers(model, 'clickhouse', [ - { - startLineNumber: err.startLine, - startColumn: err.startCol, - endLineNumber: err.endLine, - endColumn: err.endCol, - message: err.expected, - severity: errorSeverity, - }, - ]); - } - }; - - const handleMount = (editor: any) => { - const me = registerSQL('chSql', editor, fetchSuggestions); - editor.expanded = (query as CHSQLQuery).expand; - editor.onDidChangeModelDecorations((a: any) => { - if (editor.expanded) { - const height = getEditorHeight(editor); - setExpand({ height: `${height}px`, on: true, icon: 'minus' }); - } - }); - editor.onKeyUp((e: any) => { - if (datasource.settings.jsonData.validate) { - const sql = editor.getValue(); - validateSql(sql, editor.getModel(), me); - } - }); - setCodeEditor(editor); - }; - - return ( -
- onToggleExpand()} - className={styles.Common.expand} - data-testid={selectors.components.QueryEditor.CodeEditor.Expand} - > - - - onChange({ ...query, rawSql: text })} - onEditorDidMount={(editor: any) => handleMount(editor)} - /> -
- ); -}; - -const getEditorHeight = (editor: any): number | undefined => { - const editorElement = editor.getDomNode(); - if (!editorElement) { - return; - } - - const lineCount = editor.getModel()?.getLineCount() || 1; - return editor.getTopForLineNumber(lineCount + 1) + 40; -}; diff --git a/src/components/SQLEditor.test.tsx b/src/components/SqlEditor.test.tsx similarity index 58% rename from src/components/SQLEditor.test.tsx rename to src/components/SqlEditor.test.tsx index 621c1459..11fe682c 100644 --- a/src/components/SQLEditor.test.tsx +++ b/src/components/SqlEditor.test.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { act, render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import '@testing-library/jest-dom'; -import { SQLEditor } from './SQLEditor'; -import { Components } from '../selectors'; +import { SqlEditor } from './SqlEditor'; import * as ui from '@grafana/ui'; import { mockDatasource } from '__mocks__/datasource'; -import { QueryType } from 'types'; +import { EditorType } from 'types/sql'; +import { Components } from 'selectors'; jest.mock('@grafana/ui', () => ({ ...jest.requireActual('@grafana/ui'), @@ -26,8 +26,8 @@ describe('SQL Editor', () => { it('Should display sql in the editor', () => { const rawSql = 'foo'; render( - { expect(screen.queryByText(rawSql)).toBeInTheDocument(); }); it('Should Expand Query', async () => { - const onChangeValue = jest.fn(); + const onChange = jest.fn(); const onRunQuery = jest.fn(); - await act(async () => { + const result = await waitFor(() => render( - - ); - expect(screen.queryByText('test')).toBeInTheDocument(); - await userEvent.click(screen.getByTestId(Components.QueryEditor.CodeEditor.Expand)); - expect(onChangeValue).toHaveBeenCalledTimes(1); - }); + )); + + expect(result.queryByText('test')).toBeInTheDocument(); + await userEvent.click(result.getByTestId(Components.QueryEditor.CodeEditor.Expand)); + expect(onChange).toHaveBeenCalledTimes(0); // TODO: codeEditor isn't mounting and wont call onChange }); }); diff --git a/src/components/SqlEditor.tsx b/src/components/SqlEditor.tsx new file mode 100644 index 00000000..85226bc7 --- /dev/null +++ b/src/components/SqlEditor.tsx @@ -0,0 +1,160 @@ +import React, { useState } from 'react'; +import { CoreApp, QueryEditorProps } from '@grafana/data'; +import { CodeEditor } from '@grafana/ui'; +import { Datasource } from 'data/CHDatasource'; +import { registerSQL, Range, Fetcher } from './sqlProvider'; +import { CHConfig } from 'types/config'; +import { CHQuery, EditorType, CHSqlQuery } from 'types/sql'; +import { styles } from 'styles'; +import { fetchSuggestions, Schema } from './suggestions'; +import { selectors } from 'selectors'; +import { validate } from 'data/validate'; +import { mapQueryTypeToGrafanaFormat } from 'data/utils'; +import { QueryType } from 'types/queryBuilder'; +import { QueryTypeSwitcher } from 'components/queryBuilder/QueryTypeSwitcher'; +import { pluginVersion } from 'utils/version'; + +type SqlEditorProps = QueryEditorProps; + +interface Expand { + height: string; + icon: 'plus' | 'minus'; + on: boolean; +} + +export const SqlEditor = (props: SqlEditorProps) => { + const defaultHeight = '150px'; + const { app, query, onChange, datasource } = props; + const sqlQuery = query as CHSqlQuery; + const [codeEditor, setCodeEditor] = useState(); + const [expand, setExpand] = useState({ + height: defaultHeight, + icon: 'plus', + on: sqlQuery.expand || false, + }); + const queryType = sqlQuery.queryType || QueryType.Table; + + const saveChanges = (changes: Partial) => { + onChange({ + ...sqlQuery, + pluginVersion, + editorType: EditorType.SQL, + format: mapQueryTypeToGrafanaFormat(changes.queryType || queryType), + ...changes + }); + } + + const updateExpand = (expand: Expand) => { + setExpand(expand); + saveChanges({ expand: expand.on }); + } + + const onToggleExpand = () => { + const on = !expand.on; + const icon = on ? 'minus' : 'plus'; + + if (!codeEditor) { + return; + } + if (on) { + codeEditor.expanded = true; + const height = getEditorHeight(codeEditor); + updateExpand({ height: `${height}px`, on, icon }); + return; + } + + codeEditor.expanded = false; + updateExpand({ height: defaultHeight, icon, on }); + }; + + const schema: Schema = { + databases: () => datasource.fetchDatabases(), + tables: (db?: string) => datasource.fetchTables(db), + fields: (db: string, table: string) => datasource.fetchFields(db, table), + defaultDatabase: datasource.getDefaultDatabase(), + }; + + const getSuggestions: Fetcher = async (text: string, range: Range) => { + const suggestions = await fetchSuggestions(text, schema, range); + return Promise.resolve({ suggestions }); + }; + + const validateSql = (sql: string, model: any, me: any) => { + const v = validate(sql); + const errorSeverity = 8; + if (v.valid) { + me.setModelMarkers(model, 'clickhouse', []); + } else { + const err = v.error!; + me.setModelMarkers(model, 'clickhouse', [ + { + startLineNumber: err.startLine, + startColumn: err.startCol, + endLineNumber: err.endLine, + endColumn: err.endCol, + message: err.expected, + severity: errorSeverity, + }, + ]); + } + }; + + const handleMount = (editor: any) => { + const me = registerSQL('chSql', editor, getSuggestions); + editor.expanded = (query as CHSqlQuery).expand; + editor.onDidChangeModelDecorations((a: any) => { + if (editor.expanded) { + const height = getEditorHeight(editor); + updateExpand({ height: `${height}px`, on: true, icon: 'minus' }); + } + }); + editor.onKeyUp((e: any) => { + if (datasource.settings.jsonData.validateSql) { + const sql = editor.getValue(); + validateSql(sql, editor.getModel(), me); + } + }); + setCodeEditor(editor); + }; + + return ( + <> + {/* Only show in explore view where panel can't be manually selected. Dashboard view lets you change the panel. */} + { app === CoreApp.Explore && +
+ saveChanges({ queryType })} sqlEditor /> +
+ } +
+ onToggleExpand()} + className={styles.Common.expand} + data-testid={selectors.components.QueryEditor.CodeEditor.Expand} + > + + + saveChanges({ rawSql: sql })} + showMiniMap={false} + showLineNumbers={true} + onBlur={sql => saveChanges({ rawSql: sql })} + onEditorDidMount={(editor: any) => handleMount(editor)} + /> +
+ + ); +}; + +const getEditorHeight = (editor: any): number | undefined => { + const editorElement = editor.getDomNode(); + if (!editorElement) { + return; + } + + const lineCount = editor.getModel()?.getLineCount() || 1; + return editor.getTopForLineNumber(lineCount + 1) + 40; +}; diff --git a/src/components/configEditor/DefaultDatabaseTableConfig.test.tsx b/src/components/configEditor/DefaultDatabaseTableConfig.test.tsx new file mode 100644 index 00000000..72f932f3 --- /dev/null +++ b/src/components/configEditor/DefaultDatabaseTableConfig.test.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { DefaultDatabaseTableConfig } from './DefaultDatabaseTableConfig'; +import allLabels from 'labels'; + +describe('DefaultDatabaseTableConfig', () => { + it('should render', () => { + const result = render( {}} onDefaultTableChange={() => {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onDefaultDatabaseChange when default database is changed', () => { + const onDefaultDatabaseChange = jest.fn(); + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + + const databaseInput = result.getByLabelText(allLabels.components.Config.DefaultDatabaseTableConfig.database.label); + expect(databaseInput).toBeInTheDocument(); + fireEvent.change(databaseInput, { target: { value: 'test' } }); + fireEvent.blur(databaseInput); + expect(onDefaultDatabaseChange).toHaveBeenCalledTimes(1); + expect(onDefaultDatabaseChange).toHaveBeenCalledWith(expect.any(Object)); + }); + + it('should call onDefaultTableChange when default table is changed', () => { + const onDefaultTableChange = jest.fn(); + const result = render( {}} onDefaultTableChange={onDefaultTableChange} />); + expect(result.container.firstChild).not.toBeNull(); + + const tableInput = result.getByLabelText(allLabels.components.Config.DefaultDatabaseTableConfig.table.label); + expect(tableInput).toBeInTheDocument(); + fireEvent.change(tableInput, { target: { value: 'test' } }); + fireEvent.blur(tableInput); + expect(onDefaultTableChange).toHaveBeenCalledTimes(1); + expect(onDefaultTableChange).toHaveBeenCalledWith(expect.any(Object)); + }); +}); diff --git a/src/components/configEditor/DefaultDatabaseTableConfig.tsx b/src/components/configEditor/DefaultDatabaseTableConfig.tsx new file mode 100644 index 00000000..2ac2221b --- /dev/null +++ b/src/components/configEditor/DefaultDatabaseTableConfig.tsx @@ -0,0 +1,51 @@ +import React, { SyntheticEvent } from 'react'; +import { ConfigSection } from '@grafana/experimental'; +import { Input, Field } from '@grafana/ui'; +import allLabels from 'labels'; + +interface DefaultDatabaseTableConfigProps { + defaultDatabase?: string; + defaultTable?: string; + onDefaultDatabaseChange: (e: SyntheticEvent) => void; + onDefaultTableChange: (e: SyntheticEvent) => void; +} + +export const DefaultDatabaseTableConfig = (props: DefaultDatabaseTableConfigProps) => { + const { defaultDatabase, defaultTable, onDefaultDatabaseChange, onDefaultTableChange } = props; + const labels = allLabels.components.Config.DefaultDatabaseTableConfig; + + return ( + + + + + + + + + ); +} diff --git a/src/components/configEditor/LabeledInput.test.tsx b/src/components/configEditor/LabeledInput.test.tsx new file mode 100644 index 00000000..b9bb03a1 --- /dev/null +++ b/src/components/configEditor/LabeledInput.test.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { LabeledInput } from './LabeledInput'; + +describe('LabeledInput', () => { + it('should render', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onChange when input is changed', async () => { + const onChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText('test'); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith('changed'); + }); +}); diff --git a/src/components/configEditor/LabeledInput.tsx b/src/components/configEditor/LabeledInput.tsx new file mode 100644 index 00000000..e700565f --- /dev/null +++ b/src/components/configEditor/LabeledInput.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Input, InlineFormLabel } from '@grafana/ui'; + +interface LabeledInputProps { + label: string; + tooltip?: string; + placeholder?: string; + disabled?: boolean; + value: string; + onChange: (value: string) => void; +} + +export function LabeledInput(props: LabeledInputProps) { + const { label, tooltip, placeholder, disabled, value, onChange } = props; + + return ( +
+ + {label} + + onChange(e.currentTarget.value)} + placeholder={placeholder} + /> +
+ ) +} diff --git a/src/components/configEditor/LogsConfig.test.tsx b/src/components/configEditor/LogsConfig.test.tsx new file mode 100644 index 00000000..fe4392da --- /dev/null +++ b/src/components/configEditor/LogsConfig.test.tsx @@ -0,0 +1,190 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { LogsConfig } from './LogsConfig'; +import allLabels from 'labels'; +import { columnLabelToPlaceholder } from 'data/utils'; + +describe('LogsConfig', () => { + it('should render', () => { + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTimeColumnChange={() => {}} + onLevelColumnChange={() => {}} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onDefaultDatabase when changed', () => { + const onDefaultDatabaseChange = jest.fn(); + const result = render( + {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTimeColumnChange={() => {}} + onLevelColumnChange={() => {}} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(allLabels.components.Config.LogsConfig.defaultDatabase.placeholder); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onDefaultDatabaseChange).toHaveBeenCalledTimes(1); + expect(onDefaultDatabaseChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onDefaultTable when changed', () => { + const onDefaultTableChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={onDefaultTableChange} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTimeColumnChange={() => {}} + onLevelColumnChange={() => {}} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(allLabels.components.Config.LogsConfig.defaultTable.placeholder); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onDefaultTableChange).toHaveBeenCalledTimes(1); + expect(onDefaultTableChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onOtelEnabled when changed', () => { + const onOtelEnabledChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={onOtelEnabledChange} + onOtelVersionChange={() => {}} + onTimeColumnChange={() => {}} + onLevelColumnChange={() => {}} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByRole('checkbox'); + expect(input).toBeInTheDocument(); + fireEvent.click(input); + expect(onOtelEnabledChange).toHaveBeenCalledTimes(1); + expect(onOtelEnabledChange).toHaveBeenCalledWith(true); + }); + + it('should call onOtelVersionChange when changed', () => { + const onOtelVersionChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={onOtelVersionChange} + onTimeColumnChange={() => {}} + onLevelColumnChange={() => {}} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const select = result.getByRole('combobox'); + expect(select).toBeInTheDocument(); + fireEvent.keyDown(select, { key: 'ArrowDown' }); + fireEvent.keyDown(select, { key: 'Enter' }); + expect(onOtelVersionChange).toHaveBeenCalledTimes(2); // 2 from hook + expect(onOtelVersionChange).toHaveBeenCalledWith(expect.any(String)); + }); + + it('should call onTimeColumnChange when changed', () => { + const onTimeColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTimeColumnChange={onTimeColumnChange} + onLevelColumnChange={() => {}} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.LogsConfig.columns.time.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onTimeColumnChange).toHaveBeenCalledTimes(1); + expect(onTimeColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onLevelColumnChange when changed', () => { + const onLevelColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTimeColumnChange={() => {}} + onLevelColumnChange={onLevelColumnChange} + onMessageColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.LogsConfig.columns.level.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onLevelColumnChange).toHaveBeenCalledTimes(1); + expect(onLevelColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onMessageColumnChange when changed', () => { + const onMessageColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTimeColumnChange={() => {}} + onLevelColumnChange={() => {}} + onMessageColumnChange={onMessageColumnChange} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.LogsConfig.columns.message.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onMessageColumnChange).toHaveBeenCalledTimes(1); + expect(onMessageColumnChange).toHaveBeenCalledWith('changed'); + }); +}); diff --git a/src/components/configEditor/LogsConfig.tsx b/src/components/configEditor/LogsConfig.tsx new file mode 100644 index 00000000..0cb7484e --- /dev/null +++ b/src/components/configEditor/LogsConfig.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { ConfigSection, ConfigSubSection } from '@grafana/experimental'; +import { Input, Field } from '@grafana/ui'; +import { OtelVersionSelect } from 'components/queryBuilder/OtelVersionSelect'; +import { ColumnHint } from 'types/queryBuilder'; +import { versions as otelVersions } from 'otel'; +import { LabeledInput } from './LabeledInput'; +import { CHLogsConfig } from 'types/config'; +import allLabels from 'labels'; +import { columnLabelToPlaceholder } from 'data/utils'; + +interface LogsConfigProps { + logsConfig?: CHLogsConfig; + onDefaultDatabaseChange: (v: string) => void; + onDefaultTableChange: (v: string) => void; + onOtelEnabledChange: (v: boolean) => void; + onOtelVersionChange: (v: string) => void; + onTimeColumnChange: (v: string) => void; + onLevelColumnChange: (v: string) => void; + onMessageColumnChange: (v: string) => void; +} + +export const LogsConfig = (props: LogsConfigProps) => { + const { + onDefaultDatabaseChange, onDefaultTableChange, + onOtelEnabledChange, onOtelVersionChange, + onTimeColumnChange, onLevelColumnChange, onMessageColumnChange + } = props; + let { + defaultDatabase, defaultTable, + otelEnabled, otelVersion, + timeColumn, levelColumn, messageColumn + } = (props.logsConfig || {}); + const labels = allLabels.components.Config.LogsConfig; + + const otelConfig = otelVersions.find(v => v.version === otelVersion); + if (otelEnabled && otelConfig) { + timeColumn = otelConfig.logColumnMap.get(ColumnHint.Time); + levelColumn = otelConfig.logColumnMap.get(ColumnHint.LogLevel); + messageColumn = otelConfig.logColumnMap.get(ColumnHint.LogMessage); + } + + return ( + + + onDefaultDatabaseChange(e.currentTarget.value)} + label={labels.defaultDatabase.label} + aria-label={labels.defaultDatabase.label} + placeholder={labels.defaultDatabase.placeholder} + /> + + + onDefaultTableChange(e.currentTarget.value)} + label={labels.defaultTable.label} + aria-label={labels.defaultTable.label} + placeholder={labels.defaultTable.placeholder} + /> + + + + + + + + + ); +} diff --git a/src/components/configEditor/QuerySettingsConfig.test.tsx b/src/components/configEditor/QuerySettingsConfig.test.tsx new file mode 100644 index 00000000..10834ef7 --- /dev/null +++ b/src/components/configEditor/QuerySettingsConfig.test.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { QuerySettingsConfig } from './QuerySettingsConfig'; +import allLabels from 'labels'; + +describe('QuerySettingsConfig', () => { + it('should render', () => { + const result = render( + {}} + onQueryTimeoutChange={() => {}} + onValidateSqlChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onDialTimeout when changed', () => { + const onDialTimeout = jest.fn(); + const result = render( + {}} + onValidateSqlChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(allLabels.components.Config.QuerySettingsConfig.dialTimeout.placeholder); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: '10' } }); + fireEvent.blur(input); + expect(onDialTimeout).toBeCalledTimes(1); + expect(onDialTimeout).toBeCalledWith(expect.any(Object)); + }); + + it('should call onQueryTimeout when changed', () => { + const onQueryTimeout = jest.fn(); + const result = render( + {}} + onQueryTimeoutChange={onQueryTimeout} + onValidateSqlChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(allLabels.components.Config.QuerySettingsConfig.queryTimeout.placeholder); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: '10' } }); + fireEvent.blur(input); + expect(onQueryTimeout).toBeCalledTimes(1); + expect(onQueryTimeout).toBeCalledWith(expect.any(Object)); + }); + + it('should call onValidateSqlChange when changed', () => { + const onValidateSqlChange = jest.fn(); + const result = render( + {}} + onQueryTimeoutChange={() => {}} + onValidateSqlChange={onValidateSqlChange} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByRole('checkbox'); + expect(input).toBeInTheDocument(); + fireEvent.click(input); + expect(onValidateSqlChange).toBeCalledTimes(1); + expect(onValidateSqlChange).toBeCalledWith(expect.any(Object)); + }); +}); diff --git a/src/components/configEditor/QuerySettingsConfig.tsx b/src/components/configEditor/QuerySettingsConfig.tsx new file mode 100644 index 00000000..1a096965 --- /dev/null +++ b/src/components/configEditor/QuerySettingsConfig.tsx @@ -0,0 +1,58 @@ +import React, { FormEvent } from 'react'; +import { Switch, Input, Field } from '@grafana/ui'; +import { ConfigSection } from '@grafana/experimental'; +import allLabels from 'labels'; + +interface QuerySettingsConfigProps { + dialTimeout?: string; + queryTimeout?: string; + validateSql?: boolean; + onDialTimeoutChange: (e: FormEvent) => void; + onQueryTimeoutChange: (e: FormEvent) => void; + onValidateSqlChange: (e: FormEvent) => void; +} + +export const QuerySettingsConfig = (props: QuerySettingsConfigProps) => { + const { dialTimeout, queryTimeout, validateSql, onDialTimeoutChange, onQueryTimeoutChange, onValidateSqlChange } = props; + const labels = allLabels.components.Config.QuerySettingsConfig; + + return ( + + + + + + + + + + + + ); +} diff --git a/src/components/configEditor/TracesConfig.test.tsx b/src/components/configEditor/TracesConfig.test.tsx new file mode 100644 index 00000000..2f84bf01 --- /dev/null +++ b/src/components/configEditor/TracesConfig.test.tsx @@ -0,0 +1,463 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { TracesConfig } from './TracesConfig'; +import allLabels from 'labels'; +import { columnLabelToPlaceholder } from 'data/utils'; + +describe('TracesConfig', () => { + it('should render', () => { + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onDefaultDatabase when changed', () => { + const onDefaultDatabaseChange = jest.fn(); + const result = render( + {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(allLabels.components.Config.TracesConfig.defaultDatabase.placeholder); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onDefaultDatabaseChange).toHaveBeenCalledTimes(1); + expect(onDefaultDatabaseChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onDefaultTable when changed', () => { + const onDefaultTableChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={onDefaultTableChange} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(allLabels.components.Config.TracesConfig.defaultTable.placeholder); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onDefaultTableChange).toHaveBeenCalledTimes(1); + expect(onDefaultTableChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onOtelEnabled when changed', () => { + const onOtelEnabledChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={onOtelEnabledChange} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByRole('checkbox'); + expect(input).toBeInTheDocument(); + fireEvent.click(input); + expect(onOtelEnabledChange).toHaveBeenCalledTimes(1); + expect(onOtelEnabledChange).toHaveBeenCalledWith(true); + }); + + it('should call onOtelVersionChange when changed', () => { + const onOtelVersionChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={onOtelVersionChange} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const select = result.getByRole('combobox'); + expect(select).toBeInTheDocument(); + fireEvent.keyDown(select, { key: 'ArrowDown' }); + fireEvent.keyDown(select, { key: 'Enter' }); + expect(onOtelVersionChange).toHaveBeenCalledTimes(2); // 2 from hook + expect(onOtelVersionChange).toHaveBeenCalledWith(expect.any(String)); + }); + + it('should call onTraceIdColumnChange when changed', () => { + const onTraceIdColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={onTraceIdColumnChange} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.traceId.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onTraceIdColumnChange).toHaveBeenCalledTimes(1); + expect(onTraceIdColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onSpanIdColumnChange when changed', () => { + const onSpanIdColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={onSpanIdColumnChange} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.spanId.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onSpanIdColumnChange).toHaveBeenCalledTimes(1); + expect(onSpanIdColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onOperationNameColumnChange when changed', () => { + const onOperationNameColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={onOperationNameColumnChange} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.operationName.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onOperationNameColumnChange).toHaveBeenCalledTimes(1); + expect(onOperationNameColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onParentSpanIdColumnChange when changed', () => { + const onParentSpanIdColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={onParentSpanIdColumnChange} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.parentSpanId.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onParentSpanIdColumnChange).toHaveBeenCalledTimes(1); + expect(onParentSpanIdColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onServiceNameColumnChange when changed', () => { + const onServiceNameColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={onServiceNameColumnChange} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.serviceName.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onServiceNameColumnChange).toHaveBeenCalledTimes(1); + expect(onServiceNameColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onDurationColumnChange when changed', () => { + const onDurationColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={onDurationColumnChange} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.durationTime.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onDurationColumnChange).toHaveBeenCalledTimes(1); + expect(onDurationColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onDurationUnitChange when changed', () => { + const onDurationUnitChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={onDurationUnitChange} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const select = result.getByRole('combobox'); + expect(select).toBeInTheDocument(); + fireEvent.keyDown(select, { key: 'ArrowDown' }); + fireEvent.keyDown(select, { key: 'Enter' }); + expect(onDurationUnitChange).toHaveBeenCalledTimes(1); + expect(onDurationUnitChange).toHaveBeenCalledWith(expect.any(String)); + }); + + it('should call onStartTimeColumnChange when changed', () => { + const onStartTimeColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={onStartTimeColumnChange} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.startTime.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onStartTimeColumnChange).toHaveBeenCalledTimes(1); + expect(onStartTimeColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onTagsColumnChange when changed', () => { + const onTagsColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={onTagsColumnChange} + onServiceTagsColumnChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.tags.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onTagsColumnChange).toHaveBeenCalledTimes(1); + expect(onTagsColumnChange).toHaveBeenCalledWith('changed'); + }); + + it('should call onServiceTagsColumnChange when changed', () => { + const onServiceTagsColumnChange = jest.fn(); + const result = render( + {}} + onDefaultTableChange={() => {}} + onOtelEnabledChange={() => {}} + onOtelVersionChange={() => {}} + onTraceIdColumnChange={() => {}} + onSpanIdColumnChange={() => {}} + onOperationNameColumnChange={() => {}} + onParentSpanIdColumnChange={() => {}} + onServiceNameColumnChange={() => {}} + onDurationColumnChange={() => {}} + onDurationUnitChange={() => {}} + onStartTimeColumnChange={() => {}} + onTagsColumnChange={() => {}} + onServiceTagsColumnChange={onServiceTagsColumnChange} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const input = result.getByPlaceholderText(columnLabelToPlaceholder(allLabels.components.Config.TracesConfig.columns.serviceTags.label)); + expect(input).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'changed' } }); + fireEvent.blur(input); + expect(onServiceTagsColumnChange).toHaveBeenCalledTimes(1); + expect(onServiceTagsColumnChange).toHaveBeenCalledWith('changed'); + }); +}); diff --git a/src/components/configEditor/TracesConfig.tsx b/src/components/configEditor/TracesConfig.tsx new file mode 100644 index 00000000..e45944a7 --- /dev/null +++ b/src/components/configEditor/TracesConfig.tsx @@ -0,0 +1,186 @@ + +import React from 'react'; +import { ConfigSection, ConfigSubSection } from '@grafana/experimental'; +import { Input, Field } from '@grafana/ui'; +import { OtelVersionSelect } from 'components/queryBuilder/OtelVersionSelect'; +import { ColumnHint, TimeUnit } from 'types/queryBuilder'; +import { versions as otelVersions } from 'otel'; +import { LabeledInput } from './LabeledInput'; +import { DurationUnitSelect } from 'components/queryBuilder/DurationUnitSelect'; +import { CHTracesConfig } from 'types/config'; +import allLabels from 'labels'; +import { columnLabelToPlaceholder } from 'data/utils'; + +interface TraceConfigProps { + tracesConfig?: CHTracesConfig; + onDefaultDatabaseChange: (v: string) => void; + onDefaultTableChange: (v: string) => void; + onOtelEnabledChange: (v: boolean) => void; + onOtelVersionChange: (v: string) => void; + onTraceIdColumnChange: (v: string) => void; + onSpanIdColumnChange: (v: string) => void; + onOperationNameColumnChange: (v: string) => void; + onParentSpanIdColumnChange: (v: string) => void; + onServiceNameColumnChange: (v: string) => void; + onDurationColumnChange: (v: string) => void; + onDurationUnitChange: (v: TimeUnit) => void; + onStartTimeColumnChange: (v: string) => void; + onTagsColumnChange: (v: string) => void; + onServiceTagsColumnChange: (v: string) => void; +} + +export const TracesConfig = (props: TraceConfigProps) => { + const { + onDefaultDatabaseChange, onDefaultTableChange, + onOtelEnabledChange, onOtelVersionChange, + onTraceIdColumnChange, onSpanIdColumnChange, onOperationNameColumnChange, onParentSpanIdColumnChange, + onServiceNameColumnChange, onDurationColumnChange, onDurationUnitChange, onStartTimeColumnChange, + onTagsColumnChange, onServiceTagsColumnChange, + } = props; + let { + defaultDatabase, defaultTable, + otelEnabled, otelVersion, + traceIdColumn, spanIdColumn, operationNameColumn, parentSpanIdColumn, serviceNameColumn, + durationColumn, durationUnit, startTimeColumn, tagsColumn, serviceTagsColumn + } = (props.tracesConfig || {}) as CHTracesConfig; + const labels = allLabels.components.Config.TracesConfig; + + const otelConfig = otelVersions.find(v => v.version === otelVersion); + if (otelEnabled && otelConfig) { + startTimeColumn = otelConfig.traceColumnMap.get(ColumnHint.Time); + traceIdColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceId); + spanIdColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceSpanId); + parentSpanIdColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceParentSpanId); + serviceNameColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceServiceName); + operationNameColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceOperationName); + durationColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceDurationTime); + tagsColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceTags); + serviceTagsColumn = otelConfig.traceColumnMap.get(ColumnHint.TraceServiceTags); + durationUnit = otelConfig.traceDurationUnit.toString(); + } + + return ( + + + onDefaultDatabaseChange(e.currentTarget.value)} + label={labels.defaultDatabase.label} + aria-label={labels.defaultDatabase.label} + placeholder={labels.defaultDatabase.placeholder} + /> + + + onDefaultTableChange(e.currentTarget.value)} + label={labels.defaultTable.label} + aria-label={labels.defaultTable.label} + placeholder={labels.defaultTable.placeholder} + /> + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/editor.ts b/src/components/editor.ts deleted file mode 100644 index 1f8c4982..00000000 --- a/src/components/editor.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { getFields } from 'data/ast'; -import { Format } from 'types'; -import { isString } from 'lodash'; - -export const getFormat = (sql: string, selectedFormat: Format): Format => { - if (selectedFormat === Format.AUTO) { - // convention to format as time series - // first field as "time" alias and requires at least 2 fields (time and metric) - const selectList = getFields(sql); - // if there are more than 2 fields, index 1 will be a ',' - if (selectList.length >= 2 && isString(selectList[0])) { - const firstProjection = selectList[0].trim().toLowerCase(); - if (firstProjection.endsWith('as time')) { - return Format.TIMESERIES; - } - if (firstProjection.endsWith('as log_time')) { - return Format.LOGS; - } - } - return Format.TABLE; - } - return selectedFormat; -}; diff --git a/src/components/queryBuilder/AggregateEditor.test.tsx b/src/components/queryBuilder/AggregateEditor.test.tsx new file mode 100644 index 00000000..59505829 --- /dev/null +++ b/src/components/queryBuilder/AggregateEditor.test.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { AggregateEditor } from './AggregateEditor'; +import { selectors } from 'selectors'; +import { AggregateColumn, AggregateType } from 'types/queryBuilder'; + +describe('AggregateEditor', () => { + it('should render with no aggregates', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should render with aggregates', () => { + const testAggregate: AggregateColumn = { aggregateType: AggregateType.Count, column: 'foo', alias: 'f' }; + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + + const firstAggregate = result.getByTestId(selectors.components.QueryBuilder.AggregateEditor.itemWrapper); + expect(firstAggregate).toBeInTheDocument(); + }); + + it('should call onAggregatesChange when add aggregate button is clicked', async () => { + const onAggregatesChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const addButton = result.getByTestId(selectors.components.QueryBuilder.AggregateEditor.addButton); + expect(addButton).toBeInTheDocument(); + await userEvent.click(addButton); + expect(onAggregatesChange).toBeCalledTimes(1); + expect(onAggregatesChange).toBeCalledWith([expect.anything()]); + }); + + it('should call onAggregatesChange when remove aggregate button is clicked', async () => { + const testAggregate: AggregateColumn = { aggregateType: AggregateType.Count, column: 'foo', alias: 'f' }; + const onAggregatesChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const removeButton = result.getByTestId(selectors.components.QueryBuilder.AggregateEditor.itemRemoveButton); + expect(removeButton).toBeInTheDocument(); + await userEvent.click(removeButton); + expect(onAggregatesChange).toBeCalledWith([]); + }); + + it('should call onAggregatesChange when aggregate is updated', async () => { + const inputAggregate: AggregateColumn = { aggregateType: AggregateType.Count, column: 'foo', alias: 'f' }; + const expectedAggregate: AggregateColumn = { aggregateType: AggregateType.Sum, column: 'foo', alias: 'f' }; + const onAggregatesChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const aggregateSelect = result.getAllByRole('combobox')[0]; + expect(aggregateSelect).toBeInTheDocument(); + fireEvent.keyDown(aggregateSelect, { key: 'ArrowDown' }); + fireEvent.keyDown(aggregateSelect, { key: 'ArrowDown' }); + fireEvent.keyDown(aggregateSelect, { key: 'Enter' }); + expect(onAggregatesChange).toBeCalledWith([expectedAggregate]); + }); +}); diff --git a/src/components/queryBuilder/AggregateEditor.tsx b/src/components/queryBuilder/AggregateEditor.tsx new file mode 100644 index 00000000..26031f12 --- /dev/null +++ b/src/components/queryBuilder/AggregateEditor.tsx @@ -0,0 +1,149 @@ +import React, { useState } from 'react'; +import { SelectableValue } from '@grafana/data'; +import { InlineFormLabel, Select, Button, Input } from '@grafana/ui'; +import { AggregateColumn, AggregateType, TableColumn } from 'types/queryBuilder'; +import labels from 'labels'; +import { selectors } from 'selectors'; +import { styles } from 'styles'; + +interface AggregateProps { + columnOptions: Array>; + index: number, + aggregate: AggregateColumn; + updateAggregate: (index: number, aggregate: AggregateColumn) => void; +} + +const aggregateOptions: Array> = [ + { label: 'Count', value: AggregateType.Count }, + { label: 'Sum', value: AggregateType.Sum }, + { label: 'Min', value: AggregateType.Min }, + { label: 'Max', value: AggregateType.Max }, + { label: 'Average', value: AggregateType.Average }, + { label: 'Any', value: AggregateType.Any }, + // { label: 'Distinct Count', value: AggregateType.Count_Distinct }, +]; + +const Aggregate = (props: AggregateProps) => { + const { columnOptions, index, aggregate, updateAggregate } = props; + const [isOpen, setIsOpen] = useState(false); + const [alias, setAlias] = useState(aggregate.alias || ''); + const { aliasLabel } = labels.components.AggregatesEditor; + + return ( + <> + setAlias(e.currentTarget.value)} + onBlur={e => updateAggregate(index, { ...aggregate, alias: e.currentTarget.value })} + placeholder="alias" + /> + + ); +}; + +interface AggregateEditorProps { + allColumns: readonly TableColumn[]; + aggregates: AggregateColumn[]; + onAggregatesChange: (aggregates: AggregateColumn[]) => void; +} + +const allColumnName = '*'; + +export const AggregateEditor = (props: AggregateEditorProps) => { + const { allColumns, aggregates, onAggregatesChange } = props; + const { label, tooltip, addLabel } = labels.components.AggregatesEditor; + const columnOptions: Array> = allColumns.map(c => ({ label: c.name, value: c.name })); + columnOptions.push({ label: allColumnName, value: allColumnName }); + + const addAggregate = () => { + const nextAggregates: AggregateColumn[] = aggregates.slice(); + nextAggregates.push({ column: '', aggregateType: AggregateType.Count }); + onAggregatesChange(nextAggregates); + }; + const removeAggregate = (index: number) => { + const nextAggregates: AggregateColumn[] = aggregates.slice(); + nextAggregates.splice(index, 1); + onAggregatesChange(nextAggregates); + }; + const updateAggregate = (index: number, aggregatesItem: AggregateColumn) => { + const nextAggregates: AggregateColumn[] = aggregates.slice(); + nextAggregates[index] = aggregatesItem; + onAggregatesChange(nextAggregates); + }; + + const fieldLabel = ( + + {label} + + ); + const fieldSpacer =
; + + return ( + <> + {aggregates.map((aggregate, index) => { + const key = `${index}-${aggregate.column}-${aggregate.aggregateType}-${aggregate.alias}`; + return ( +
+ { index === 0 ? fieldLabel : fieldSpacer } + +
+ ); + })} + +
+ {aggregates.length === 0 ? fieldLabel : fieldSpacer} + +
+ + ); +}; diff --git a/src/components/queryBuilder/ColumnSelect.test.tsx b/src/components/queryBuilder/ColumnSelect.test.tsx new file mode 100644 index 00000000..9b93c005 --- /dev/null +++ b/src/components/queryBuilder/ColumnSelect.test.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { ColumnSelect } from './ColumnSelect'; +import { SelectedColumn, TableColumn } from 'types/queryBuilder'; + +describe('ColumnSelect', () => { + const testLabel = 'Label'; + const testTooltip = 'Tooltip'; + + it('should render with empty properties', () => { + const result = render( + {}} + label={testLabel} + tooltip={testTooltip} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should render with valid properties', () => { + const allColumns: readonly TableColumn[] = [{ name: 'foo', type: 'string', picklistValues: [] }]; + const selectedColumn: SelectedColumn = { name: 'foo' }; + const result = render( + {}} + label={testLabel} + tooltip={testTooltip} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByText('foo')).not.toBeUndefined(); + }); + + it('should call onColumnChange when a new column is selected', () => { + const allColumns: readonly TableColumn[] = [ + { name: 'one', type: 'string', picklistValues: [] }, + { name: 'two', type: 'string', picklistValues: [] } + ]; + const onColumnChange = jest.fn(); + const result = render( + + ); + expect(result.container.firstChild).not.toBeNull(); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); + fireEvent.keyDown(multiSelect, { key: 'Enter' }); + expect(onColumnChange).toHaveBeenCalledTimes(1); + expect(onColumnChange).toHaveBeenCalledWith(expect.any(Object)); + }); +}); diff --git a/src/components/queryBuilder/ColumnSelect.tsx b/src/components/queryBuilder/ColumnSelect.tsx new file mode 100644 index 00000000..340d2676 --- /dev/null +++ b/src/components/queryBuilder/ColumnSelect.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { SelectableValue } from '@grafana/data'; +import { InlineFormLabel, Select } from '@grafana/ui'; +import { ColumnHint, SelectedColumn, TableColumn } from 'types/queryBuilder'; +import { styles } from 'styles'; + +interface ColumnSelectProps { + allColumns: readonly TableColumn[]; + selectedColumn: SelectedColumn | undefined; + onColumnChange: (c: SelectedColumn | undefined) => void; + columnFilterFn?: (c: TableColumn) => boolean; + columnHint?: ColumnHint; + label: string; + tooltip: string; + disabled?: boolean; + invalid?: boolean; + wide?: boolean; + inline?: boolean; + clearable?: boolean; +} + +const defaultFilterFn = () => true; + +export const ColumnSelect = (props: ColumnSelectProps) => { + const { allColumns, selectedColumn, onColumnChange, columnFilterFn, columnHint, label, tooltip, disabled, invalid, wide, inline, clearable } = props; + const selectedColumnName = selectedColumn?.name; + const columns: Array> = allColumns. + filter(columnFilterFn || defaultFilterFn). + map(c => ({ label: c.name, value: c.name })); + + // Select component WILL NOT display the value if it isn't present in the options. + let staleOption = false; + if (selectedColumn && !columns.find(c => c.value === selectedColumn.name)) { + columns.push({ label: selectedColumn.name, value: selectedColumn.name }); + staleOption = true; + } + + const onChange = (selected: SelectableValue) => { + if (!selected || !selected.value) { + onColumnChange(undefined); + return; + } + + const column = allColumns.find(c => c.name === selected!.value)!; + if (!column) { + return; + } + + onColumnChange({ + name: column.name, + type: column.type, + hint: columnHint + }); + } + + const labelStyle = 'query-keyword ' + (inline ? styles.QueryEditor.inlineField : ''); + + return ( +
+ + {label} + + + disabled={disabled} + invalid={invalid || staleOption} + options={columns} + value={selectedColumnName} + placeholder={selectedColumnName || undefined} + onChange={onChange} + width={wide ? 25 : 20} + menuPlacement={'bottom'} + isClearable={clearable === undefined || clearable} + /> +
+ ); +}; diff --git a/src/components/queryBuilder/ColumnsEditor.test.tsx b/src/components/queryBuilder/ColumnsEditor.test.tsx new file mode 100644 index 00000000..e79c62cb --- /dev/null +++ b/src/components/queryBuilder/ColumnsEditor.test.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import { ColumnsEditor } from './ColumnsEditor'; +import { TableColumn, SelectedColumn } from 'types/queryBuilder'; +import { selectors } from 'selectors'; + +describe('ColumnsEditor', () => { + const allColumns: readonly TableColumn[] = [ + { name: 'name', type: 'string', picklistValues: [] }, + { name: 'dummy', type: 'string', picklistValues: [] }, + ]; + const selectedColumns: SelectedColumn[] = [ + { name: 'name' }, + ]; + + it('should render default value when no options passed', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByTestId(selectors.components.QueryBuilder.ColumnsEditor.multiSelectWrapper)).toBeInTheDocument(); + }); + + it('should render the correct values when passed', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByTestId(selectors.components.QueryBuilder.ColumnsEditor.multiSelectWrapper)).toBeInTheDocument(); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); + expect(result.getByText('name')).toBeInTheDocument(); + expect(result.getByText('dummy')).toBeInTheDocument(); + }); + + it('should call onSelectedColumnsChange when a column is selected', () => { + const onSelectedColumnsChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByTestId(selectors.components.QueryBuilder.ColumnsEditor.multiSelectWrapper)).toBeInTheDocument(); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); + fireEvent.keyDown(multiSelect, { key: 'Enter' }); + + expect(onSelectedColumnsChange).toBeCalledTimes(1); + expect(onSelectedColumnsChange).toBeCalledWith([expect.any(Object), expect.any(Object)]); + }); + + it('should call onSelectedColumnsChange when a column is deselected', () => { + const onSelectedColumnsChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByTestId(selectors.components.QueryBuilder.ColumnsEditor.multiSelectWrapper)).toBeInTheDocument(); + + const removeButton = result.getByTestId('times'); // find by "x" symbol + fireEvent.click(removeButton); + expect(onSelectedColumnsChange).toBeCalledTimes(1); + expect(onSelectedColumnsChange).toBeCalledWith([]); + }); + + it('should close when clicked outside', () => { + const onSelectedColumnsChange = jest.fn(); + const result = render(); + expect(onSelectedColumnsChange).toHaveBeenCalledTimes(0); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + + expect(result.queryAllByText('dummy').length).toBe(0); // is popup closed + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); + expect(result.getByText('dummy')).toBeInTheDocument(); // is popup open + fireEvent.keyDown(multiSelect, { key: 'Esc' }); + expect(result.queryAllByText('dummy').length).toBe(0); // is popup closed + expect(onSelectedColumnsChange).toHaveBeenCalledTimes(0); + }); +}); diff --git a/src/components/queryBuilder/ColumnsEditor.tsx b/src/components/queryBuilder/ColumnsEditor.tsx new file mode 100644 index 00000000..66be0e88 --- /dev/null +++ b/src/components/queryBuilder/ColumnsEditor.tsx @@ -0,0 +1,102 @@ +import React, { useState, useEffect } from 'react'; +import { InlineFormLabel, MultiSelect } from '@grafana/ui'; +import { SelectableValue } from '@grafana/data'; +import { TableColumn, SelectedColumn } from 'types/queryBuilder'; +import labels from 'labels'; +import { selectors } from 'selectors'; +import { styles } from 'styles'; + +interface ColumnsEditorProps { + allColumns: readonly TableColumn[]; + selectedColumns: SelectedColumn[]; + onSelectedColumnsChange: (selectedColumns: SelectedColumn[]) => void; + disabled?: boolean; + showAllOption?: boolean; +} + +function getCustomColumns(columnNames: string[], allColumns: readonly TableColumn[]): Array> { + const columnNamesSet = new Set(columnNames); + return allColumns. + filter(c => columnNamesSet.has(c.name)). + map(c => ({ label: c.name, value: c.name })); +} + +const allColumnName = '*'; + +export const ColumnsEditor = (props: ColumnsEditorProps) => { + const { allColumns, selectedColumns, onSelectedColumnsChange, disabled, showAllOption } = props; + const [customColumns, setCustomColumns] = useState>>([]); + const [isOpen, setIsOpen] = useState(false); + const allColumnNames = allColumns.map(c => ({ label: c.name, value: c.name })); + if (showAllOption) { + allColumnNames.push({ label: allColumnName, value: allColumnName }); + } + const selectedColumnNames = (selectedColumns || []).map(c => ({ label: c.name, value: c.name })); + const { label, tooltip } = labels.components.ColumnsEditor; + + const options = [...allColumnNames, ...customColumns]; + + useEffect(() => { + if (allColumns.length === 0) { + return; + } + + const columnNames = selectedColumns.map(c => c.name); + const customColumns = getCustomColumns(columnNames, allColumns); + setCustomColumns(customColumns); + }, [allColumns, selectedColumns]); + + const onChange = (selected: Array>): void => { + setIsOpen(false); + const selectedColumnNames = new Set(selected.map(s => s.value!)); + const customColumnNames = new Set(customColumns.map(c => c.value!)) + const columnMap = new Map(); + const currentColumnMap = new Map(); + allColumns.forEach(c => columnMap.set(c.name, c)); + selectedColumns.forEach(c => currentColumnMap.set(c.name, c)); + + const excludeAllColumn = selectedColumnNames.size > 1; + const nextSelectedColumns: SelectedColumn[] = []; + for (let columnName of selectedColumnNames) { + if (excludeAllColumn && columnName === allColumnName) { + continue; + } + + const tableColumn = columnMap.get(columnName); + const existingColumn = currentColumnMap.get(columnName); + + if (existingColumn) { + nextSelectedColumns.push(existingColumn); + } else { + nextSelectedColumns.push({ + name: columnName, + type: tableColumn?.type || 'String', + custom: customColumnNames.has(columnName) + }); + } + } + + onSelectedColumnsChange(nextSelectedColumns); + }; + + return ( +
+ + {label} + +
+ + disabled={disabled} + options={options} + value={selectedColumnNames} + isOpen={isOpen} + onOpenMenu={() => setIsOpen(true)} + onCloseMenu={() => setIsOpen(false)} + onChange={onChange} + allowCustomValue={true} + menuPlacement={'bottom'} + /> +
+
+ ); +}; diff --git a/src/components/queryBuilder/DatabaseSelect.tsx b/src/components/queryBuilder/DatabaseSelect.tsx deleted file mode 100644 index 4274fe9f..00000000 --- a/src/components/queryBuilder/DatabaseSelect.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Select } from '@grafana/ui'; -import { SelectableValue } from '@grafana/data'; -import { Datasource } from '../../data/CHDatasource'; -import { selectors } from './../../selectors'; -import { EditorField } from '@grafana/experimental'; - -export type Props = { datasource: Datasource; value?: string; onChange: (value: string) => void }; - -export const DatabaseSelect = (props: Props) => { - const { datasource, onChange, value } = props; - const [list, setList] = useState>>([]); - const { label, tooltip } = selectors.components.QueryEditor.QueryBuilder.DATABASE; - useEffect(() => { - async function fetchList() { - const list = await datasource.fetchDatabases(); - const values = list.map((t) => ({ label: t, value: t })); - // Add selected value to the list if it does not exist. - if (value && !list.find((x) => x === value)) { - values.push({ label: value!, value: value! }); - } - setList(values); - } - fetchList(); - }, [datasource, value]); - - const defaultDatabase = datasource.settings.jsonData.defaultDatabase; - const db = value ?? defaultDatabase; - return ( - - - - ); -}; diff --git a/src/components/queryBuilder/DatabaseTableSelect.test.tsx b/src/components/queryBuilder/DatabaseTableSelect.test.tsx new file mode 100644 index 00000000..22780e3f --- /dev/null +++ b/src/components/queryBuilder/DatabaseTableSelect.test.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import { DatabaseSelect, TableSelect, DatabaseTableSelect } from './DatabaseTableSelect'; +import { Datasource } from '../../data/CHDatasource'; + +const defaultDB = 'default'; +const testTable = 'samples'; + +describe('DatabaseSelect', () => { + it('should render with empty options', async () => { + const mockDs = {} as Datasource; + mockDs.getDefaultDatabase = jest.fn(() => ''); + mockDs.fetchDatabases = jest.fn(() => Promise.resolve([])); + + const result = await waitFor(() => render( {}} />)); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should render with valid options', async () => { + const mockDs = {} as Datasource; + mockDs.getDefaultDatabase = jest.fn(() => defaultDB); + mockDs.fetchDatabases = jest.fn(() => Promise.resolve([defaultDB])); + + const result = await waitFor(() => render( {}} />)); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByText(defaultDB)).toBeInTheDocument(); + }); + + it('selects a default database when none is provided', async () => { + const mockDs = {} as Datasource; + mockDs.getDefaultDatabase = jest.fn(() => defaultDB); + mockDs.fetchDatabases = jest.fn(() => Promise.resolve([defaultDB])); + const onDatabaseChange = jest.fn(); + + const result = await waitFor(() => render()); + expect(result.container.firstChild).not.toBeNull(); + + expect(onDatabaseChange).toBeCalledTimes(1); + expect(onDatabaseChange).toBeCalledWith(defaultDB); + }); + + it('should call onDatabaseChange when a database is selected', async () => { + const mockDs = {} as Datasource; + mockDs.getDefaultDatabase = jest.fn(() => defaultDB); + mockDs.fetchDatabases = jest.fn(() => Promise.resolve([defaultDB])); + const onDatabaseChange = jest.fn(); + + const result = await waitFor(() => render()); + expect(result.container.firstChild).not.toBeNull(); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); // "other" db, a custom value + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); // "default" db + fireEvent.keyDown(multiSelect, { key: 'Enter' }); + expect(onDatabaseChange).toBeCalledTimes(1); + expect(onDatabaseChange).toBeCalledWith(defaultDB); + }); +}); + +describe('TableSelect', () => { + it('should render with empty options', async () => { + const mockDs = {} as Datasource; + mockDs.fetchTables = jest.fn(() => Promise.resolve([])); + + const result = await waitFor(() => render( {}} />)); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should render with valid options', async () => { + const mockDs = {} as Datasource; + mockDs.fetchTables = jest.fn(() => Promise.resolve([testTable])); + + const result = await waitFor(() => render( {}} />)); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByText(testTable)).toBeInTheDocument(); + }); + + // TODO: this hook is disabled in the component for now + // it('selects a default table when none is provided', async () => { + // const mockDs = {} as Datasource; + // mockDs.fetchTables = jest.fn(() => Promise.resolve([testTable])); + // const onTableChange = jest.fn(); + + // const result = await waitFor(() => render()); + // expect(result.container.firstChild).not.toBeNull(); + + // expect(onTableChange).toBeCalledTimes(1); + // expect(onTableChange).toBeCalledWith(testTable); + // }); + + it('should call onTableChange when a table is selected', async () => { + const mockDs = {} as Datasource; + mockDs.fetchTables = jest.fn(() => Promise.resolve([testTable])); + const onTableChange = jest.fn(); + + const result = await waitFor(() => render()); + expect(result.container.firstChild).not.toBeNull(); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); // "other" table, a custom value + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); // test table + fireEvent.keyDown(multiSelect, { key: 'Enter' }); + expect(onTableChange).toBeCalledTimes(1); + expect(onTableChange).toBeCalledWith(testTable); + }); +}); + +describe('DatabaseTableSelect', () => { + it('should render the combined components', async () => { + const mockDs = {} as Datasource; + mockDs.fetchDatabases = jest.fn(() => Promise.resolve([])); + mockDs.fetchTables = jest.fn(() => Promise.resolve([])); + + const result = await waitFor(() => render( + {}} + table={testTable} + onTableChange={() => {}} + /> + )); + expect(result.container.firstChild).not.toBeNull(); + expect(result.container.firstChild?.childNodes).toHaveLength(2 * 2); // 2 components with a fragment of 2 components + }); +}); diff --git a/src/components/queryBuilder/DatabaseTableSelect.tsx b/src/components/queryBuilder/DatabaseTableSelect.tsx new file mode 100644 index 00000000..76328086 --- /dev/null +++ b/src/components/queryBuilder/DatabaseTableSelect.tsx @@ -0,0 +1,114 @@ +import React, { useEffect } from 'react'; +import { InlineFormLabel, Select } from '@grafana/ui'; +import { Datasource } from '../../data/CHDatasource'; +import labels from 'labels'; +import { styles } from '../../styles'; +import useTables from 'hooks/useTables'; +import useDatabases from 'hooks/useDatabases'; + +export type DatabaseSelectProps = { + datasource: Datasource; + database: string; + onDatabaseChange: (value: string) => void +}; + +export const DatabaseSelect = (props: DatabaseSelectProps) => { + const { datasource, onDatabaseChange, database } = props; + const databases = useDatabases(datasource); + const { label, tooltip, empty } = labels.components.DatabaseSelect; + + const options = databases.map(d => ({ label: d, value: d })); + options.push({ label: empty, value: '' }); // Allow a blank value + + // Add selected value to the list if it does not exist. + // When loading an existing query, the saved value may no longer be in the list + if (database && !databases.includes(database)) { + options.push({ label: database, value: database }); + } + + useEffect(() => { + // Auto select default db + if (!database) { + onDatabaseChange(datasource.getDefaultDatabase()); + } + }, [datasource, database, onDatabaseChange]); + + return ( + <> + + {label} + + + + ); +}; + +export type TableSelectProps = { + datasource: Datasource; + database: string; + table: string; + onTableChange: (value: string) => void; +}; + +export const TableSelect = (props: TableSelectProps) => { + const { datasource, onTableChange, database, table } = props; + const tables = useTables(datasource, database); + const { label, tooltip, empty } = labels.components.TableSelect; + + const options = tables.map(t => ({ label: t, value: t })); + options.push({ label: empty, value: '' }); // Allow a blank value + + // Include saved value in case it's no longer listed + if (table && !tables.includes(table)) { + options.push({ label: table, value: table }); + } + + useEffect(() => { + // Auto select first/default table + if (database && !table && tables.length > 0) { + onTableChange(datasource.getDefaultTable() || tables[0]); + } + }, [database, table, tables, datasource, onTableChange]); + + return ( + <> + + {label} + + + + ); +}; + +export type DatabaseTableSelectProps = { + datasource: Datasource; + database: string; + onDatabaseChange: (value: string) => void + table: string; + onTableChange: (value: string) => void; +}; + +export const DatabaseTableSelect = (props: DatabaseTableSelectProps) => { + const { datasource, database, onDatabaseChange, table, onTableChange } = props; + + return ( +
+ + +
+ ); +} diff --git a/src/components/queryBuilder/DurationUnitSelect.tsx b/src/components/queryBuilder/DurationUnitSelect.tsx new file mode 100644 index 00000000..78896b19 --- /dev/null +++ b/src/components/queryBuilder/DurationUnitSelect.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { TimeUnit } from "types/queryBuilder"; +import allLabels from 'labels'; +import { InlineFormLabel, Select } from '@grafana/ui'; +import { SelectableValue } from '@grafana/data'; +import { styles } from 'styles'; + +interface DurationUnitSelectProps { + unit: TimeUnit; + onChange: (u: TimeUnit) => void; + disabled?: boolean; + inline?: boolean; +}; + +const durationUnitOptions: ReadonlyArray> = [ + { label: TimeUnit.Seconds, value: TimeUnit.Seconds }, + { label: TimeUnit.Milliseconds, value: TimeUnit.Milliseconds }, + { label: TimeUnit.Microseconds, value: TimeUnit.Microseconds }, + { label: TimeUnit.Nanoseconds, value: TimeUnit.Nanoseconds }, +]; + +export const DurationUnitSelect = (props: DurationUnitSelectProps) => { + const { unit, onChange, disabled, inline } = props; + const { label, tooltip } = allLabels.components.TraceQueryBuilder.columns.durationUnit; + + return ( +
+ + {label} + + + disabled={disabled} + options={durationUnitOptions as Array>} + value={unit} + onChange={v => onChange(v.value!)} + width={inline ? 25 : 30} + menuPlacement={'bottom'} + /> +
+ ); +}; diff --git a/src/components/queryBuilder/EditorTypeSwitcher.test.tsx b/src/components/queryBuilder/EditorTypeSwitcher.test.tsx new file mode 100644 index 00000000..f8a6702d --- /dev/null +++ b/src/components/queryBuilder/EditorTypeSwitcher.test.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { EditorTypeSwitcher } from './EditorTypeSwitcher'; +import { CHQuery, CHSqlQuery, EditorType } from 'types/sql'; +import labels from 'labels'; + +const options = { + SQLEditor: labels.types.EditorType.sql, + QueryBuilder: labels.types.EditorType.builder, +}; + +describe('EditorTypeSwitcher', () => { + it('should render default query', () => { + const result = render( + {}} + onRunQuery={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByLabelText(options.SQLEditor)).not.toBeChecked(); + expect(result.getByLabelText(options.QueryBuilder)).toBeChecked(); + }); + + it('should render legacy query (query without query type)', () => { + const result = render( + {}} + onRunQuery={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByLabelText(options.SQLEditor)).toBeChecked(); + expect(result.getByLabelText(options.QueryBuilder)).not.toBeChecked(); + }); + + it('should render SQL editor', () => { + const result = render( + {}} + onRunQuery={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByLabelText(options.SQLEditor)).toBeChecked(); + expect(result.getByLabelText(options.QueryBuilder)).not.toBeChecked(); + }); + + it('should render Query Builder', () => { + const result = render( + {}} + onRunQuery={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByLabelText(options.SQLEditor)).not.toBeChecked(); + expect(result.getByLabelText(options.QueryBuilder)).toBeChecked(); + }); +}); diff --git a/src/components/queryBuilder/EditorTypeSwitcher.tsx b/src/components/queryBuilder/EditorTypeSwitcher.tsx new file mode 100644 index 00000000..abc14c19 --- /dev/null +++ b/src/components/queryBuilder/EditorTypeSwitcher.tsx @@ -0,0 +1,107 @@ +import React, { useState } from 'react'; +import { SelectableValue } from '@grafana/data'; +import { RadioButtonGroup, ConfirmModal, InlineFormLabel } from '@grafana/ui'; +import { getQueryOptionsFromSql } from '../queryBuilder/utils'; +import { generateSql } from 'data/sqlGenerator'; +import labels from 'labels'; +import { EditorType, CHQuery, defaultCHBuilderQuery } from 'types/sql'; +import { QueryBuilderOptions } from 'types/queryBuilder'; +import isString from 'lodash/isString'; +import { mapQueryTypeToGrafanaFormat } from 'data/utils'; + +interface CHEditorTypeSwitcherProps { + query: CHQuery; + onChange: (query: CHQuery) => void; + onRunQuery: () => void; +} + +const options: Array> = [ + { label: labels.types.EditorType.sql, value: EditorType.SQL }, + { label: labels.types.EditorType.builder, value: EditorType.Builder }, +]; + +/** + * Component for switching between the SQL and Query Builder editors. + */ +export const EditorTypeSwitcher = (props: CHEditorTypeSwitcherProps) => { + const { query, onChange } = props; + const { label, tooltip, switcher, cannotConvert } = labels.components.EditorTypeSwitcher; + const editorType: EditorType = query.editorType || EditorType.Builder; + const [confirmModalState, setConfirmModalState] = useState(false); + const [cannotConvertModalState, setCannotConvertModalState] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const onEditorTypeChange = (editorType: EditorType, confirmed = false) => { + // TODO: component state has updated, but not local state. + if (query.editorType === EditorType.SQL && editorType === EditorType.Builder && !confirmed) { + const queryOptionsFromSql = getQueryOptionsFromSql(query.rawSql); + if (isString(queryOptionsFromSql)) { + setCannotConvertModalState(true); + setErrorMessage(queryOptionsFromSql); + } else { + setConfirmModalState(true); + } + } else { + let builderOptions: QueryBuilderOptions; + switch (query.editorType) { + case EditorType.Builder: + builderOptions = query.builderOptions; + break; + case EditorType.SQL: + builderOptions = getQueryOptionsFromSql(query.rawSql) as QueryBuilderOptions; + break; + default: + builderOptions = defaultCHBuilderQuery.builderOptions; + break; + } + if (editorType === EditorType.SQL) { + onChange({ + ...query, + editorType: EditorType.SQL, + queryType: builderOptions.queryType, + rawSql: generateSql(builderOptions), + format: mapQueryTypeToGrafanaFormat(builderOptions.queryType), + meta: { builderOptions }, + }); + } else if (editorType === EditorType.Builder) { + onChange({ + ...query, + editorType: EditorType.Builder, + rawSql: generateSql(builderOptions), + builderOptions + }); + } + } + }; + const onConfirmEditorTypeChange = () => { + onEditorTypeChange(EditorType.Builder, true); + setConfirmModalState(false); + setCannotConvertModalState(false); + }; + return ( + + + {label} + + onEditorTypeChange(e)} /> + setConfirmModalState(false)} + /> + setCannotConvertModalState(false)} + /> + + ); +}; diff --git a/src/components/queryBuilder/Fields.test.tsx b/src/components/queryBuilder/Fields.test.tsx deleted file mode 100644 index b46c932f..00000000 --- a/src/components/queryBuilder/Fields.test.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { FieldsEditor } from './Fields'; -import { FullField } from './../../types'; - -describe('FieldsEditor', () => { - const fields: string[] = ['Name', 'StageName']; - const list: FullField[] = [ - { name: 'Name', label: 'Field Name', type: 'string', picklistValues: [] }, - { name: 'Type', label: 'Field Type', type: 'string', picklistValues: [] }, - { name: 'StageName', label: 'Stage', type: 'string', picklistValues: [] }, - { name: 'Dummy', label: 'Dummy', type: 'string', picklistValues: [] }, - ]; - it('should render default value when no options passed', () => { - const result = render( {}} />); - expect(result.container.firstChild).not.toBeNull(); - expect(result.getByTestId('query-builder-fields-multi-select-container')).toBeInTheDocument(); - }); - it('should render the correct values when passed', () => { - const onFieldsChange = jest.fn(); - const result = render(); - expect(result.container.firstChild).not.toBeNull(); - expect(result.getByTestId('query-builder-fields-multi-select-container')).toBeInTheDocument(); - expect(result.queryAllByText('Standard Fields').length).toBe(0); - expect(result.getByText('Field Name')).toBeInTheDocument(); - expect(result.getByText('Stage')).toBeInTheDocument(); - expect(result.queryAllByText('Dummy').length).toBe(0); - expect(onFieldsChange).toHaveBeenCalledTimes(0); - }); - it('should render the popup values when clicked', async () => { - const onFieldsChange = jest.fn(); - - const result = render(); - expect(onFieldsChange).toHaveBeenCalledTimes(0); - - expect(result.queryAllByText('Dummy').length).toBe(0); // Popup should be in closed state - fireEvent.focus(screen.getByRole('combobox')); - fireEvent.keyDown(screen.getByRole('combobox'), { key: 'ArrowDown' }); - expect(result.getByText('Dummy')).toBeInTheDocument(); // Popup should be in the open state - expect(result.getByText('Field Type')).toBeInTheDocument(); - fireEvent.click(result.getByText('Field Type')); - expect(result.queryAllByText('Dummy').length).toBe(0); // Popup should be in closed state - expect(result.getByText('Field Type')).toBeInTheDocument(); - fireEvent.blur(screen.getByRole('combobox')); - expect(onFieldsChange).toHaveBeenCalledTimes(2); - - expect(result.queryAllByText('Dummy').length).toBe(0); // Popup should be in closed state - }); - - it('should close when clicked outside', () => { - const onFieldsChange = jest.fn(); - - const result = render(); - expect(onFieldsChange).toHaveBeenCalledTimes(0); - - expect(result.queryAllByText('Dummy').length).toBe(0); // Popup should be in closed state - fireEvent.focus(screen.getByRole('combobox')); - fireEvent.keyDown(screen.getByRole('combobox'), { key: 'ArrowDown' }); - expect(result.getByText('Dummy')).toBeInTheDocument(); // Popup should be in the open state - fireEvent.keyDown(screen.getByRole('combobox'), { key: 'Esc' }); - expect(result.queryAllByText('Dummy').length).toBe(0); // Popup should be in closed state - expect(onFieldsChange).toHaveBeenCalledTimes(0); - }); -}); diff --git a/src/components/queryBuilder/Fields.tsx b/src/components/queryBuilder/Fields.tsx deleted file mode 100644 index 2f1952d6..00000000 --- a/src/components/queryBuilder/Fields.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { MultiSelect } from '@grafana/ui'; -import { SelectableValue } from '@grafana/data'; -import { FullField } from './../../types'; -import { selectors } from './../../selectors'; -import { EditorField } from '@grafana/experimental'; - -interface FieldsEditorProps { - fieldsList: FullField[]; - fields: string[]; - onFieldsChange: (fields: string[]) => void; -} -export const FieldsEditor = (props: FieldsEditorProps) => { - const columns = (props.fieldsList || []).map((f) => ({ label: f.label, value: f.name })); - const [custom, setCustom] = useState>>([]); - const [isOpen, setIsOpen] = useState(false); - const defaultFields: Array> = []; - const [fields, setFields] = useState(props.fields || []); - const { label, tooltipTable } = selectors.components.QueryEditor.QueryBuilder.SELECT; - - useEffect(() => { - if (props.fieldsList.length === 0) { - return; - } - setFields(props.fields); - const customFields = getCustomFields(props.fields, props.fieldsList); - setCustom(customFields); - }, [props.fieldsList, props.fields]); - - const onFieldsChange = (fields: string[]) => { - const cleanFields = cleanupFields(fields); - setFields(cleanFields); - const customFields = getCustomFields(fields, props.fieldsList); - setCustom(customFields); - }; - - const cleanupFields = (fields: string[]): string[] => { - if ( - defaultFields.map((d) => d.value).includes(fields[0]) || - defaultFields.map((d) => d.value).includes(fields[fields.length - 1]) - ) { - return [fields[fields.length - 1]]; - } - return fields; - }; - - const onUpdateField = () => { - props.onFieldsChange(fields); - }; - - const onChange = (e: Array>): void => { - setIsOpen(false); - onFieldsChange(e.map((v) => v.value!)); - }; - - return ( - - - options={[...columns, ...defaultFields, ...custom]} - value={fields && fields.length > 0 ? fields : []} - isOpen={isOpen} - onOpenMenu={() => setIsOpen(true)} - onCloseMenu={() => setIsOpen(false)} - onChange={onChange} - onBlur={onUpdateField} - allowCustomValue={true} - width={50} - /> - - ); -}; - -function getCustomFields(fields: string[], columns: FullField[]) { - return fields - .filter((f) => { - return columns.findIndex((c) => c.name === f) === -1; - }) - .map((f) => ({ label: f, value: f })); -} diff --git a/src/components/queryBuilder/Filters.test.tsx b/src/components/queryBuilder/FilterEditor.test.tsx similarity index 87% rename from src/components/queryBuilder/Filters.test.tsx rename to src/components/queryBuilder/FilterEditor.test.tsx index 9e29578e..b87e8d66 100644 --- a/src/components/queryBuilder/Filters.test.tsx +++ b/src/components/queryBuilder/FilterEditor.test.tsx @@ -1,15 +1,15 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { defaultNewFilter, FilterEditor, FiltersEditor, FilterValueEditor } from './Filters'; -import { selectors } from './../../selectors'; -import { BooleanFilter, DateFilter, Filter, FilterOperator, MultiFilter, NumberFilter, StringFilter } from 'types'; +import { defaultNewFilter, FilterEditor, FiltersEditor, FilterValueEditor } from './FilterEditor'; +import { selectors } from 'selectors'; +import { BooleanFilter, DateFilter, Filter, FilterOperator, MultiFilter, NumberFilter, StringFilter } from 'types/queryBuilder'; -describe('FiltersEditor', () => { +describe('FilterEditor', () => { describe('FiltersEditor', () => { it('renders correctly', async () => { const onFiltersChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).not.toBeNull(); expect(result.getAllByText(selectors.components.QueryEditor.QueryBuilder.WHERE.label).length).toBe(1); expect(result.getByTestId('query-builder-filters-add-button')).toBeInTheDocument(); @@ -35,10 +35,12 @@ describe('FiltersEditor', () => { operator: FilterOperator.IsNotNull, }, ]; - const result = render( {}} />); + const result = render( {}} />); expect(result.container.firstChild).not.toBeNull(); expect(result.getAllByText(selectors.components.QueryEditor.QueryBuilder.WHERE.label).length).toBe(1); - expect(result.getByTestId('query-builder-filters-add-button')).toBeInTheDocument(); + expect(result.queryByTestId('query-builder-filters-add-button')).not.toBeInTheDocument(); + expect(result.getByTestId('query-builder-filters-inline-add-button')).toBeInTheDocument(); + expect(result.getAllByTestId('query-builder-filters-inline-add-button').length).toBe(1); expect(result.getAllByTestId('query-builder-filters-remove-button').length).toBe(filters.length); }); it('should call the onFiltersChange with correct args', async () => { @@ -59,12 +61,14 @@ describe('FiltersEditor', () => { }, ]; const onFiltersChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).not.toBeNull(); expect(result.getAllByText(selectors.components.QueryEditor.QueryBuilder.WHERE.label).length).toBe(1); - expect(result.getByTestId('query-builder-filters-add-button')).toBeInTheDocument(); + expect(result.queryByTestId('query-builder-filters-add-button')).not.toBeInTheDocument(); + expect(result.getByTestId('query-builder-filters-inline-add-button')).toBeInTheDocument(); + expect(result.getAllByTestId('query-builder-filters-inline-add-button').length).toBe(1); expect(result.getAllByTestId('query-builder-filters-remove-button').length).toBe(filters.length); - await userEvent.click(result.getByTestId('query-builder-filters-add-button')); + await userEvent.click(result.getByTestId('query-builder-filters-inline-add-button')); expect(onFiltersChange).toBeCalledTimes(1); expect(onFiltersChange).toHaveBeenNthCalledWith(1, [...filters, defaultNewFilter]); await userEvent.click(result.getAllByTestId('query-builder-filters-remove-button')[0]); @@ -76,7 +80,7 @@ describe('FiltersEditor', () => { it('renders correctly', async () => { const result = render( { it('should have all provided fields in the select', async () => { const result = render( { const onFilterChange = jest.fn(); const result = render( { const onFilterChange = jest.fn(); const result = render( { it('should render nothing for null operator', async () => { const result = render( { filterType: 'custom', }; const onFilterChange = jest.fn(); - const result = render(); + const result = render(); expect(result!.container.firstChild).not.toBeNull(); expect(result!.getByTestId('query-builder-filters-boolean-value-container')).toBeInTheDocument(); expect(result!.getByLabelText('True')).toBeChecked(); @@ -217,7 +221,7 @@ describe('FiltersEditor', () => { value: 123, }; const onFilterChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).not.toBeNull(); expect(result.getByTestId('query-builder-filters-number-value-container')).toBeInTheDocument(); expect(result.getByTestId('query-builder-filters-number-value-input')).toBeInTheDocument(); @@ -235,7 +239,7 @@ describe('FiltersEditor', () => { condition: 'AND', }; const onFilterChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).toBeNull(); }); it('should render date filter with value for date operator with time range', async () => { @@ -248,7 +252,7 @@ describe('FiltersEditor', () => { value: 'now()', }; const onFilterChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).not.toBeNull(); expect(result.getByTestId('query-builder-filters-date-value-container')).toBeInTheDocument(); expect(result.getByText('NOW')).toBeInTheDocument(); @@ -265,11 +269,10 @@ describe('FiltersEditor', () => { const onFilterChange = jest.fn(); const result = render( { const onFilterChange = jest.fn(); const result = render( { value: 'ABC Corp', }; const onFilterChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).not.toBeNull(); expect(result.getByTestId('query-builder-filters-single-string-value-container')).toBeInTheDocument(); }); @@ -344,7 +346,7 @@ describe('FiltersEditor', () => { value: ['ABC Corp'], }; const onFilterChange = jest.fn(); - const result = render(); + const result = render(); expect(result.container.firstChild).not.toBeNull(); expect(result.getByTestId('query-builder-filters-multi-string-value-container')).toBeInTheDocument(); }); diff --git a/src/components/queryBuilder/Filters.tsx b/src/components/queryBuilder/FilterEditor.tsx similarity index 79% rename from src/components/queryBuilder/Filters.tsx rename to src/components/queryBuilder/FilterEditor.tsx index 435ee0d5..944b43f4 100644 --- a/src/components/queryBuilder/Filters.tsx +++ b/src/components/queryBuilder/FilterEditor.tsx @@ -1,11 +1,10 @@ import React, { useState } from 'react'; import { SelectableValue } from '@grafana/data'; -import { Button, Input, MultiSelect, RadioButtonGroup, Select } from '@grafana/ui'; -import { Filter, FilterOperator, FullField, NullFilter } from '../../types'; -import * as utils from './utils'; -import { selectors } from '../../selectors'; -import { styles } from '../../styles'; -import { EditorField, EditorFieldGroup } from '@grafana/experimental'; +import { Button, InlineFormLabel, Input, MultiSelect, RadioButtonGroup, Select } from '@grafana/ui'; +import { Filter, FilterOperator, TableColumn, NullFilter } from 'types/queryBuilder'; +import * as utils from 'components/queryBuilder/utils'; +import labels from 'labels'; +import { styles } from 'styles'; const boolValues: Array> = [ { value: true, label: 'True' }, @@ -41,12 +40,12 @@ const standardTimeOptions: Array> = [ export const defaultNewFilter: NullFilter = { filterType: 'custom', condition: 'AND', - key: 'Id', - type: 'id', + key: '', + type: '', operator: FilterOperator.IsNotNull, }; export interface PredefinedFilter { - restrictToFields?: FullField[]; + restrictToFields?: readonly TableColumn[]; } const FilterValueNumberItem = (props: { value: number; onChange: (value: number) => void }) => { @@ -67,7 +66,11 @@ const FilterValueNumberItem = (props: { value: number; onChange: (value: number) const FilterValueSingleStringItem = (props: { value: string; onChange: (value: string) => void }) => { return (
- props.onChange(e.currentTarget.value)} /> + props.onChange(e.currentTarget.value)} + />
); }; @@ -88,11 +91,11 @@ const FilterValueMultiStringItem = (props: { value: string[]; onChange: (value: }; export const FilterValueEditor = (props: { - fieldsList: FullField[]; + allColumns: readonly TableColumn[]; filter: Filter; onFilterChange: (filter: Filter) => void; }) => { - const { filter, onFilterChange, fieldsList } = props; + const { filter, onFilterChange, allColumns: fieldsList } = props; const getOptions = () => { const matchedFilter = fieldsList.find((f) => f.name === filter.key); return matchedFilter?.picklistValues || []; @@ -168,16 +171,16 @@ export const FilterValueEditor = (props: { }; export const FilterEditor = (props: { - fieldsList: FullField[]; + allColumns: readonly TableColumn[]; index: number; filter: Filter & PredefinedFilter; onFilterChange: (index: number, filter: Filter) => void; }) => { - const { index, filter, fieldsList, onFilterChange } = props; + const { index, filter, allColumns: fieldsList, onFilterChange } = props; const [isOpen, setIsOpen] = useState(false); const getFields = () => { const values = (filter.restrictToFields || fieldsList).map((f) => { - return { label: f.label, value: f.name }; + return { label: f.name, value: f.name }; }); // Add selected value to the list if it does not exist. if (filter?.key && !values.find((x) => x.value === filter.key)) { @@ -329,38 +332,44 @@ export const FilterEditor = (props: { onFilterChange(index, filter); }; return ( - + <> {index !== 0 && ( onFilterConditionChange(e!)} /> )} onFilterOperatorChange(e.value!)} + menuPlacement={'bottom'} /> - - + + ); }; export const FiltersEditor = (props: { - fieldsList: FullField[]; + allColumns: readonly TableColumn[]; filters: Filter[]; onFiltersChange: (filters: Filter[]) => void; }) => { - const { filters = [], onFiltersChange, fieldsList = [] } = props; - const { label, tooltip, AddLabel, RemoveLabel } = selectors.components.QueryEditor.QueryBuilder.WHERE; + const { filters = [], onFiltersChange, allColumns: fieldsList = [] } = props; + const { label, tooltip, addLabel } = labels.components.FilterEditor; const addFilter = () => { onFiltersChange([...filters, { ...defaultNewFilter }]); }; @@ -375,36 +384,61 @@ export const FiltersEditor = (props: { onFiltersChange(newFilters); }; return ( - - - {filters.map((filter, index) => { - return ( -
- - -
- ); - })} - -
-
+ <> + {filters.length === 0 && ( +
+ + {label} + + +
+ )} + {filters.map((filter, index) => { + return ( +
+ {index === 0 ? ( + + {label} + + ) : ( +
+ )} + +
+ ); + })} + {filters.length !== 0 && ( +
+
+ +
+ )} + ); }; diff --git a/src/components/queryBuilder/GroupBy.test.tsx b/src/components/queryBuilder/GroupBy.test.tsx deleted file mode 100644 index 2a429d55..00000000 --- a/src/components/queryBuilder/GroupBy.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { GroupByEditor } from './GroupBy'; - -describe('GroupByEditor', () => { - it('renders correctly', () => { - const result = render( {}} />); - expect(result.container.firstChild).not.toBeNull(); - }); -}); diff --git a/src/components/queryBuilder/GroupBy.tsx b/src/components/queryBuilder/GroupBy.tsx deleted file mode 100644 index e062126d..00000000 --- a/src/components/queryBuilder/GroupBy.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, { useState } from 'react'; -import { MultiSelect } from '@grafana/ui'; -import { SelectableValue } from '@grafana/data'; -import { FullField } from './../../types'; -import { selectors } from './../../selectors'; -import { EditorField } from '@grafana/experimental'; - -interface GroupByEditorProps { - fieldsList: FullField[]; - groupBy: string[]; - onGroupByChange: (groupBy: string[]) => void; -} -export const GroupByEditor = (props: GroupByEditorProps) => { - const columns: SelectableValue[] = (props.fieldsList || []).map((f) => ({ label: f.label, value: f.name })); - const [isOpen, setIsOpen] = useState(false); - const [groupBy, setGroupBy] = useState(props.groupBy || []); - const { label, tooltip } = selectors.components.QueryEditor.QueryBuilder.GROUP_BY; - const onChange = (e: Array>) => { - setIsOpen(false); - setGroupBy(e.map((item) => item.value!)); - }; - // Add selected value to the list if it does not exist. - groupBy.filter((x) => !columns.some((y) => y.value === x)).forEach((x) => columns.push({ value: x, label: x })); - return ( - - setIsOpen(true)} - onCloseMenu={() => setIsOpen(false)} - onChange={onChange} - onBlur={() => props.onGroupByChange(groupBy)} - value={groupBy} - allowCustomValue={true} - width={25} - /> - - ); -}; diff --git a/src/components/queryBuilder/GroupByEditor.test.tsx b/src/components/queryBuilder/GroupByEditor.test.tsx new file mode 100644 index 00000000..6b69bd00 --- /dev/null +++ b/src/components/queryBuilder/GroupByEditor.test.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import { GroupByEditor } from './GroupByEditor'; +import { TableColumn } from 'types/queryBuilder'; + +describe('GroupByEditor', () => { + it('should render with empty properties', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should render with valid properties', () => { + const allColumns: readonly TableColumn[] = [{ name: 'a', type: 'string', picklistValues: [] }]; + const groupBy: string[] = ['a', 'b']; + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onGroupByChange when a new column is selected', () => { + const allColumns: readonly TableColumn[] = [{ name: 'a', type: 'string', picklistValues: [] }]; + const groupBy: string[] = ['b']; + const onGroupByChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const multiSelect = result.getByRole('combobox'); + expect(multiSelect).toBeInTheDocument(); + + expect(result.queryAllByText('a').length).toBe(0); // is popup closed + fireEvent.keyDown(multiSelect, { key: 'ArrowDown' }); + expect(result.queryAllByText('a').length).toBe(1); // is popup open + fireEvent.keyDown(multiSelect, { key: 'Enter' }); + expect(result.queryAllByText('a').length).toBe(0); // is popup closed + expect(onGroupByChange).toBeCalledTimes(1); + expect(onGroupByChange).toBeCalledWith(expect.any(Object)); + }); +}); diff --git a/src/components/queryBuilder/GroupByEditor.tsx b/src/components/queryBuilder/GroupByEditor.tsx new file mode 100644 index 00000000..69a31041 --- /dev/null +++ b/src/components/queryBuilder/GroupByEditor.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import { InlineFormLabel, MultiSelect } from '@grafana/ui'; +import { SelectableValue } from '@grafana/data'; +import { TableColumn } from 'types/queryBuilder'; +import labels from 'labels'; +import { styles } from 'styles'; +import { selectors } from 'selectors'; + +interface GroupByEditorProps { + allColumns: readonly TableColumn[]; + groupBy: string[]; + onGroupByChange: (groupBy: string[]) => void; +} + +export const GroupByEditor = (props: GroupByEditorProps) => { + const { allColumns, groupBy, onGroupByChange } = props; + const [isOpen, setIsOpen] = useState(false); + const { label, tooltip } = labels.components.GroupByEditor; + const options: Array> = allColumns.map(c => ({ label: c.name, value: c.name })); + + const onChange = (selection: Array>) => { + setIsOpen(false); + onGroupByChange(selection.map(s => s.value!)); + }; + + return ( +
+ + {label} + +
+ setIsOpen(true)} + onCloseMenu={() => setIsOpen(false)} + value={groupBy} + onChange={onChange} + allowCustomValue={true} + menuPlacement={'bottom'} + /> +
+
+ ); +}; diff --git a/src/components/queryBuilder/Limit.test.tsx b/src/components/queryBuilder/Limit.test.tsx deleted file mode 100644 index efc3767a..00000000 --- a/src/components/queryBuilder/Limit.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { LimitEditor } from './Limit'; - -describe('LimitEditor', () => { - it('renders correctly', () => { - const result = render( {}} />); - expect(result.container.firstChild).not.toBeNull(); - }); -}); diff --git a/src/components/queryBuilder/Limit.tsx b/src/components/queryBuilder/Limit.tsx deleted file mode 100644 index bf24b722..00000000 --- a/src/components/queryBuilder/Limit.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { useState } from 'react'; -import { Input } from '@grafana/ui'; -import { selectors } from './../../selectors'; -import { EditorField } from '@grafana/experimental'; - -interface LimitEditorProps { - limit: number; - onLimitChange: (limit: number) => void; -} -export const LimitEditor = (props: LimitEditorProps) => { - const [limit, setLimit] = useState(props.limit || 10); - const { label, tooltip } = selectors.components.QueryEditor.QueryBuilder.LIMIT; - return ( - - setLimit(e.currentTarget.valueAsNumber)} - onBlur={() => props.onLimitChange(limit)} - /> - - ); -}; diff --git a/src/components/queryBuilder/LimitEditor.test.tsx b/src/components/queryBuilder/LimitEditor.test.tsx new file mode 100644 index 00000000..f7fabe97 --- /dev/null +++ b/src/components/queryBuilder/LimitEditor.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { LimitEditor } from './LimitEditor'; +import { selectors } from 'selectors'; + +describe('LimitEditor', () => { + it('should render', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onLimitChange when limit is changed', () => { + const onLimitChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const limitInput = result.getByTestId(selectors.components.QueryBuilder.LimitEditor.input); + expect(limitInput).toBeInTheDocument(); + fireEvent.change(limitInput, { target: { value: 5 } }); + fireEvent.blur(limitInput); + expect(limitInput).toHaveValue(5); + expect(onLimitChange).toBeCalledTimes(1); + expect(onLimitChange).toBeCalledWith(5); + }); +}); diff --git a/src/components/queryBuilder/LimitEditor.tsx b/src/components/queryBuilder/LimitEditor.tsx new file mode 100644 index 00000000..fbe059e7 --- /dev/null +++ b/src/components/queryBuilder/LimitEditor.tsx @@ -0,0 +1,31 @@ +import React, { useState } from 'react'; +import { InlineFormLabel, Input } from '@grafana/ui'; +import labels from 'labels'; +import { selectors } from 'selectors'; + +interface LimitEditorProps { + limit: number; + onLimitChange: (limit: number) => void; +} + +export const LimitEditor = (props: LimitEditorProps) => { + const [limit, setLimit] = useState(props.limit || 0); + const { label, tooltip } = labels.components.LimitEditor; + + return ( +
+ + {label} + + setLimit(e.currentTarget.valueAsNumber)} + onBlur={() => props.onLimitChange(limit)} + /> +
+ ); +}; diff --git a/src/components/queryBuilder/LogLevelField.tsx b/src/components/queryBuilder/LogLevelField.tsx deleted file mode 100644 index 8a0bfc1f..00000000 --- a/src/components/queryBuilder/LogLevelField.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { SelectableValue } from '@grafana/data'; -import { Select } from '@grafana/ui'; -import { selectors } from './../../selectors'; -import { FullField } from 'types'; -import { EditorField } from '@grafana/experimental'; - -interface LogLevelEditorProps { - fieldsList: FullField[]; - onLogLevelFieldChange: (logLevelField: string) => void; - logLevelField: string | null; -} - -export const LogLevelFieldEditor = (props: LogLevelEditorProps) => { - const { label, tooltip } = selectors.components.QueryEditor.QueryBuilder.LOG_LEVEL_FIELD; - const columns: SelectableValue[] = (props.fieldsList || []) - .filter((f) => f.name !== '*') - .map((f) => ({ label: f.label, value: f.name })); - return ( - - onMetricAggregationChange(e.value!)} - value={metric.aggregation} - /> - - width={25} - className={styles.Common.inlineSelect} - options={columns} - isOpen={isOpen} - onOpenMenu={() => setIsOpen(true)} - onCloseMenu={() => setIsOpen(false)} - onChange={onMetricFieldChange} - value={metric.field} - /> - - {ALIAS.label} - - setAlias(e.currentTarget.value)} - onBlur={onMetricAliasChange} - placeholder="alias" - /> - - ); -}; - -interface MetricsEditorProps { - fieldsList: FullField[]; - metrics: BuilderMetricField[]; - onMetricsChange: (metrics: BuilderMetricField[]) => void; -} -export const MetricsEditor = (props: MetricsEditorProps) => { - const { metrics, onMetricsChange, fieldsList = [] } = props; - const { label, tooltipAggregate, AddLabel, RemoveLabel } = selectors.components.QueryEditor.QueryBuilder.AGGREGATES; - const onMetricAdd = () => { - const newMetric: BuilderMetricField = { field: '', aggregation: BuilderMetricFieldAggregation.Count }; - onMetricsChange([...metrics, newMetric]); - }; - const onMetricRemove = (index: number) => { - const newMetrics: BuilderMetricField[] = [...metrics]; - newMetrics.splice(index, 1); - onMetricsChange(newMetrics); - }; - return ( - - - {metrics.map((metric, index) => { - return ( -
- - {metrics.length > 1 && ( - - )} -
- ); - })} - -
-
- ); -}; diff --git a/src/components/queryBuilder/ModeEditor.test.tsx b/src/components/queryBuilder/ModeEditor.test.tsx deleted file mode 100644 index cabae035..00000000 --- a/src/components/queryBuilder/ModeEditor.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { ModeEditor } from './ModeEditor'; -import { BuilderMode } from 'types'; - -describe('ModeEditor', () => { - it('renders correctly', () => { - const result = render( {}} />); - expect(result.container.firstChild).not.toBeNull(); - }); -}); diff --git a/src/components/queryBuilder/ModeEditor.tsx b/src/components/queryBuilder/ModeEditor.tsx deleted file mode 100644 index 6c73a53c..00000000 --- a/src/components/queryBuilder/ModeEditor.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { RadioButtonGroup } from '@grafana/ui'; -import { BuilderMode } from 'types'; -import { selectors } from './../../selectors'; -import { EditorField } from '@grafana/experimental'; - -interface ModeEditorProps { - mode: BuilderMode; - onModeChange: (mode: BuilderMode) => void; -} -export const ModeEditor = (props: ModeEditorProps) => { - const { options, label, tooltip } = selectors.components.QueryEditor.QueryBuilder.TYPES; - const modes = [ - { value: BuilderMode.List, label: options.LIST }, - { value: BuilderMode.Aggregate, label: options.AGGREGATE }, - { value: BuilderMode.Trend, label: options.TREND }, - ]; - return ( - - options={modes} value={props.mode} onChange={(e) => props.onModeChange(e!)} /> - - ); -}; diff --git a/src/components/queryBuilder/ModeSwitch.test.tsx b/src/components/queryBuilder/ModeSwitch.test.tsx new file mode 100644 index 00000000..f4d0bfda --- /dev/null +++ b/src/components/queryBuilder/ModeSwitch.test.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import { ModeSwitch } from './ModeSwitch'; + +describe('ModeSwitch', () => { + const labelA = 'A'; + const labelB = 'B'; + const label = 'test'; + const tooltip = 'tooltip'; + + it('should render', () => { + const result = render( + {}} + label={label} + tooltip={tooltip} + /> + ); + + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onChange when mode is changed', () => { + const onChange = jest.fn(); + const result = render( + + ); + expect(result.container.firstChild).not.toBeNull(); + + const buttonA = result.getByText(labelA); + expect(buttonA).toBeInTheDocument(); + const buttonB = result.getByText(labelB); + expect(buttonB).toBeInTheDocument(); + + fireEvent.click(buttonB); + expect(onChange).toBeCalledTimes(1); + expect(onChange).toBeCalledWith(true); + + result.rerender( + + ); + + fireEvent.click(buttonA); + expect(onChange).toBeCalledTimes(2); + expect(onChange).toBeCalledWith(false); + }); +}); diff --git a/src/components/queryBuilder/ModeSwitch.tsx b/src/components/queryBuilder/ModeSwitch.tsx new file mode 100644 index 00000000..23d2c070 --- /dev/null +++ b/src/components/queryBuilder/ModeSwitch.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { RadioButtonGroup, InlineFormLabel } from '@grafana/ui'; + +export interface ModeSwitchProps { + labelA: string; + labelB: string; + value: boolean; + onChange: (value: boolean) => void; + label: string; + tooltip: string; +}; + +/** + * Component for switching between modes. Boxes are labeled unlike regular Switch. + */ +export const ModeSwitch = (props: ModeSwitchProps) => { + const { labelA, labelB, value, onChange, label, tooltip } = props; + + const options = [ + { + label: labelA, + value: false, + }, + { + label: labelB, + value: true, + }, + ]; + + return ( +
+ + {label} + + + options={options} + value={value} + onChange={v => onChange(v)} + /> +
+ ); +} diff --git a/src/components/queryBuilder/OrderBy.tsx b/src/components/queryBuilder/OrderBy.tsx deleted file mode 100644 index 53bbc521..00000000 --- a/src/components/queryBuilder/OrderBy.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import React from 'react'; -import { SelectableValue } from '@grafana/data'; -import { Button, Select } from '@grafana/ui'; -import { - OrderBy, - OrderByDirection, - SqlBuilderOptions, - FullField, - BuilderMode, - BuilderMetricField, - SqlBuilderOptionsAggregate, -} from './../../types'; -import { selectors } from './../../selectors'; -import { styles } from '../../styles'; -import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/experimental'; - -const OrderByItem = (props: { - index: number; - fieldsList: Array>; - orderBy: OrderBy[]; - orderByItem: OrderBy; - onOrderByItemsChange: (orderBy: OrderBy[]) => void; -}) => { - const columns: SelectableValue[] = props.fieldsList || []; - const { index, orderByItem } = props; - const sortOptions = [ - { value: OrderByDirection.ASC, label: 'ASC' }, - { value: OrderByDirection.DESC, label: 'DESC' }, - ]; - const onOrderBySortFieldUpdate = (name: string) => { - const orderByItems: OrderBy[] = [...props.orderBy].map((o, i) => { - return { ...o, name: i === index ? name : o.name }; - }); - props.onOrderByItemsChange(orderByItems); - }; - const onOrderBySortDirectionUpdate = (direction: OrderByDirection) => { - const orderByItems: OrderBy[] = [...props.orderBy].map((o, i) => { - return { ...o, dir: i === index ? direction : o.dir }; - }); - props.onOrderByItemsChange(orderByItems); - }; - return ( - - - - value={orderByItem.dir} - className={styles.Common.inlineSelect} - width={12} - options={sortOptions} - onChange={(e) => onOrderBySortDirectionUpdate(e.value!)} - menuPlacement={'bottom'} - /> - - ); -}; - -interface OrderByEditorProps { - fieldsList: Array>; - orderBy: OrderBy[]; - onOrderByItemsChange: (orderBy: OrderBy[]) => void; -} -export const OrderByEditor = (props: OrderByEditorProps) => { - const columns: SelectableValue[] = props.fieldsList || []; - const { label, tooltip, AddLabel, RemoveLabel } = selectors.components.QueryEditor.QueryBuilder.ORDER_BY; - const onOrderByAdd = () => { - const orderByItems: OrderBy[] = [...props.orderBy]; - orderByItems.push({ - name: columns[0]?.value || 'Name', - dir: OrderByDirection.ASC, - }); - props.onOrderByItemsChange(orderByItems); - }; - const onOrderByRemove = (index: number) => { - const orderByItems: OrderBy[] = [...props.orderBy]; - orderByItems.splice(index, 1); - props.onOrderByItemsChange(orderByItems); - }; - return columns.length === 0 ? null : ( - - - - {props.orderBy.map((o, index) => { - return ( -
- - -
- ); - })} - -
-
-
- ); -}; - -export const getOrderByFields = ( - builder: SqlBuilderOptions, - fieldsList: FullField[] -): Array> => { - let values: Array> | Array<{ value: string; label: string }> = []; - switch (builder.mode) { - case BuilderMode.Aggregate: - values = [ - ...(builder.fields || []).map((g) => { - return { value: g, label: g }; - }), - ...((builder.metrics as BuilderMetricField[]) || []).map((m) => { - return { value: `${m.aggregation}(${m.field})`, label: `${m.aggregation}(${m.field})` }; - }), - ...((builder.groupBy as string[]) || []).map((g) => { - return { value: g, label: g }; - }), - ]; - break; - case BuilderMode.List: - default: - values = fieldsList.map((m) => { - return { value: m.name, label: m.label }; - }); - } - // Add selected value to the list if it does not exist. - (builder as SqlBuilderOptionsAggregate).orderBy - ?.filter((x) => !values.some((y: { value: string; label: string } | SelectableValue) => y.value === x.name)) - .forEach((x) => values.push({ value: x.name, label: x.name })); - return values; -}; diff --git a/src/components/queryBuilder/OrderBy.test.tsx b/src/components/queryBuilder/OrderByEditor.test.tsx similarity index 55% rename from src/components/queryBuilder/OrderBy.test.tsx rename to src/components/queryBuilder/OrderByEditor.test.tsx index 0e1c2068..101dfb3b 100644 --- a/src/components/queryBuilder/OrderBy.test.tsx +++ b/src/components/queryBuilder/OrderByEditor.test.tsx @@ -1,23 +1,30 @@ import React from 'react'; import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { OrderByEditor, getOrderByFields } from './OrderBy'; -import { BuilderMetricFieldAggregation, BuilderMode, OrderByDirection } from './../../types'; +import { OrderByEditor, getOrderByOptions } from './OrderByEditor'; +import { AggregateType, BuilderMode, OrderByDirection, QueryType, TableColumn } from 'types/queryBuilder'; +import { SelectableValue } from '@grafana/data'; + +const testOptions: Array> = [ + { label: 'foo', value: 'foo' }, + { label: 'bar', value: 'bar' }, + { label: 'baz', value: 'baz' }, +]; describe('OrderByEditor', () => { it('should render null when no fields passed', () => { - const result = render( {}} />); + const result = render( {}} />); expect(result.container.firstChild).toBeNull(); }); it('should render component when fields passed', () => { const result = render( - {}} /> + {}} /> ); expect(result.container.firstChild).not.toBeNull(); }); it('should render default add button when no orderby fields passed', () => { const result = render( - {}} /> + {}} /> ); expect(result.container.firstChild).not.toBeNull(); expect(result.getByTestId('query-builder-orderby-add-button')).toBeInTheDocument(); @@ -27,9 +34,9 @@ describe('OrderByEditor', () => { it('should render remove button when at least one orderby fields passed', () => { const result = render( {}} + onOrderByChange={() => {}} /> ); expect(result.container.firstChild).not.toBeNull(); @@ -40,12 +47,12 @@ describe('OrderByEditor', () => { it('should render add/remove buttons correctly when multiple orderby elements passed', () => { const result = render( {}} + onOrderByChange={() => {}} /> ); expect(result.container.firstChild).not.toBeNull(); @@ -53,49 +60,59 @@ describe('OrderByEditor', () => { expect(result.getAllByTestId('query-builder-orderby-item-wrapper').length).toBe(2); expect(result.getAllByTestId('query-builder-orderby-remove-button').length).toBe(2); }); + it('should render label only once', () => { + const result = render( + {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByTestId('query-builder-orderby-item-label')).toBeInTheDocument(); + }); it('should add default item when add button clicked', async () => { - const onOrderByItemsChange = jest.fn(); + const onOrderByChange = jest.fn(); const result = render( ); expect(result.container.firstChild).not.toBeNull(); expect(result.getByTestId('query-builder-orderby-add-button')).toBeInTheDocument(); expect(result.queryByTestId('query-builder-orderby-item-wrapper')).not.toBeInTheDocument(); expect(result.queryByTestId('query-builder-orderby-remove-button')).not.toBeInTheDocument(); - expect(onOrderByItemsChange).toBeCalledTimes(0); + expect(onOrderByChange).toBeCalledTimes(0); await userEvent.click(result.getByTestId('query-builder-orderby-add-button')); - expect(onOrderByItemsChange).toBeCalledTimes(1); - expect(onOrderByItemsChange).toBeCalledWith([{ name: 'foo', dir: OrderByDirection.ASC }]); + expect(onOrderByChange).toBeCalledTimes(1); + expect(onOrderByChange).toBeCalledWith([{ name: 'foo', dir: OrderByDirection.ASC }]); }); - it('should add and remove items when remove button clicked', async () => { - const onOrderByItemsChange = jest.fn(); + it('should remove items when remove button clicked', async () => { + const onOrderByChange = jest.fn(); const result = render( ); expect(result.container.firstChild).not.toBeNull(); - expect(onOrderByItemsChange).toBeCalledTimes(0); + expect(onOrderByChange).toBeCalledTimes(0); await userEvent.click(result.getAllByTestId('query-builder-orderby-remove-button')[1]); await userEvent.click(result.getAllByTestId('query-builder-orderby-remove-button')[0]); await userEvent.click(result.getAllByTestId('query-builder-orderby-add-button')[0]); - expect(onOrderByItemsChange).toBeCalledTimes(3); - expect(onOrderByItemsChange).toHaveBeenNthCalledWith(1, [{ name: 'foo', dir: OrderByDirection.ASC }]); - expect(onOrderByItemsChange).toHaveBeenNthCalledWith(2, [{ name: 'bar', dir: OrderByDirection.ASC }]); - expect(onOrderByItemsChange).toHaveBeenNthCalledWith(3, [ + expect(onOrderByChange).toBeCalledTimes(3); + expect(onOrderByChange).toHaveBeenNthCalledWith(1, [{ name: 'foo', dir: OrderByDirection.ASC }]); + expect(onOrderByChange).toHaveBeenNthCalledWith(2, [{ name: 'bar', dir: OrderByDirection.ASC }]); + expect(onOrderByChange).toHaveBeenNthCalledWith(3, [ { name: 'foo', dir: OrderByDirection.ASC }, { name: 'bar', dir: OrderByDirection.ASC }, { name: 'foo', dir: OrderByDirection.ASC }, @@ -103,192 +120,196 @@ describe('OrderByEditor', () => { }); }); -describe('getOrderByFields', () => { - const sampleFields = [ +describe('getOrderByOptions', () => { + const allColumns: readonly TableColumn[] = [ { name: 'field1', - label: 'Field1', type: 'string', picklistValues: [], }, { - name: 'field11', - label: 'Field11', + name: 'field2', type: 'string', picklistValues: [], }, { - name: 'field2', - label: 'Field2', + name: 'field3', type: 'string', picklistValues: [], }, { - name: 'field3', - label: 'Field3', + name: 'field4', type: 'string', picklistValues: [], }, ]; - it('list view', () => { + + it('should return all columns as options', () => { expect( - getOrderByFields( + getOrderByOptions( { - mode: BuilderMode.List, database: 'db', table: 'foo', - fields: ['field1', 'field3'], + queryType: QueryType.Table, + columns: [{ name: 'field1' }, { name: 'field3' }], }, - sampleFields + allColumns ) ).toStrictEqual([ { - label: 'Field1', + label: 'field1', value: 'field1', }, { - label: 'Field11', - value: 'field11', - }, - { - label: 'Field2', + label: 'field2', value: 'field2', }, { - label: 'Field3', + label: 'field3', value: 'field3', }, + { + label: 'field4', + value: 'field4', + }, ]); }); - it('aggregated view - no group by and no metrics', () => { + it('should return only selected columns for aggregate query', () => { expect( - getOrderByFields( + getOrderByOptions( { - mode: BuilderMode.Aggregate, database: 'db', table: 'foo', - fields: [], - metrics: [], + queryType: QueryType.Table, + columns: [{ name: 'field1' }], + aggregates: [{ column: 'field2', aggregateType: AggregateType.Max }], }, - sampleFields + allColumns ) - ).toStrictEqual([]); + ).toStrictEqual([ + { + label: 'field1', + value: 'field1', + }, + { + label: 'max(field2)', + value: 'max(field2)', + } + ]); }); - it('aggregated view - no group by and with two metrics', () => { + it('should return correct label and value for aggregates with aliases', () => { expect( - getOrderByFields( + getOrderByOptions( { - mode: BuilderMode.Aggregate, database: 'db', table: 'foo', - fields: [], - metrics: [ - { field: 'field2', aggregation: BuilderMetricFieldAggregation.Max }, - { field: 'field1', aggregation: BuilderMetricFieldAggregation.Sum }, - ], + queryType: QueryType.Table, + aggregates: [{ column: 'field1', aggregateType: AggregateType.Max, alias: 'a' }], }, - sampleFields + allColumns ) ).toStrictEqual([ { - value: 'max(field2)', - label: 'max(field2)', - }, - { - value: 'sum(field1)', - label: 'sum(field1)', - }, + label: 'max(field1) as a', + value: 'a', + } ]); }); - it('aggregated view - two group by and with no metrics', () => { + it('should show options from selected columns, aggregates, and groupBy', () => { expect( - getOrderByFields( + getOrderByOptions( { - mode: BuilderMode.Aggregate, database: 'db', table: 'foo', - fields: [], - metrics: [], - groupBy: ['field3', 'field1'], + queryType: QueryType.Table, + columns: [{ name: 'field1' }], + aggregates: [ + { column: 'field2', aggregateType: AggregateType.Max }, + ], + groupBy: ['field2'] }, - sampleFields + allColumns ) ).toStrictEqual([ - { - value: 'field3', - label: 'field3', - }, { value: 'field1', label: 'field1', }, + { + value: 'max(field2)', + label: 'max(field2)', + }, + { + value: 'field2', + label: 'field2', + }, ]); }); - it('aggregated view - two group by and with two metrics', () => { + it('aggregated view - two group by and with no aggregates', () => { expect( - getOrderByFields( + getOrderByOptions( { - mode: BuilderMode.Aggregate, database: 'db', table: 'foo', - fields: [], - metrics: [ - { field: 'field2', aggregation: BuilderMetricFieldAggregation.Max }, - { field: 'field1', aggregation: BuilderMetricFieldAggregation.Sum }, - ], + queryType: QueryType.Table, + columns: [], + aggregates: [], groupBy: ['field3', 'field1'], }, - sampleFields + allColumns ) ).toStrictEqual([ { - value: 'max(field2)', - label: 'max(field2)', + value: 'field1', + label: 'field1', }, { - value: 'sum(field1)', - label: 'sum(field1)', + value: 'field2', + label: 'field2', }, { value: 'field3', label: 'field3', }, { - value: 'field1', - label: 'field1', + value: 'field4', + label: 'field4', }, ]); }); - it('trend view', () => { + it('aggregated view - two group by and with two metrics', () => { expect( - getOrderByFields( + getOrderByOptions( { - mode: BuilderMode.Trend, database: 'db', table: 'foo', - fields: [], - metrics: [{ field: 'field2', aggregation: BuilderMetricFieldAggregation.Max }], - timeField: 'field3', - timeFieldType: 'datetime', + queryType: QueryType.Table, + mode: BuilderMode.Aggregate, + columns: [], + aggregates: [ + { column: 'field2', aggregateType: AggregateType.Max }, + { column: 'field1', aggregateType: AggregateType.Sum }, + ], + groupBy: ['field3', 'field1'], }, - sampleFields + allColumns ) ).toStrictEqual([ { - label: 'Field1', - value: 'field1', + value: 'max(field2)', + label: 'max(field2)', }, { - label: 'Field11', - value: 'field11', + value: 'sum(field1)', + label: 'sum(field1)', }, { - label: 'Field2', - value: 'field2', + value: 'field3', + label: 'field3', }, { - label: 'Field3', - value: 'field3', + value: 'field1', + label: 'field1', }, ]); }); diff --git a/src/components/queryBuilder/OrderByEditor.tsx b/src/components/queryBuilder/OrderByEditor.tsx new file mode 100644 index 00000000..8fa56227 --- /dev/null +++ b/src/components/queryBuilder/OrderByEditor.tsx @@ -0,0 +1,168 @@ +import React from 'react'; +import { SelectableValue } from '@grafana/data'; +import { Button, InlineFormLabel, Select } from '@grafana/ui'; +import { + OrderBy, + OrderByDirection, + QueryBuilderOptions, + TableColumn, +} from 'types/queryBuilder'; +import allLabels from 'labels'; +import { styles } from 'styles'; +import { isAggregateQuery } from 'data/sqlGenerator'; + +interface OrderByItemProps { + columnOptions: Array>; + index: number, + orderByItem: OrderBy; + updateOrderByItem: (index: number, orderByItem: OrderBy) => void; +} + +const sortOptions = [ + { label: 'ASC', value: OrderByDirection.ASC }, + { label: 'DESC', value: OrderByDirection.DESC }, +]; + +const OrderByItem = (props: OrderByItemProps) => { + const { columnOptions, index, orderByItem, updateOrderByItem } = props; + + return ( + <> + + + value={orderByItem.dir} + className={styles.Common.inlineSelect} + width={12} + options={sortOptions} + onChange={e => updateOrderByItem(index, { ...orderByItem, dir: e.value! })} + menuPlacement={'bottom'} + /> + + ); +}; + +interface OrderByEditorProps { + orderByOptions: Array>; + orderBy: OrderBy[]; + onOrderByChange: (orderBy: OrderBy[]) => void; +} +export const OrderByEditor = (props: OrderByEditorProps) => { + const { orderByOptions, orderBy, onOrderByChange } = props; + const { label, tooltip, addLabel } = allLabels.components.OrderByEditor; + + const addOrderByItem = () => { + const nextOrderBy: OrderBy[] = orderBy.slice(); + nextOrderBy.push({ name: orderByOptions[0]?.value!, dir: OrderByDirection.ASC }); + onOrderByChange(nextOrderBy); + }; + const removeOrderByItem = (index: number) => { + const nextOrderBy: OrderBy[] = orderBy.slice(); + nextOrderBy.splice(index, 1); + onOrderByChange(nextOrderBy); + }; + const updateOrderByItem = (index: number, orderByItem: OrderBy) => { + const nextOrderBy: OrderBy[] = orderBy.slice(); + nextOrderBy[index] = orderByItem; + onOrderByChange(nextOrderBy); + }; + + if (orderByOptions.length === 0) { + return null; + } + + const fieldLabel = ( + + {label} + + ); + const fieldSpacer =
; + + return ( + <> + {orderBy.map((orderByItem, index) => { + const key = `${index}-${orderByItem.name}-${orderByItem.dir}`; + return ( +
+ { index === 0 ? fieldLabel : fieldSpacer } + +
+ ); + })} + +
+ {orderBy.length === 0 ? fieldLabel : fieldSpacer} + +
+ + ); +}; + +export const getOrderByOptions = (builder: QueryBuilderOptions, allColumns: readonly TableColumn[]): Array> => { + let allOptions: Array> = []; + + if (isAggregateQuery(builder)) { + builder.columns?.forEach(c => { + allOptions.push({ label: c.name, value: c.name }); + }); + + builder.aggregates!.forEach(a => { + let label = `${a.aggregateType}(${a.column})`; + let value = label; + + if (a.alias) { + label += ` as ${a.alias}`; + value = a.alias; + } + + allOptions.push({ label, value }); + }); + + if (builder.groupBy && builder.groupBy.length > 0) { + builder.groupBy.forEach(g => allOptions.push({ label: g, value: g })); + } + } else { + allColumns.forEach(c => allOptions.push({ label: c.name, value: c.name })); + } + + // Add selected value to the list if it does not exist. + const allValues = new Set(allOptions.map(o => o.value)); + const customValues = builder.orderBy?.filter(o => !allValues.has(o.name)); + customValues?.forEach(o => allOptions.push({ label: o.name, value: o.name })); + + return allOptions; +}; diff --git a/src/components/queryBuilder/OtelVersionSelect.test.tsx b/src/components/queryBuilder/OtelVersionSelect.test.tsx new file mode 100644 index 00000000..b891ebbc --- /dev/null +++ b/src/components/queryBuilder/OtelVersionSelect.test.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { OtelVersionSelect } from './OtelVersionSelect'; +import { versions as allVersions } from 'otel'; + +describe('OtelVersionSelect', () => { + const testVersion = allVersions[0]; + const testVersionName = allVersions[0].version + ' (latest)'; + + it('should render with empty properties', () => { + const result = render( + {}} + selectedVersion="" + onVersionChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should render with valid properties', () => { + const result = render( + {}} + selectedVersion={testVersion.version} + onVersionChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + expect(result.getByText(testVersionName)).toBeInTheDocument(); + }); + + it('should call onEnabledChange when the switch is enabled', () => { + const onEnabledChange = jest.fn(); + const result = render( + {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const toggle = result.getByRole('checkbox'); + expect(toggle).toBeInTheDocument(); + fireEvent.click(toggle); + expect(onEnabledChange).toBeCalledTimes(1); + expect(onEnabledChange).toBeCalledWith(true); + }); + + it('should call onEnabledChange when the switch is disabled', () => { + const onEnabledChange = jest.fn(); + const result = render( + {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const toggle = result.getByRole('checkbox'); + expect(toggle).toBeInTheDocument(); + fireEvent.click(toggle); + expect(onEnabledChange).toBeCalledTimes(1); + expect(onEnabledChange).toBeCalledWith(false); + }); + + it('should call onVersionChange when a new version is selected', () => { + const onVersionChange = jest.fn(); + const result = render( + {}} + selectedVersion={testVersion.version} + onVersionChange={onVersionChange} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const select = result.getByRole('combobox'); + expect(select).toBeInTheDocument(); + fireEvent.keyDown(select, { key: 'ArrowDown' }); + fireEvent.keyDown(select, { key: 'Enter' }); + expect(onVersionChange).toBeCalledTimes(1); + expect(onVersionChange).toBeCalledWith(expect.any(String)); + }); + + it('should disable version selection when switch is disabled', () => { + const result = render( + {}} + selectedVersion={testVersion.version} + onVersionChange={() => {}} + /> + ); + expect(result.container.firstChild).not.toBeNull(); + + const select = result.getByRole('combobox', { hidden: true }); + expect(select).toBeDisabled(); + }); +}); diff --git a/src/components/queryBuilder/OtelVersionSelect.tsx b/src/components/queryBuilder/OtelVersionSelect.tsx new file mode 100644 index 00000000..511b6402 --- /dev/null +++ b/src/components/queryBuilder/OtelVersionSelect.tsx @@ -0,0 +1,60 @@ +import React, { useEffect } from 'react'; +import { SelectableValue } from '@grafana/data'; +import { InlineFormLabel, Select, Switch as GrafanaSwitch, useTheme } from '@grafana/ui'; +import { versions as allVersions } from 'otel'; +import selectors from 'labels'; + +interface OtelVersionSelectProps { + enabled: boolean, + onEnabledChange: (enabled: boolean) => void, + selectedVersion: string, + onVersionChange: (version: string) => void, + wide?: boolean, +} + +export const OtelVersionSelect = (props: OtelVersionSelectProps) => { + const { enabled, onEnabledChange, selectedVersion, onVersionChange, wide } = props; + const { label, tooltip } = selectors.components.OtelVersionSelect; + const options: SelectableValue[] = allVersions.map(v => ({ + label: `${v.version}${v.name ? (` (${v.name})`) : ''}`, + value: v.version + })); + + useEffect(() => { + if (selectedVersion === '') { + onVersionChange(allVersions[0].version); + } + }, [selectedVersion, onVersionChange]); + + const theme = useTheme(); + const switchContainerStyle: React.CSSProperties = { + padding: `0 ${theme.spacing.sm}`, + height: `${theme.spacing.formInputHeight}px`, + display: 'flex', + alignItems: 'center', + }; + + return ( +
+ + {label} + +
+ onEnabledChange(e.currentTarget.checked)} + role="checkbox" + /> +
+ onChange(e.value ? e.value : '')} - options={list} - value={table} - allowCustomValue={true} - width={25} - > - - ); -}; diff --git a/src/components/queryBuilder/TimeField.test.tsx b/src/components/queryBuilder/TimeField.test.tsx deleted file mode 100644 index 50c52440..00000000 --- a/src/components/queryBuilder/TimeField.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { TimeFieldEditor } from './TimeField'; - -describe('TimeFieldEditor', () => { - it('renders correctly', () => { - const result = render( - {}} - timeFieldType="" - timeFieldTypeCheckFn={() => true} - labelAndTooltip={{ label: 'foo', tooltip: 'bar' }} - /> - ); - expect(result.container.firstChild).not.toBeNull(); - }); -}); diff --git a/src/components/queryBuilder/TimeField.tsx b/src/components/queryBuilder/TimeField.tsx deleted file mode 100644 index d6cbb93d..00000000 --- a/src/components/queryBuilder/TimeField.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { SelectableValue } from '@grafana/data'; -import { Select } from '@grafana/ui'; -import { selectors } from './../../selectors'; -import { FullField } from 'types'; -import { EditorField } from '@grafana/experimental'; - -interface TimeFieldEditorProps { - fieldsList: FullField[]; - timeField: string | null; - timeFieldType: string; - onTimeFieldChange: (timeField: string, timeFieldType: string) => void; - timeFieldTypeCheckFn: (type: string) => boolean; - labelAndTooltip: typeof selectors.components.QueryEditor.QueryBuilder.TIME_FIELD; -} - -export const TimeFieldEditor = (props: TimeFieldEditorProps) => { - const { label, tooltip } = props.labelAndTooltip; - const columns: SelectableValue[] = (props.fieldsList || []) - .filter((f) => props.timeFieldTypeCheckFn(f.type)) - .map((f) => ({ label: f.label, value: f.name })); - const getColumnType = (columnName: string): string => { - const matchedColumn = props.fieldsList.find((f) => f.name === columnName); - return matchedColumn ? matchedColumn.type : ''; - }; - return ( - - setInputId(e.currentTarget.value)} + onBlur={() => onChange(inputId)} + /> +
+ ) +} diff --git a/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts b/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts new file mode 100644 index 00000000..f92b1d67 --- /dev/null +++ b/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts @@ -0,0 +1,209 @@ +import { renderHook } from '@testing-library/react'; +import { useDefaultFilters, useDefaultTimeColumn, useLogDefaultsOnMount, useOtelColumns } from './logsQueryBuilderHooks'; +import { mockDatasource } from '__mocks__/datasource'; +import { ColumnHint, Filter, OrderBy, QueryBuilderOptions, SelectedColumn, TableColumn } from 'types/queryBuilder'; +import { setColumnByHint, setOptions } from 'hooks/useBuilderOptionsState'; +import { versions as otelVersions } from 'otel'; + +describe('useLogDefaultsOnMount', () => { + it('should call builderOptionsDispatch with default log columns', async () => { + const builderOptionsDispatch = jest.fn(); + jest.spyOn(mockDatasource, 'getLogsOtelVersion').mockReturnValue(undefined); + jest.spyOn(mockDatasource, 'getDefaultLogsColumns').mockReturnValue(new Map([[ColumnHint.Time, 'timestamp']])); + + renderHook(() => useLogDefaultsOnMount(mockDatasource, true, {} as QueryBuilderOptions, builderOptionsDispatch)); + + const expectedOptions = { + database: expect.anything(), + table: expect.anything(), + columns: [{ name: 'timestamp', hint: ColumnHint.Time }], + meta: { + otelEnabled: expect.anything(), + otelVersion: undefined + } + }; + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should not call builderOptionsDispatch after defaults are set', async () => { + const builderOptions = {} as QueryBuilderOptions; + const builderOptionsDispatch = jest.fn(); + + const hook = renderHook(() => useLogDefaultsOnMount(mockDatasource, true, builderOptions, builderOptionsDispatch)); + hook.rerender(); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + }); + + it('should not call builderOptionsDispatch for existing query', async () => { + const isNewQuery = false; // query already exists, is not new + const builderOptionsDispatch = jest.fn(); + renderHook(() => useLogDefaultsOnMount(mockDatasource, isNewQuery, {} as QueryBuilderOptions, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); +}); + +describe('useOtelColumns', () => { + const testOtelVersion = otelVersions[0]; // use latest version + + it('should not call builderOptionsDispatch if OTEL is already enabled', async () => { + const builderOptionsDispatch = jest.fn(); + renderHook(() => useOtelColumns(true, testOtelVersion.version, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should not call builderOptionsDispatch if OTEL is disabled', async () => { + const builderOptionsDispatch = jest.fn(); + renderHook(() => useOtelColumns(true, testOtelVersion.version, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch with columns when OTEL is toggled on', async () => { + const builderOptionsDispatch = jest.fn(); + + let otelEnabled = false; + const hook = renderHook(enabled => useOtelColumns(enabled, testOtelVersion.version, builderOptionsDispatch), { initialProps: otelEnabled }); + otelEnabled = true; + hook.rerender(otelEnabled); + + const columns: SelectedColumn[] = []; + testOtelVersion.logColumnMap.forEach((v, k) => columns.push({ name: v, hint: k })); + const expectedOptions = { columns }; + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should not call builderOptionsDispatch after OTEL columns are set', async () => { + const builderOptionsDispatch = jest.fn(); + + let otelEnabled = false; // OTEL is off + const hook = renderHook(enabled => useOtelColumns(enabled, testOtelVersion.version, builderOptionsDispatch), { initialProps: otelEnabled }); + otelEnabled = true; + hook.rerender(otelEnabled); // OTEL is on, columns are set + hook.rerender(otelEnabled); // OTEL still on, should not set again + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + }); +}); + +describe('useDefaultTimeColumn', () => { + it('should call builderOptionsDispatch when there are no configured defaults', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'logs'; + const timeColumnName = 'time'; + jest.spyOn(mockDatasource, 'getDefaultLogsTable').mockReturnValue(undefined); + const allColumns: readonly TableColumn[] = [{ name: timeColumnName, type: 'DateTime', picklistValues: [] }]; + const timeColumn = undefined; + const otelEnabled = false; + + renderHook(() => useDefaultTimeColumn(mockDatasource, allColumns, tableName, timeColumn, otelEnabled, builderOptionsDispatch)); + + const expectedColumn: SelectedColumn = { name: timeColumnName, type: 'DateTime', hint: ColumnHint.Time }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setColumnByHint(expectedColumn))); + }); + + it('should not call builderOptionsDispatch when column is already set', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'logs'; + const timeColumnName = 'time'; + jest.spyOn(mockDatasource, 'getDefaultLogsTable').mockReturnValue(tableName); + jest.spyOn(mockDatasource, 'getDefaultLogsColumns').mockReturnValue(new Map([[ColumnHint.Time, timeColumnName]])); + const allColumns: readonly TableColumn[] = [{ name: timeColumnName, type: 'DateTime', picklistValues: [] }]; + const timeColumn: SelectedColumn = { name: timeColumnName, hint: ColumnHint.Time }; + const otelEnabled = false; + + renderHook(() => useDefaultTimeColumn(mockDatasource, allColumns, tableName, timeColumn, otelEnabled, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch when table changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'logs'; + const timeColumnName = 'time'; + jest.spyOn(mockDatasource, 'getDefaultLogsTable').mockReturnValue(tableName); + jest.spyOn(mockDatasource, 'getDefaultLogsColumns').mockReturnValue(new Map([[ColumnHint.Time, timeColumnName]])); + const allColumns: readonly TableColumn[] = [{ name: timeColumnName, type: 'DateTime', picklistValues: [] }]; + const timeColumn = undefined; + const otelEnabled = false; + + const hook = renderHook(table => + useDefaultTimeColumn( + mockDatasource, + allColumns, + table, + timeColumn, + otelEnabled, + builderOptionsDispatch + ), + { initialProps: tableName } + ); + hook.rerender('other_logs'); + + const expectedColumn: SelectedColumn = { name: timeColumnName, type: 'DateTime', hint: ColumnHint.Time }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setColumnByHint(expectedColumn))); + }); +}); + +describe('useDefaultFilters', () => { + it('should not call builderOptionsDispatch when column/table are present on initial load', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'logs'; + const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; + const filters: Filter[] = []; + const orderBy: OrderBy[] = []; + + renderHook(() => useDefaultFilters(tableName, timeColumn, filters, orderBy, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch when table changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'logs'; + const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; + const filters: Filter[] = []; + const orderBy: OrderBy[] = []; + + const hook = renderHook(table => + useDefaultFilters(table, timeColumn, filters, orderBy, builderOptionsDispatch), + { initialProps: tableName } + ); + hook.rerender('other_logs'); + + const expectedOptions = { + filters: [expect.anything()], + orderBy: [expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should call builderOptionsDispatch when time column changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'logs'; + const filters: Filter[] = []; + const orderBy: OrderBy[] = []; + + const hook = renderHook(timeColumn => + useDefaultFilters(tableName, timeColumn, filters, orderBy, builderOptionsDispatch), + { initialProps: { name: 'timestamp', hint: ColumnHint.Time } } + ); + hook.rerender({ name: 'other_timestamp', hint: ColumnHint.Time }); + + const expectedOptions = { + filters: [expect.anything()], + orderBy: [expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); +}); diff --git a/src/components/queryBuilder/views/logsQueryBuilderHooks.ts b/src/components/queryBuilder/views/logsQueryBuilderHooks.ts new file mode 100644 index 00000000..f16f0816 --- /dev/null +++ b/src/components/queryBuilder/views/logsQueryBuilderHooks.ts @@ -0,0 +1,144 @@ +import { Datasource } from "data/CHDatasource"; +import { columnFilterDateTime } from "data/columnFilters"; +import { BuilderOptionsReducerAction, setColumnByHint, setOptions } from "hooks/useBuilderOptionsState"; +import { useEffect, useMemo, useRef } from "react"; +import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, OrderBy, OrderByDirection, QueryBuilderOptions, SelectedColumn, TableColumn } from "types/queryBuilder"; +import { versions as otelVersions } from 'otel'; + +/** + * Loads the default configuration for new queries. (Only runs on new queries) + */ +export const useLogDefaultsOnMount = (datasource: Datasource, isNewQuery: boolean, builderOptions: QueryBuilderOptions, builderOptionsDispatch: React.Dispatch) => { + const didSetDefaults = useRef(false); + useEffect(() => { + if (!isNewQuery || didSetDefaults.current) { + return; + } + + const defaultDb = datasource.getDefaultLogsDatabase() || datasource.getDefaultDatabase(); + const defaultTable = datasource.getDefaultLogsTable() || datasource.getDefaultTable(); + const otelVersion = datasource.getLogsOtelVersion(); + const defaultColumns = datasource.getDefaultLogsColumns(); + + const nextColumns: SelectedColumn[] = []; + for (let [hint, colName] of defaultColumns) { + nextColumns.push({ name: colName, hint }); + } + + builderOptionsDispatch(setOptions({ + database: defaultDb, + table: defaultTable || builderOptions.table, + columns: nextColumns, + meta: { + otelEnabled: Boolean(otelVersion), + otelVersion, + } + })); + didSetDefaults.current = true; + }, [builderOptions.columns, builderOptions.orderBy, builderOptions.table, builderOptionsDispatch, datasource, isNewQuery]); +}; + +/** + * Sets OTEL Logs columns automatically when OTEL is enabled. + * Does not run if OTEL is already enabled, only when it's changed. + */ +export const useOtelColumns = (otelEnabled: boolean, otelVersion: string, builderOptionsDispatch: React.Dispatch) => { + const didSetColumns = useRef(otelEnabled); + if (!otelEnabled) { + didSetColumns.current = false; + } + + useEffect(() => { + if (!otelEnabled || didSetColumns.current) { + return; + } + + const otelConfig = otelVersions.find(v => v.version === otelVersion); + const logColumnMap = otelConfig?.logColumnMap; + if (!logColumnMap) { + return; + } + + const columns: SelectedColumn[] = []; + logColumnMap.forEach((name, hint) => { + columns.push({ name, hint }); + }); + + builderOptionsDispatch(setOptions({ columns })); + didSetColumns.current = true; + }, [otelEnabled, otelVersion, builderOptionsDispatch]); +}; + +// Finds and selects a default log time column, updates when table changes +export const useDefaultTimeColumn = (datasource: Datasource, allColumns: readonly TableColumn[], table: string, timeColumn: SelectedColumn | undefined, otelEnabled: boolean, builderOptionsDispatch: React.Dispatch) => { + const hasDefaultColumnConfigured = useMemo(() => Boolean(datasource.getDefaultLogsTable()) && datasource.getDefaultLogsColumns().has(ColumnHint.Time), [datasource]); + const didSetDefaultTime = useRef(Boolean(timeColumn) || hasDefaultColumnConfigured); + const lastTable = useRef(table || ''); + if (table !== lastTable.current) { + didSetDefaultTime.current = false; + } + + if (Boolean(timeColumn) || otelEnabled) { + lastTable.current = table; + didSetDefaultTime.current = true; + } + + useEffect(() => { + if (didSetDefaultTime.current || allColumns.length === 0 || !table) { + return; + } + + const col = allColumns.filter(columnFilterDateTime)[0]; + if (!col) { + return; + } + + const timeColumn: SelectedColumn = { + name: col.name, + type: col.type, + hint: ColumnHint.Time + }; + + builderOptionsDispatch(setColumnByHint(timeColumn)); + lastTable.current = table; + didSetDefaultTime.current = true; + }, [datasource, allColumns, table, builderOptionsDispatch]); +}; + +// Apply default filters/orderBy on timeColumn change +const timeRangeFilterId = 'timeRange'; +export const useDefaultFilters = (table: string, timeColumn: SelectedColumn | undefined, filters: Filter[], orderBy: OrderBy[], builderOptionsDispatch: React.Dispatch) => { + const lastTimeColumn = useRef(timeColumn?.name || ''); + const lastTable = useRef(table || ''); + if (!timeColumn || table !== lastTable.current) { + lastTimeColumn.current = ''; + } + + useEffect(() => { + if (!timeColumn || (timeColumn.name === lastTimeColumn.current) || !table) { + return; + } + + const nextFilters: Filter[] = filters.filter(f => f.id !== timeRangeFilterId); + const timeRangeFilter: DateFilterWithoutValue = { + type: 'datetime', + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: timeColumn.name, + id: timeRangeFilterId, + condition: 'AND' + }; + nextFilters.unshift(timeRangeFilter); + + const nextOrderBy: OrderBy[] = orderBy.filter(o => !o.default); + const defaultOrderBy: OrderBy = { name: timeColumn?.name, dir: OrderByDirection.DESC, default: true }; + nextOrderBy.unshift(defaultOrderBy); + + lastTable.current = table; + lastTimeColumn.current = timeColumn.name; + builderOptionsDispatch(setOptions({ + filters: nextFilters, + orderBy: nextOrderBy + })); + }, [table, timeColumn, filters, orderBy, builderOptionsDispatch]); +}; diff --git a/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts new file mode 100644 index 00000000..57d2a6be --- /dev/null +++ b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts @@ -0,0 +1,104 @@ +import { renderHook } from '@testing-library/react'; +import { useDefaultFilters, useDefaultTimeColumn } from './timeSeriesQueryBuilderHooks'; +import { ColumnHint, Filter, SelectedColumn, TableColumn } from 'types/queryBuilder'; +import { setColumnByHint, setOptions } from 'hooks/useBuilderOptionsState'; + +describe('useDefaultTimeColumn', () => { + it('should call builderOptionsDispatch when time column is unset', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const timeColumnName = 'time'; + const allColumns: readonly TableColumn[] = [{ name: timeColumnName, type: 'DateTime', picklistValues: [] }]; + const timeColumn = undefined; + + renderHook(() => useDefaultTimeColumn(allColumns, tableName, timeColumn, builderOptionsDispatch)); + + const expectedColumn: SelectedColumn = { name: timeColumnName, type: 'DateTime', hint: ColumnHint.Time }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setColumnByHint(expectedColumn))); + }); + + it('should not call builderOptionsDispatch when time column is already set', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const timeColumnName = 'time'; + const allColumns: readonly TableColumn[] = [{ name: timeColumnName, type: 'DateTime', picklistValues: [] }]; + const timeColumn: SelectedColumn = { name: timeColumnName, hint: ColumnHint.Time }; + + renderHook(() => useDefaultTimeColumn(allColumns, tableName, timeColumn, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch when table changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const timeColumnName = 'time'; + const allColumns: readonly TableColumn[] = [{ name: timeColumnName, type: 'DateTime', picklistValues: [] }]; + const timeColumn = undefined; + + renderHook(table => + useDefaultTimeColumn( + allColumns, + table, + timeColumn, + builderOptionsDispatch + ), + { initialProps: tableName } + ); + + const expectedColumn: SelectedColumn = { name: timeColumnName, type: 'DateTime', hint: ColumnHint.Time }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setColumnByHint(expectedColumn))); + }); +}); + +describe('useDefaultFilters', () => { + it('should not call builderOptionsDispatch when column/table are present on initial load', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; + const filters: Filter[] = []; + + renderHook(() => useDefaultFilters(tableName, timeColumn, filters, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch when table changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; + const filters: Filter[] = []; + + const hook = renderHook(table => + useDefaultFilters(table, timeColumn, filters, builderOptionsDispatch), + { initialProps: tableName } + ); + hook.rerender('other_timeseries'); + + const expectedOptions = { + filters: [expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should call builderOptionsDispatch when time column changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const filters: Filter[] = []; + + const hook = renderHook(timeColumn => + useDefaultFilters(tableName, timeColumn, filters, builderOptionsDispatch), + { initialProps: { name: 'timestamp', hint: ColumnHint.Time } } + ); + hook.rerender({ name: 'other_timestamp', hint: ColumnHint.Time }); + + const expectedOptions = { + filters: [expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); +}); diff --git a/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts new file mode 100644 index 00000000..4302d3b0 --- /dev/null +++ b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts @@ -0,0 +1,67 @@ +import { columnFilterDateTime } from 'data/columnFilters'; +import { BuilderOptionsReducerAction, setColumnByHint, setOptions } from 'hooks/useBuilderOptionsState'; +import React, { useEffect, useRef } from 'react'; +import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, SelectedColumn, TableColumn } from 'types/queryBuilder'; + +// Finds and selects a default log time column, updates when table changes +export const useDefaultTimeColumn = (allColumns: readonly TableColumn[], table: string, timeColumn: SelectedColumn | undefined, builderOptionsDispatch: React.Dispatch) => { + const didSetDefaultTime = useRef(Boolean(timeColumn)); + const lastTable = useRef(table || ''); + if (table !== lastTable.current) { + didSetDefaultTime.current = false; + } + + useEffect(() => { + if (didSetDefaultTime.current || allColumns.length === 0 || !table) { + return; + } + + const col = allColumns.filter(columnFilterDateTime)[0]; + if (!col) { + return; + } + + const timeColumn: SelectedColumn = { + name: col.name, + type: col.type, + hint: ColumnHint.Time + }; + + builderOptionsDispatch(setColumnByHint(timeColumn)); + lastTable.current = table; + didSetDefaultTime.current = true; + }, [allColumns, table, builderOptionsDispatch]); +}; + +// Apply default filters/orderBy on timeColumn change +const timeRangeFilterId = 'timeRange'; +export const useDefaultFilters = (table: string, timeColumn: SelectedColumn | undefined, filters: Filter[], builderOptionsDispatch: React.Dispatch) => { + const lastTimeColumn = useRef(timeColumn?.name || ''); + const lastTable = useRef(table || ''); + if (!timeColumn || table !== lastTable.current) { + lastTimeColumn.current = ''; + } + + useEffect(() => { + if (!timeColumn || (timeColumn.name === lastTimeColumn.current) || !table) { + return; + } + + const nextFilters: Filter[] = filters.filter(f => f.id !== timeRangeFilterId); + const timeRangeFilter: DateFilterWithoutValue = { + type: 'datetime', + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: timeColumn.name, + id: timeRangeFilterId, + condition: 'AND' + }; + nextFilters.unshift(timeRangeFilter); + + lastTable.current = table; + lastTimeColumn.current = timeColumn.name; + builderOptionsDispatch(setOptions({ + filters: nextFilters + })); + }, [table, timeColumn, filters, builderOptionsDispatch]); +}; diff --git a/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts b/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts new file mode 100644 index 00000000..7bbb7038 --- /dev/null +++ b/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts @@ -0,0 +1,99 @@ +import { renderHook } from '@testing-library/react'; +import { useTraceDefaultsOnMount, useOtelColumns } from './traceQueryBuilderHooks'; +import { mockDatasource } from '__mocks__/datasource'; +import { ColumnHint, QueryBuilderOptions, SelectedColumn } from 'types/queryBuilder'; +import { setOptions } from 'hooks/useBuilderOptionsState'; +import { versions as otelVersions } from 'otel'; + +describe('useTraceDefaultsOnMount', () => { + it('should call builderOptionsDispatch with default trace columns', async () => { + const builderOptionsDispatch = jest.fn(); + jest.spyOn(mockDatasource, 'getTraceOtelVersion').mockReturnValue(undefined); + jest.spyOn(mockDatasource, 'getDefaultTraceColumns').mockReturnValue(new Map([[ColumnHint.Time, 'timestamp']])); + + renderHook(() => useTraceDefaultsOnMount(mockDatasource, true, {} as QueryBuilderOptions, builderOptionsDispatch)); + + const expectedOptions = { + database: expect.anything(), + table: expect.anything(), + columns: [{ name: 'timestamp', hint: ColumnHint.Time }], + meta: { + otelEnabled: expect.anything(), + otelVersion: undefined, + traceDurationUnit: expect.anything() + } + }; + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should not call builderOptionsDispatch after defaults are set', async () => { + const builderOptions = {} as QueryBuilderOptions; + const builderOptionsDispatch = jest.fn(); + + const hook = renderHook(() => useTraceDefaultsOnMount(mockDatasource, true, builderOptions, builderOptionsDispatch)); + hook.rerender(); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + }); + + it('should not call builderOptionsDispatch for existing query', async () => { + const isNewQuery = false; // query already exists, is not new + const builderOptionsDispatch = jest.fn(); + renderHook(() => useTraceDefaultsOnMount(mockDatasource, isNewQuery, {} as QueryBuilderOptions, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); +}); + +describe('useOtelColumns', () => { + const testOtelVersion = otelVersions[0]; // use latest version + + it('should not call builderOptionsDispatch if OTEL is already enabled', async () => { + const builderOptionsDispatch = jest.fn(); + renderHook(() => useOtelColumns(true, testOtelVersion.version, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should not call builderOptionsDispatch if OTEL is disabled', async () => { + const builderOptionsDispatch = jest.fn(); + renderHook(() => useOtelColumns(true, testOtelVersion.version, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch with columns when OTEL is toggled on', async () => { + const builderOptionsDispatch = jest.fn(); + + let otelEnabled = false; + const hook = renderHook(enabled => useOtelColumns(enabled, testOtelVersion.version, builderOptionsDispatch), { initialProps: otelEnabled }); + otelEnabled = true; + hook.rerender(otelEnabled); + + const columns: SelectedColumn[] = []; + testOtelVersion.traceColumnMap.forEach((v, k) => columns.push({ name: v, hint: k })); + const expectedOptions = { + columns, + meta: { + traceDurationUnit: expect.anything() + } + }; + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should not call builderOptionsDispatch after OTEL columns are set', async () => { + const builderOptionsDispatch = jest.fn(); + + let otelEnabled = false; // OTEL is off + const hook = renderHook(enabled => useOtelColumns(enabled, testOtelVersion.version, builderOptionsDispatch), { initialProps: otelEnabled }); + otelEnabled = true; + hook.rerender(otelEnabled); // OTEL is on, columns are set + hook.rerender(otelEnabled); // OTEL still on, should not set again + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/components/queryBuilder/views/traceQueryBuilderHooks.ts b/src/components/queryBuilder/views/traceQueryBuilderHooks.ts new file mode 100644 index 00000000..fdca6788 --- /dev/null +++ b/src/components/queryBuilder/views/traceQueryBuilderHooks.ts @@ -0,0 +1,76 @@ +import React, { useEffect, useRef } from 'react'; +import { Datasource } from 'data/CHDatasource'; +import { versions as otelVersions } from 'otel'; +import { QueryBuilderOptions, SelectedColumn } from 'types/queryBuilder'; +import { BuilderOptionsReducerAction, setOptions } from 'hooks/useBuilderOptionsState'; + +/** + * Loads the default configuration for new queries. (Only runs on new queries) + */ +export const useTraceDefaultsOnMount = (datasource: Datasource, isNewQuery: boolean, builderOptions: QueryBuilderOptions, builderOptionsDispatch: React.Dispatch) => { + const didSetDefaults = useRef(false); + useEffect(() => { + if (!isNewQuery || didSetDefaults.current) { + return; + } + + const defaultDb = datasource.getDefaultTraceDatabase() || datasource.getDefaultDatabase(); + const defaultTable = datasource.getDefaultTraceTable() || datasource.getDefaultTable(); + const defaultDurationUnit = datasource.getDefaultTraceDurationUnit(); + const otelVersion = datasource.getTraceOtelVersion(); + const defaultColumns = datasource.getDefaultTraceColumns(); + + const nextColumns: SelectedColumn[] = []; + for (let [hint, colName] of defaultColumns) { + nextColumns.push({ name: colName, hint }); + } + + builderOptionsDispatch(setOptions({ + database: defaultDb, + table: defaultTable || builderOptions.table, + columns: nextColumns, + meta: { + otelEnabled: Boolean(otelVersion), + otelVersion, + traceDurationUnit: defaultDurationUnit + } + })); + didSetDefaults.current = true; + }, [builderOptions.columns, builderOptions.orderBy, builderOptions.table, builderOptionsDispatch, datasource, isNewQuery]); +}; + +/** + * Sets OTEL Trace columns automatically when OTEL is enabled + * Does not run if OTEL is already enabled, only when it's changed. + */ +export const useOtelColumns = (otelEnabled: boolean, otelVersion: string, builderOptionsDispatch: React.Dispatch) => { + const didSetColumns = useRef(otelEnabled); + if (!otelEnabled) { + didSetColumns.current = false; + } + + useEffect(() => { + if (!otelEnabled || didSetColumns.current) { + return; + } + + const otelConfig = otelVersions.find(v => v.version === otelVersion); + const traceColumnMap = otelConfig?.traceColumnMap; + if (!traceColumnMap) { + return; + } + + const columns: SelectedColumn[] = []; + traceColumnMap.forEach((name, hint) => { + columns.push({ name, hint }); + }); + + builderOptionsDispatch(setOptions({ + columns, + meta: { + traceDurationUnit: otelConfig.traceDurationUnit + } + })); + didSetColumns.current = true; + }, [otelEnabled, otelVersion, builderOptionsDispatch]); +}; diff --git a/src/data/CHDatasource.test.ts b/src/data/CHDatasource.test.ts index b037b255..fb00db4d 100644 --- a/src/data/CHDatasource.test.ts +++ b/src/data/CHDatasource.test.ts @@ -12,7 +12,8 @@ import { DataQuery } from '@grafana/schema'; import { Observable, of } from 'rxjs'; import { DataSourceWithBackend } from '@grafana/runtime'; import { mockDatasource } from '__mocks__/datasource'; -import { BuilderMode, CHBuilderQuery, CHQuery, CHSQLQuery, Format, QueryType, SqlBuilderOptionsList } from 'types'; +import { CHBuilderQuery, CHQuery, CHSqlQuery, EditorType } from 'types/sql'; +import { ColumnHint, QueryType, BuilderMode, QueryBuilderOptions} from 'types/queryBuilder'; import { cloneDeep } from 'lodash'; import { Datasource } from './CHDatasource'; import * as logs from './logs'; @@ -74,30 +75,30 @@ describe('ClickHouseDatasource', () => { it('interpolates', async () => { const rawSql = 'foo'; const spyOnReplace = jest.spyOn(templateSrvMock, 'replace').mockImplementation(() => rawSql); - const query = { rawSql: 'select', queryType: QueryType.SQL } as CHQuery; + const query = { rawSql: 'select', editorType: EditorType.SQL } as CHQuery; const val = createInstance({}).applyTemplateVariables(query, {}); expect(spyOnReplace).toHaveBeenCalled(); - expect(val).toEqual({ rawSql, queryType: QueryType.SQL }); + expect(val).toEqual({ rawSql, editorType: EditorType.SQL }); }); it('should handle $__conditionalAll and not replace', async () => { - const query = { rawSql: '$__conditionalAll(foo, $fieldVal)', queryType: QueryType.SQL } as CHQuery; + const query = { rawSql: '$__conditionalAll(foo, $fieldVal)', editorType: EditorType.SQL } as CHQuery; const vars = [{ current: { value: `'val1', 'val2'` }, name: 'fieldVal' }] as TypedVariableModel[]; const spyOnReplace = jest.spyOn(templateSrvMock, 'replace').mockImplementation((x) => x); const spyOnGetVars = jest.spyOn(templateSrvMock, 'getVariables').mockImplementation(() => vars); const val = createInstance({}).applyTemplateVariables(query, {}); expect(spyOnReplace).toHaveBeenCalled(); expect(spyOnGetVars).toHaveBeenCalled(); - expect(val).toEqual({ rawSql: `foo`, queryType: QueryType.SQL }); + expect(val).toEqual({ rawSql: `foo`, editorType: EditorType.SQL }); }); it('should handle $__conditionalAll and replace', async () => { - const query = { rawSql: '$__conditionalAll(foo, $fieldVal)', queryType: QueryType.SQL } as CHQuery; + const query = { rawSql: '$__conditionalAll(foo, $fieldVal)', editorType: EditorType.SQL } as CHQuery; const vars = [{ current: { value: '$__all' }, name: 'fieldVal' }] as TypedVariableModel[]; const spyOnReplace = jest.spyOn(templateSrvMock, 'replace').mockImplementation((x) => x); const spyOnGetVars = jest.spyOn(templateSrvMock, 'getVariables').mockImplementation(() => vars); const val = createInstance({}).applyTemplateVariables(query, {}); expect(spyOnReplace).toHaveBeenCalled(); expect(spyOnGetVars).toHaveBeenCalled(); - expect(val).toEqual({ rawSql: `1=1`, queryType: QueryType.SQL }); + expect(val).toEqual({ rawSql: `1=1`, editorType: EditorType.SQL }); }); }); @@ -105,8 +106,8 @@ describe('ClickHouseDatasource', () => { it('should Fetch Default Tags When No Second AdHoc Variable', async () => { const spyOnReplace = jest.spyOn(templateSrvMock, 'replace').mockImplementation(() => '$clickhouse_adhoc_query'); const ds = cloneDeep(mockDatasource); - ds.settings.jsonData.defaultDatabase = undefined; const frame = new ArrayDataFrame([{ name: 'foo', type: 'string', table: 'table' }]); + jest.spyOn(ds, 'getDefaultDatabase').mockImplementation(() => undefined!); // Disable default DB const spyOnQuery = jest.spyOn(ds, 'query').mockImplementation((_request) => of({ data: [frame] })); const keys = await ds.getTagKeys(); @@ -248,12 +249,12 @@ describe('ClickHouseDatasource', () => { }); }); - describe('fetchFieldsFull', () => { + describe('fetchColumnsFull', () => { it('sends a correct query when database and table names are provided', async () => { const ds = cloneDeep(mockDatasource); const frame = new ArrayDataFrame([{ name: 'foo', type: 'string', table: 'table' }]); const spyOnQuery = jest.spyOn(ds, 'query').mockImplementation((_request) => of({ data: [frame] })); - await ds.fetchFieldsFull('db_name', 'table_name'); + await ds.fetchColumnsFull('db_name', 'table_name'); const expected = { rawSql: 'DESC TABLE "db_name"."table_name"' }; expect(spyOnQuery).toHaveBeenCalledWith( @@ -265,7 +266,7 @@ describe('ClickHouseDatasource', () => { const ds = cloneDeep(mockDatasource); const frame = new ArrayDataFrame([{ name: 'foo', type: 'string', table: 'table' }]); const spyOnQuery = jest.spyOn(ds, 'query').mockImplementation((_request) => of({ data: [frame] })); - await ds.fetchFieldsFull('', 'table_name'); + await ds.fetchColumnsFull('', 'table_name'); const expected = { rawSql: 'DESC TABLE "table_name"' }; expect(spyOnQuery).toHaveBeenCalledWith( @@ -278,7 +279,7 @@ describe('ClickHouseDatasource', () => { const frame = new ArrayDataFrame([{ name: 'foo', type: 'string', table: 'table' }]); const spyOnQuery = jest.spyOn(ds, 'query').mockImplementation((_) => of({ data: [frame] })); - await ds.fetchFieldsFull('', 'table.name'); + await ds.fetchColumnsFull('', 'table.name'); const expected = { rawSql: 'DESC TABLE "table.name"' }; expect(spyOnQuery).toHaveBeenCalledWith( @@ -311,18 +312,20 @@ describe('ClickHouseDatasource', () => { describe('SupplementaryQueriesSupport', () => { const query: CHBuilderQuery = { + pluginVersion: '', refId: '42', - queryType: QueryType.Builder, + editorType: EditorType.Builder, rawSql: 'SELECT * FROM system.numbers LIMIT 1', builderOptions: { - mode: BuilderMode.List, database: 'default', table: 'logs', - timeField: 'created_at', - logLevelField: 'level', + queryType: QueryType.Logs, + mode: BuilderMode.List, + columns: [ + { name: 'created_at', hint: ColumnHint.Time }, + { name: 'level', hint: ColumnHint.LogLevel }, + ] }, - format: Format.LOGS, - selectedFormat: Format.LOGS, }; const request: DataQueryRequest = { app: CoreApp.Explore, @@ -348,11 +351,14 @@ describe('ClickHouseDatasource', () => { describe('getSupplementaryLogsVolumeQuery', () => { it('should return undefined if any of the conditions are not met', async () => { - [Format.AUTO, Format.TIMESERIES, Format.TABLE, Format.TRACE].forEach((format) => { + [QueryType.Table, QueryType.TimeSeries, QueryType.Traces].forEach(queryType => { expect( datasource.getSupplementaryLogsVolumeQuery(request, { ...query, - format, + builderOptions: { + ...query.builderOptions, + queryType + } }) ).toBeUndefined(); }); @@ -360,14 +366,18 @@ describe('ClickHouseDatasource', () => { expect( datasource.getSupplementaryLogsVolumeQuery(request, { ...query, - builderOptions: { mode } as any, + builderOptions: { + ...query.builderOptions, + mode + }, }) ).toBeUndefined(); }); expect( datasource.getSupplementaryLogsVolumeQuery(request, { ...query, - queryType: QueryType.SQL, + editorType: EditorType.SQL, + queryType: undefined }) ).toBeUndefined(); expect( @@ -375,8 +385,8 @@ describe('ClickHouseDatasource', () => { ...query, builderOptions: { ...query.builderOptions, - database: undefined, - } as SqlBuilderOptionsList, + database: '', + }, }) ).toBeUndefined(); expect( @@ -384,8 +394,8 @@ describe('ClickHouseDatasource', () => { ...query, builderOptions: { ...query.builderOptions, - table: undefined, - } as SqlBuilderOptionsList, + table: '', + }, }) ).toBeUndefined(); expect( @@ -393,8 +403,8 @@ describe('ClickHouseDatasource', () => { ...query, builderOptions: { ...query.builderOptions, - timeField: undefined, - } as SqlBuilderOptionsList, + columns: query.builderOptions.columns?.filter(c => c.hint !== ColumnHint.Time) + } as QueryBuilderOptions, }) ).toBeUndefined(); }); @@ -407,8 +417,8 @@ describe('ClickHouseDatasource', () => { ...query, builderOptions: { ...query.builderOptions, - logLevelField: undefined, - } as SqlBuilderOptionsList, + columns: query.builderOptions.columns?.filter(c => c.hint !== ColumnHint.LogLevel) + } as QueryBuilderOptions, }); expect(result?.rawSql).toEqual( 'SELECT toStartOfInterval("created_at", INTERVAL 1 DAY) AS time, count(*) logs ' + @@ -461,7 +471,7 @@ describe('ClickHouseDatasource', () => { const range = ['from', 'to']; const supplementaryQuery = { rawSql: 'supplementaryQuery', - } as CHSQLQuery; + } as CHSqlQuery; jest.spyOn(Datasource.prototype, 'getSupplementaryLogsVolumeQuery').mockReturnValue(supplementaryQuery); jest.spyOn(logs, 'getIntervalInfo').mockReturnValue({ interval: '1d' }); const queryLogsVolumeSpy = jest diff --git a/src/data/CHDatasource.ts b/src/data/CHDatasource.ts index 23b3e3e4..3eef6e42 100644 --- a/src/data/CHDatasource.ts +++ b/src/data/CHDatasource.ts @@ -4,9 +4,11 @@ import { DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings, + DataSourceWithLogsContextSupport, DataSourceWithSupplementaryQueriesSupport, getTimeZone, getTimeZoneInfo, + LogRowModel, MetricFindValue, QueryFixAction, ScopedVars, @@ -16,20 +18,21 @@ import { } from '@grafana/data'; import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; import { Observable } from 'rxjs'; +import { CHConfig } from 'types/config'; +import { EditorType, CHQuery } from 'types/sql'; import { - BuilderMetricField, - BuilderMetricFieldAggregation, + QueryType, + AggregateColumn, + AggregateType, BuilderMode, - CHConfig, - CHQuery, Filter, FilterOperator, - Format, - FullField, + TableColumn, OrderByDirection, - QueryType, - SqlBuilderOptionsAggregate, -} from '../types'; + QueryBuilderOptions, + ColumnHint, + TimeUnit, +} from 'types/queryBuilder'; import { AdHocFilter } from './adHocFilter'; import { cloneDeep, isEmpty, isString } from 'lodash'; import { @@ -40,11 +43,15 @@ import { queryLogsVolume, TIME_FIELD_ALIAS, } from './logs'; -import { getSQLFromQueryOptions } from '../components/queryBuilder/utils'; +import { getSqlFromQueryBuilderOptions } from '../components/queryBuilder/utils'; +import { generateSql, getColumnByHint } from './sqlGenerator'; +import { versions as otelVersions } from 'otel'; +import { ReactNode } from 'react'; export class Datasource extends DataSourceWithBackend - implements DataSourceWithSupplementaryQueriesSupport + implements DataSourceWithSupplementaryQueriesSupport, + DataSourceWithLogsContextSupport { // This enables default annotation support for 7.2+ annotations = {}; @@ -114,46 +121,52 @@ export class Datasource getSupplementaryLogsVolumeQuery(logsVolumeRequest: DataQueryRequest, query: CHQuery): CHQuery | undefined { if ( - query.format !== Format.LOGS || - query.queryType !== QueryType.Builder || + query.editorType !== EditorType.Builder || + query.builderOptions.queryType !== QueryType.Logs || query.builderOptions.mode !== BuilderMode.List || - query.builderOptions.timeField === undefined || - query.builderOptions.database === undefined || - query.builderOptions.table === undefined + query.builderOptions.database === '' || + query.builderOptions.table === '' ) { return undefined; } + const timeColumn = getColumnByHint(query.builderOptions, ColumnHint.Time); + if (timeColumn === undefined) { + return undefined; + } + const timeFieldRoundingClause = getTimeFieldRoundingClause( logsVolumeRequest.scopedVars, - query.builderOptions.timeField + timeColumn.name ); const fields: string[] = []; - const metrics: BuilderMetricField[] = []; + const aggregates: AggregateColumn[] = []; // could be undefined or an empty string (if user deselects the field) - if (query.builderOptions.logLevelField) { + const logLevelColumn = getColumnByHint(query.builderOptions, ColumnHint.LogLevel); + if (logLevelColumn) { // Generate "fields" like // sum(toString("log_level") IN ('dbug', 'debug', 'DBUG', 'DEBUG', 'Dbug', 'Debug')) AS debug - const llf = `toString("${query.builderOptions.logLevelField}")`; + const llf = `toString("${logLevelColumn.name}")`; let level: keyof typeof LOG_LEVEL_TO_IN_CLAUSE; for (level in LOG_LEVEL_TO_IN_CLAUSE) { fields.push(`sum(${llf} ${LOG_LEVEL_TO_IN_CLAUSE[level]}) AS ${level}`); } } else { - metrics.push({ - aggregation: BuilderMetricFieldAggregation.Count, + aggregates.push({ + aggregateType: AggregateType.Count, + column: '*', alias: DEFAULT_LOGS_ALIAS, - field: '*', }); } - const logVolumeSqlBuilderOptions: SqlBuilderOptionsAggregate = { - mode: BuilderMode.Aggregate, + const logVolumeSqlBuilderOptions: QueryBuilderOptions = { database: query.builderOptions.database, table: query.builderOptions.table, + queryType: QueryType.TimeSeries, + mode: BuilderMode.Aggregate, filters: query.builderOptions.filters, - fields, - metrics, + columns: fields.map(f => ({ name: f })), + aggregates, groupBy: [`${timeFieldRoundingClause} AS ${TIME_FIELD_ALIAS}`], orderBy: [ { @@ -163,13 +176,14 @@ export class Datasource ], }; - const logVolumeSupplementaryQuery = getSQLFromQueryOptions(logVolumeSqlBuilderOptions); + const logVolumeSupplementaryQuery = getSqlFromQueryBuilderOptions(logVolumeSqlBuilderOptions); return { - format: Format.AUTO, - queryType: QueryType.SQL, + // format: Format.AUTO, + // selectedFormat: Format.AUTO, + pluginVersion: '', + editorType: EditorType.SQL, rawSql: logVolumeSupplementaryQuery, refId: '', - selectedFormat: Format.AUTO, }; } @@ -181,17 +195,16 @@ export class Datasource if (this.adHocFiltersStatus === AdHocFilterStatus.none) { this.adHocFiltersStatus = await this.canUseAdhocFilters(); } - const chQuery = isString(query) ? { rawSql: query, queryType: QueryType.SQL } : query; + const chQuery = isString(query) ? { rawSql: query, editorType: EditorType.SQL } : query; - if (!(chQuery.queryType === QueryType.SQL || chQuery.queryType === QueryType.Builder || !chQuery.queryType)) { + if (!(chQuery.editorType === EditorType.SQL || chQuery.editorType === EditorType.Builder || !chQuery.editorType)) { return []; } if (!chQuery.rawSql) { return []; } - const q = { ...chQuery, queryType: chQuery.queryType || QueryType.SQL }; - const frame = await this.runQuery(q, options); + const frame = await this.runQuery(chQuery, options); if (frame.fields?.length === 0) { return []; } @@ -256,7 +269,7 @@ export class Datasource modifyQuery(query: CHQuery, action: QueryFixAction): CHQuery { // support filtering by field value in Explore if ( - query.queryType === QueryType.Builder && + query.editorType === EditorType.Builder && action.options !== undefined && 'key' in action.options && 'value' in action.options @@ -309,7 +322,7 @@ export class Datasource return { ...query, // the query is updated to trigger the URL update and propagation to the panels - rawSql: getSQLFromQueryOptions(updatedBuilder), + rawSql: generateSql(updatedBuilder), builderOptions: updatedBuilder, }; } @@ -356,8 +369,98 @@ export class Datasource return value; } - getDefaultDatabase() { - return this.settings.jsonData.defaultDatabase; + getDefaultDatabase(): string { + return this.settings.jsonData.defaultDatabase || 'default'; + } + + getDefaultTable(): string | undefined { + return this.settings.jsonData.defaultTable; + } + + getDefaultLogsDatabase(): string | undefined { + return this.settings.jsonData.logs?.defaultDatabase; + } + + getDefaultLogsTable(): string | undefined { + return this.settings.jsonData.logs?.defaultTable; + } + + getDefaultLogsColumns(): Map { + const result = new Map(); + const logsConfig = this.settings.jsonData.logs; + if (!logsConfig) { + return result; + } + + const otelEnabled = logsConfig.otelEnabled; + const otelVersion = logsConfig.otelVersion; + + const otelConfig = otelVersions.find(v => v.version === otelVersion); + if (otelEnabled && otelConfig) { + return otelConfig.logColumnMap; + } + + logsConfig.timeColumn && result.set(ColumnHint.Time, logsConfig.timeColumn); + logsConfig.levelColumn && result.set(ColumnHint.LogLevel, logsConfig.levelColumn); + logsConfig.messageColumn && result.set(ColumnHint.LogMessage, logsConfig.messageColumn); + + return result; + } + + /** + * Get configured OTEL version for logs. Returns undefined when versioning is disabled/unset. + */ + getLogsOtelVersion(): string | undefined { + const logConfig = this.settings.jsonData.logs; + return logConfig?.otelEnabled ? (logConfig.otelVersion || undefined) : undefined; + } + + getDefaultTraceDatabase(): string | undefined { + return this.settings.jsonData.traces?.defaultDatabase; + } + + getDefaultTraceTable(): string | undefined { + return this.settings.jsonData.traces?.defaultTable; + } + + getDefaultTraceColumns(): Map { + const result = new Map(); + const traceConfig = this.settings.jsonData.traces; + if (!traceConfig) { + return result; + } + + const otelEnabled = traceConfig.otelEnabled; + const otelVersion = traceConfig.otelVersion; + + const otelConfig = otelVersions.find(v => v.version === otelVersion); + if (otelEnabled && otelConfig) { + return otelConfig.traceColumnMap; + } + + traceConfig.traceIdColumn && result.set(ColumnHint.TraceId, traceConfig.traceIdColumn); + traceConfig.spanIdColumn && result.set(ColumnHint.TraceSpanId, traceConfig.spanIdColumn); + traceConfig.operationNameColumn && result.set(ColumnHint.TraceOperationName, traceConfig.operationNameColumn); + traceConfig.parentSpanIdColumn && result.set(ColumnHint.TraceParentSpanId, traceConfig.parentSpanIdColumn); + traceConfig.serviceNameColumn && result.set(ColumnHint.TraceServiceName, traceConfig.serviceNameColumn); + traceConfig.durationColumn && result.set(ColumnHint.TraceDurationTime, traceConfig.durationColumn); + traceConfig.startTimeColumn && result.set(ColumnHint.Time, traceConfig.startTimeColumn); + traceConfig.tagsColumn && result.set(ColumnHint.TraceTags, traceConfig.tagsColumn); + traceConfig.serviceTagsColumn && result.set(ColumnHint.TraceServiceTags, traceConfig.serviceTagsColumn); + + return result; + } + + /** + * Get configured OTEL version for traces. Returns undefined when versioning is disabled/unset. + */ + getTraceOtelVersion(): string | undefined { + const traceConfig = this.settings.jsonData.traces; + return traceConfig?.otelEnabled ? (traceConfig.otelVersion || undefined) : undefined; + } + + getDefaultTraceDurationUnit(): TimeUnit { + return this.settings.jsonData.traces?.durationUnit as TimeUnit || TimeUnit.Nanoseconds; } async fetchDatabases(): Promise { @@ -377,7 +480,7 @@ export class Datasource return this.fetchData(`DESC TABLE "${database}"."${table}"`); } - async fetchFieldsFull(database: string | undefined, table: string): Promise { + async fetchColumnsFull(database: string | undefined, table: string): Promise { const prefix = Boolean(database) ? `"${database}".` : ''; const rawSql = `DESC TABLE ${prefix}"${table}"`; const frame = await this.runQuery({ rawSql }); @@ -385,7 +488,7 @@ export class Datasource return []; } const view = new DataFrameView(frame); - return view.map((item) => ({ + return view.map(item => ({ name: item[0], type: item[1], label: item[0], @@ -569,6 +672,19 @@ export class Datasource throw err; } } + + // interface DataSourceWithLogsContextSupport + async getLogRowContext(row: LogRowModel, options?: any | undefined, query?: CHQuery | undefined): Promise { + return {} as DataQueryResponse; + } + + showContextToggle(row?: LogRowModel): boolean { + return false; + } + + getLogRowContextUi(row: LogRowModel, runContextQuery?: (() => void) | undefined): ReactNode { + return false; + } } enum TagType { diff --git a/src/data/adHocFilter.test.ts b/src/data/adHocFilter.test.ts index c2355814..12f4aa7d 100644 --- a/src/data/adHocFilter.test.ts +++ b/src/data/adHocFilter.test.ts @@ -183,8 +183,8 @@ describe('AdHocManager', () => { }); it('log a malformed filter', () => { - const warn = jest.spyOn(console, 'error'); - const value = { key: 'foo.key', operator: '=', value: undefined }; + const warn = jest.spyOn(console, "warn"); + const value = { key: 'foo.key', operator: '=', value: undefined } const ahm = new AdHocFilter(); ahm.setTargetTableFromQuery('SELECT * FROM foo'); ahm.apply('SELECT foo.stuff FROM foo', [ diff --git a/src/data/adHocFilter.ts b/src/data/adHocFilter.ts index f11cd33e..d2426b2d 100644 --- a/src/data/adHocFilter.ts +++ b/src/data/adHocFilter.ts @@ -10,7 +10,6 @@ export class AdHocFilter { setTargetTableFromQuery(query: string) { this._targetTable = getTable(query); if (this._targetTable === '') { - console.error('Failed to get table from adhoc query.'); throw new Error('Failed to get table from adhoc query.'); } } @@ -32,7 +31,7 @@ export class AdHocFilter { .filter((filter: AdHocVariableFilter) => { const valid = isValid(filter); if (!valid) { - console.error('Invalid adhoc filter will be ignored:', filter); + console.warn('Invalid adhoc filter will be ignored:', filter); } return valid; }) diff --git a/src/data/columnFilters.test.ts b/src/data/columnFilters.test.ts new file mode 100644 index 00000000..dc196233 --- /dev/null +++ b/src/data/columnFilters.test.ts @@ -0,0 +1,71 @@ +import { SelectedColumn } from "types/queryBuilder"; +import { columnFilterDateTime, columnFilterOr, columnFilterString } from "./columnFilters"; + +describe('columnFilterDateTime', () => { + it.each<{ col: SelectedColumn, expected: boolean }>([ + { col: { name: 't', type: 'Date' }, expected: true }, + { col: { name: 't', type: 'DateTime' }, expected: true }, + { col: { name: 't', type: 'Nullable(DateTime)' }, expected: true }, + { col: { name: 't', type: 'DateTime64' }, expected: true }, + { col: { name: 't', type: 'DateTime64(9)' }, expected: true }, + { col: { name: 't', type: 'date' }, expected: true }, + { col: { name: 't', type: 'datEtIME' }, expected: true }, + + { col: { name: 't', type: 'String' }, expected: false }, + { col: { name: 't', type: 'Int64' }, expected: false }, + { col: { name: 't', type: 'Dat' }, expected: false }, + { col: { name: 't', type: 'DaTme' }, expected: false }, + { col: { name: 't', type: 'nullaBLE(DaTme)' }, expected: false }, + ])('returns $expected for case $# ("$col.type")', ({ col, expected }) => { + expect(columnFilterDateTime(col)).toBe(expected); + }); +}); + +describe('columnFilterString', () => { + it.each<{ col: SelectedColumn, expected: boolean }>([ + { col: { name: 't', type: 'String' }, expected: true }, + { col: { name: 't', type: 'LowCardinality(String)' }, expected: true }, + { col: { name: 't', type: 'LowCardinality(Nullable(String))' }, expected: true }, + { col: { name: 't', type: 'newFeature(nullable(string))' }, expected: true }, + { col: { name: 't', type: 'string' }, expected: true }, + + { col: { name: 't', type: 'Int64' }, expected: false }, + { col: { name: 't', type: 'str' }, expected: false }, + { col: { name: 't', type: 'Date' }, expected: false }, + { col: { name: 't', type: 'DateTime' }, expected: false }, + ])('returns $expected for case $# ("$col.type")', ({ col, expected }) => { + expect(columnFilterString(col)).toBe(expected); + }); +}); + +describe('columnFilterOr', () => { + it('matches no filters using logical OR operator', () => { + const col: SelectedColumn = { name: 't', type: 'invalid' }; + expect( + columnFilterOr(col, + columnFilterString, + columnFilterDateTime, + ) + ).toBe(false); + }); + + it('compares multiple filters using logical OR operator, matching first', () => { + const col: SelectedColumn = { name: 't', type: 'String' }; + expect( + columnFilterOr(col, + columnFilterString, + columnFilterDateTime, + ) + ).toBe(true); + }); + + it('compares multiple filters using logical OR operator, matching last', () => { + const col: SelectedColumn = { name: 't', type: 'String' }; + expect( + columnFilterOr(col, + columnFilterDateTime, + columnFilterString, + ) + ).toBe(true); + }); +}); diff --git a/src/data/columnFilters.ts b/src/data/columnFilters.ts new file mode 100644 index 00000000..3abf6d10 --- /dev/null +++ b/src/data/columnFilters.ts @@ -0,0 +1,14 @@ +import { SelectedColumn } from "types/queryBuilder"; + + +export const columnFilterDateTime = (s: SelectedColumn): boolean => (s.type || '').toLowerCase().includes('date'); +export const columnFilterString = (s: SelectedColumn): boolean => (s.type || '').toLowerCase().includes('string') || (s.type || '').toLowerCase().includes('enum'); +export const columnFilterOr = (s: SelectedColumn, ...filterFuncs: ReadonlyArray<(s: SelectedColumn) => boolean>): boolean => { + for (let filterFn of filterFuncs) { + if (filterFn(s)) { + return true; + } + } + + return false; +}; diff --git a/src/data/migration.test.ts b/src/data/migration.test.ts new file mode 100644 index 00000000..9a98cd86 --- /dev/null +++ b/src/data/migration.test.ts @@ -0,0 +1,368 @@ +import { CHBuilderQuery, CHQuery, CHSqlQuery, EditorType } from "types/sql"; +import { migrateCHQuery } from "./migration"; +import { pluginVersion } from "utils/version"; +import { AggregateType, BuilderMode, ColumnHint, Filter, FilterOperator, OrderByDirection, QueryBuilderOptions, QueryType } from "types/queryBuilder"; +import { mapQueryTypeToGrafanaFormat } from "./utils"; + +describe('Query Editor Version Migration', () => { + it('does not apply migration for empty query', () => { + const query = {} as CHQuery; + + const migratedQuery = migrateCHQuery(query); + expect(migratedQuery).not.toBeUndefined(); + expect(migratedQuery).toEqual(query); + }); + + it('does not apply migration for default grafana query', () => { + const defaultGrafanaQuery = { + datasource: 'test-ds', + refId: 'A' + } as unknown as CHQuery; + + const migratedQuery = migrateCHQuery(defaultGrafanaQuery); + expect(migratedQuery).not.toBeUndefined(); + expect(migratedQuery).toEqual(defaultGrafanaQuery); + }); + + it('does not apply migration to latest query schema', () => { + const latestQuery: CHBuilderQuery = { + pluginVersion, + editorType: EditorType.Builder, + builderOptions: { + database: 'default', + table: 'test', + queryType: QueryType.Table, + mode: BuilderMode.List, + columns: [ + { name: 'a', type: 'String' }, + { name: 'b', type: 'String' }, + ], + aggregates: [ + { aggregateType: AggregateType.Count, column: '*', alias: 'c' } + ], + filters: [ + { + type: 'String', + operator: FilterOperator.Equals, + filterType: 'custom', + key: 'b', + condition: 'AND', + value: 'test' + } + ], + groupBy: ['a'], + orderBy: [ + { name: 'a', dir: OrderByDirection.ASC } + ], + limit: 250, + meta: { + otelEnabled: false, + otelVersion: 'test' + } + }, + rawSql: 'sql', + refId: 'A' + }; + + const migratedQuery = migrateCHQuery(latestQuery); + expect(migratedQuery).toBe(latestQuery); + expect(migratedQuery).toEqual(latestQuery); + }); + + it('apply migration for v3 builder query', () => { + const v3Query = { + refId: 'A', + datasource: { + type: 'ch-ds', + uid: 'test-uid' + }, + key: 'test-key', + queryType: 'builder', + rawSql: 'SELECT 1', + builderOptions: { + mode: 'list', + fields: [ + 'created_at', + 'level', + 'event' + ], + limit: 50, + database: 'default', + table: 'logs', + filters: [ + { + operator: 'WITH IN DASHBOARD TIME RANGE', + filterType: 'custom', + key: 'created_at', + type: 'datetime', + condition: 'AND', + restrictToFields: [ + { + name: 'created_at', + type: 'DateTime', + label: 'created_at', + picklistValues: [] + } + ] + }, + { + filterType: 'custom', + key: 'event', + type: 'String', + condition: 'AND', + operator: 'IS NOT NULL' + } + ], + metrics: [ + { field: 'level', aggregation: 'count', alias: 'c' } + ], + groupBy: ['c'], + orderBy: [ + { name: 'created_at', dir: 'DESC' } + ] + }, + format: 1, + selectedFormat: 1, + meta: { + timezone: 'tz' + } + } as unknown as CHQuery; + + const latestQuery: CHBuilderQuery = { + pluginVersion, + editorType: EditorType.Builder, + refId: 'A', + datasource: { + type: 'ch-ds', + uid: 'test-uid' + }, + key: 'test-key', + builderOptions: { + database: 'default', + table: 'logs', + queryType: QueryType.Table, + mode: BuilderMode.List, + columns: [ + { name: 'created_at' }, + { name: 'level' }, + { name: 'event' }, + ], + filters: [ + { + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: 'created_at', + type: 'datetime', + condition: 'AND', + restrictToFields: [ + { + name: 'created_at', + type: 'DateTime', + label: 'created_at', + picklistValues: [] + } + ] + } as Filter, + { + filterType: 'custom', + key: 'event', + type: 'String', + condition: 'AND', + operator: FilterOperator.IsNotNull + } + ], + aggregates: [ + { aggregateType: AggregateType.Count, column: 'level', alias: 'c' } + ], + groupBy: ['c'], + orderBy: [ + { name: 'created_at', dir: OrderByDirection.DESC } + ], + limit: 50, + }, + rawSql: 'SELECT 1', + format: mapQueryTypeToGrafanaFormat(QueryType.Table), + meta: { + timezone: 'tz' + } + }; + + const migratedQuery = migrateCHQuery(v3Query); + expect(migratedQuery).toEqual(latestQuery); + }); + + it('apply migration for v3 sql query', () => { + const v3Query = { + refId: 'A', + datasource: { + type: 'ch-ds', + uid: 'test-uid' + }, + key: 'test-key', + queryType: 'sql', + rawSql: 'SELECT 1', + meta: { + timezone: 'tz', + builderOptions: { + fields: [ + 'created_at', + 'level', + 'event' + ] + } + }, + format: 1, + selectedFormat: 1, + expand: true + } as unknown as CHQuery; + + const latestQuery: CHSqlQuery = { + pluginVersion, + editorType: EditorType.SQL, + refId: 'A', + datasource: { + type: 'ch-ds', + uid: 'test-uid' + }, + key: 'test-key', + rawSql: 'SELECT 1', + queryType: QueryType.Table, + format: mapQueryTypeToGrafanaFormat(QueryType.Table), + expand: true, + meta: { + timezone: 'tz', + builderOptions: { + database: '', + table: '', + queryType: QueryType.Table, + columns: [ + { name: 'created_at' }, + { name: 'level' }, + { name: 'event' }, + ] + } as QueryBuilderOptions + } + }; + + const migratedQuery = migrateCHQuery(v3Query); + expect(migratedQuery).toEqual(latestQuery); + }); + + it('apply migration for partial v3 query', () => { + const v3Query = { + queryType: 'builder', + builderOptions: { + mode: 'list', + }, + rawSql: '' + } as unknown as CHQuery; + + const latestQuery: CHBuilderQuery = { + pluginVersion, + editorType: EditorType.Builder, + builderOptions: { + database: '', + table: '', + queryType: QueryType.Table, + mode: BuilderMode.List, + columns: [] + }, + rawSql: '', + refId: '' + }; + + const migratedQuery = migrateCHQuery(v3Query); + expect(migratedQuery).toEqual(latestQuery); + }); + + it('v3 migration maps hinted columns', () => { + const v3Query = { + queryType: 'builder', + builderOptions: { + timeField: 'timestamp', + timeFieldType: 'DateTime', + logLevelField: 'level' + }, + rawSql: '' + } as unknown as CHQuery; + + const latestQuery: CHBuilderQuery = { + pluginVersion, + editorType: EditorType.Builder, + builderOptions: { + database: '', + table: '', + queryType: QueryType.TimeSeries, // TimeSeries because v3 timeField is present + columns: [ + { name: 'timestamp', type: 'DateTime', hint: ColumnHint.Time }, + { name: 'level', hint: ColumnHint.LogLevel } + ] + }, + format: undefined, + rawSql: '', + refId: '' + }; + + const migratedQuery = migrateCHQuery(v3Query); + expect(migratedQuery).toEqual(latestQuery); + }); + + it('v3 migration detects QueryType.TimeSeries', () => { + const v3Query = { + queryType: 'builder', + builderOptions: { + timeField: 'timestamp', + timeFieldType: 'DateTime', + }, + rawSql: '' + } as unknown as CHQuery; + + const latestQuery: CHBuilderQuery = { + pluginVersion, + editorType: EditorType.Builder, + builderOptions: { + database: '', + table: '', + queryType: QueryType.TimeSeries, + columns: [ + { name: 'timestamp', type: 'DateTime', hint: ColumnHint.Time }, + ] + }, + format: undefined, + rawSql: '', + refId: '' + }; + + const migratedQuery = migrateCHQuery(v3Query); + expect(migratedQuery).toEqual(latestQuery); + }); + + it('v3 migration detects QueryType.Logs', () => { + const v3Query = { + queryType: 'builder', + builderOptions: { + logLevelField: 'level', + }, + rawSql: '' + } as unknown as CHQuery; + + const latestQuery: CHBuilderQuery = { + pluginVersion, + editorType: EditorType.Builder, + builderOptions: { + database: '', + table: '', + queryType: QueryType.Logs, + columns: [ + { name: 'level', hint: ColumnHint.LogLevel } + ] + }, + format: undefined, + rawSql: '', + refId: '' + }; + + const migratedQuery = migrateCHQuery(v3Query); + expect(migratedQuery).toEqual(latestQuery); + }); +}); diff --git a/src/data/migration.ts b/src/data/migration.ts new file mode 100644 index 00000000..4f98a318 --- /dev/null +++ b/src/data/migration.ts @@ -0,0 +1,196 @@ +import { ColumnHint, Filter, QueryBuilderOptions, QueryType, SelectedColumn } from "types/queryBuilder"; +import { CHBuilderQuery, CHQuery, CHSqlQuery, EditorType } from "types/sql"; +import { isVersionGtOrEq, pluginVersion } from "utils/version"; +import { mapGrafanaFormatToQueryType } from "./utils"; + +export type AnyCHQuery = Partial & {[k: string]: any}; +export type AnyQueryBuilderOptions = Partial & {[k: string]: any}; + +/** + * Takes a CHQuery and transforms it to the latest interface. + */ +export const migrateCHQuery = (savedQuery: CHQuery): CHQuery => { + const isGrafanaDefaultQuery = savedQuery.rawSql === undefined; + if (isGrafanaDefaultQuery) { + return savedQuery; + } + + if (isV3CHQuery(savedQuery)) { + return migrateV3CHQuery(savedQuery); + } + + return savedQuery; +}; + +/** + * Takes v3 CHQuery and returns a version compatible with the latest editor. + */ +const migrateV3CHQuery = (savedQuery: AnyCHQuery): CHQuery => { + // Builder Query + if (savedQuery['queryType'] === 'builder') { + const builderQuery: CHBuilderQuery = { + ...savedQuery, + pluginVersion, + editorType: EditorType.Builder, + builderOptions: migrateV3QueryBuilderOptions(savedQuery['builderOptions'] || {}), + rawSql: savedQuery.rawSql || '', + refId: savedQuery.refId || '', + format: savedQuery.format, + }; + + if (savedQuery?.meta?.timezone) { + builderQuery.meta = { + timezone: savedQuery.meta.timezone + }; + } + + // delete unwanted properties from v3 + delete (builderQuery as any)['queryType']; + delete (builderQuery as any)['selectedFormat']; + + return builderQuery; + } + + // Raw SQL Query + const rawSqlQuery: CHSqlQuery = { + ...savedQuery, + pluginVersion, + editorType: EditorType.SQL, + rawSql: savedQuery.rawSql || '', + refId: savedQuery.refId || '', + format: savedQuery.format, + queryType: mapGrafanaFormatToQueryType(savedQuery.format), + meta: {} + }; + + if (savedQuery.expand) { + rawSqlQuery.expand = savedQuery.expand; + } + + if (savedQuery.meta) { + const meta = (savedQuery.meta as any); + if (meta.timezone) { + rawSqlQuery.meta!.timezone = meta.timezone; + } + + if (meta.builderOptions) { + // When changing from builder to raw editor, the builder options are saved and also require migration + rawSqlQuery.meta!.builderOptions = migrateV3QueryBuilderOptions(meta.builderOptions); + } + } + + // delete unwanted properties from v3 + delete (rawSqlQuery as any)['builderOptions']; + delete (rawSqlQuery as any)['selectedFormat']; + + return rawSqlQuery; +}; + +/** + * Takes v3 options and returns a version compatible with the latest builder. + */ +const migrateV3QueryBuilderOptions = (savedOptions: AnyQueryBuilderOptions): QueryBuilderOptions => { + const mapped: QueryBuilderOptions = { + database: savedOptions.database || '', + table: savedOptions.table || '', + queryType: getV3QueryType(savedOptions), + columns: [] + }; + + if (savedOptions.mode) { + mapped.mode = savedOptions.mode; + } + + if (savedOptions['fields'] || Array.isArray(savedOptions['fields'])) { + const oldColumns: string[] = savedOptions['fields']; + mapped.columns = oldColumns.map((name: string) => ({ name })); + } + + + const timeField: string = savedOptions['timeField']; + const timeFieldType: string = savedOptions['timeFieldType']; + if (timeField) { + const timeColumn: SelectedColumn = { + name: timeField, + type: timeFieldType, + hint: ColumnHint.Time + }; + + mapped.columns!.push(timeColumn); + } + + const logLevelField: string = savedOptions['logLevelField']; + if (logLevelField) { + const logLevelColumn: SelectedColumn = { + name: logLevelField, + hint: ColumnHint.LogLevel + }; + + mapped.columns!.push(logLevelColumn); + } + + if (savedOptions['metrics'] || Array.isArray(savedOptions['metrics'])) { + const oldAggregates: any[] = savedOptions['metrics']; + mapped.aggregates = oldAggregates.map(agg => ({ + aggregateType: agg['aggregation'], + column: agg['field'], + alias: agg['alias'] + })); + } + + if (savedOptions.filters || Array.isArray(savedOptions.filters)) { + const oldFilters: Filter[] = savedOptions.filters; + + mapped.filters = oldFilters.map((filter: Filter) => { + const result: Filter = { + ...filter + }; + + if (filter.key === timeField) { + result.hint = ColumnHint.Time; + } else if (filter.key === logLevelField) { + result.hint = ColumnHint.LogLevel; + } + + return result; + }); + } + + if (savedOptions.groupBy || Array.isArray(savedOptions.groupBy)) { + mapped.groupBy = savedOptions.groupBy; + } + + if (savedOptions.orderBy || Array.isArray(savedOptions.orderBy)) { + mapped.orderBy = savedOptions.orderBy; + } + + if (savedOptions.limit !== undefined && savedOptions.limit >= 0) { + mapped.limit = savedOptions.limit; + } + + return mapped; +}; + + +/** + * Checks if CHQuery is from <= v3 options. + */ +const isV3CHQuery = (savedQuery: AnyCHQuery): boolean => { + // pluginVersion was added in v4 + const oldPluginVersion = !savedQuery['pluginVersion'] || !isVersionGtOrEq(savedQuery.pluginVersion, '4.0.0'); + const oldQueryType = savedQuery['queryType'] === 'sql' || savedQuery['queryType'] === 'builder'; + return oldPluginVersion || oldQueryType; +}; + +/** + * Takes v3 options and returns the optimal QueryType. Defaults to QueryType.Table. + */ +const getV3QueryType = (savedOptions: AnyQueryBuilderOptions): QueryType => { + if (savedOptions['timeField']) { + return QueryType.TimeSeries; + } else if (savedOptions['logLevelField']) { + return QueryType.Logs; + } + + return QueryType.Table; +}; diff --git a/src/data/sqlGenerator.test.ts b/src/data/sqlGenerator.test.ts new file mode 100644 index 00000000..95793f1c --- /dev/null +++ b/src/data/sqlGenerator.test.ts @@ -0,0 +1,218 @@ +import { AggregateType, ColumnHint, FilterOperator, QueryBuilderOptions, QueryType } from 'types/queryBuilder'; +import { generateSql, getColumnByHint, getColumnIndexByHint, getColumnsByHints, isAggregateQuery } from './sqlGenerator'; + +describe('SQL Generator', () => { + it('generates logs sql', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'logs', + queryType: QueryType.Logs, + columns: [ + { name: 'timestamp', type: 'DateTime', hint: ColumnHint.Time }, + { name: 'level', type: 'String', hint: ColumnHint.LogLevel }, + { name: 'message', type: 'String', hint: ColumnHint.LogMessage }, + ], + limit: 1000, + filters: [ + { + filterType: 'custom', + key: 'message', + type: 'String', + condition: 'AND', + operator: FilterOperator.IsNotNull + } + ], + orderBy: [] + }; + const expectedSql = ( + 'SELECT timestamp as timestamp, message as body, level as level ' + + 'FROM "default"."logs" WHERE ( message IS NOT NULL ) LIMIT 1000' + ); + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSql); + }); + + it('generates trace sql', () => { + const opts: QueryBuilderOptions = { + database: 'otel', + table: 'otel_traces', + queryType: QueryType.Traces, + columns: [ + { name: 'TraceId', type: 'String', hint: ColumnHint.TraceId }, + { name: 'SpanId', type: 'String', hint: ColumnHint.TraceSpanId }, + { name: 'ParentSpanId', type: 'String', hint: ColumnHint.TraceParentSpanId }, + { name: 'ServiceName', type: 'LowCardinality(String)', hint: ColumnHint.TraceServiceName }, + { name: 'SpanName', type: 'LowCardinality(String)', hint: ColumnHint.TraceOperationName }, + { name: 'Timestamp', type: 'DateTime64(9)', hint: ColumnHint.Time }, + { name: 'Duration', type: 'Int64', hint: ColumnHint.TraceDurationTime }, + { name: 'SpanAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceTags }, + { name: 'ResourceAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceServiceTags }, + ], + limit: 1000, + filters: [ + { + filterType: 'custom', + key: '', // hint property is used instead of column name + type: 'String', + condition: 'AND', + hint: ColumnHint.TraceId, + operator: FilterOperator.Equals, + value: '1234' + } + ], + orderBy: [] + }; + const expectedSql = ( + 'SELECT "TraceId" as traceID, "SpanId" as spanID, "ParentSpanId" as parentSpanID, "ServiceName" as serviceName, ' + + '"SpanName" as operationName, "Timestamp" as startTime, "Duration" as duration, ' + + 'arrayMap(key -> map(\'key\', key, \'value\',"SpanAttributes"[key]), mapKeys("SpanAttributes")) as tags, ' + + 'arrayMap(key -> map(\'key\', key, \'value\',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags ' + + 'FROM "otel"."otel_traces" WHERE ( TraceId = \'1234\' ) ORDER BY startTime ASC LIMIT 1000' + ); + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSql); + }); + + it('generates other sql', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'data', + queryType: QueryType.Table, + columns: [ + { name: 'timestamp', type: 'DateTime' }, + { name: 'text', type: 'String' }, + ], + limit: 1000, + filters: [], + orderBy: [] + }; + const expectedSql = ( + 'SELECT "timestamp", "text" FROM "default"."data" LIMIT 1000' + ); + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSql); + }); + + it('generates other sql with filters', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'data', + queryType: QueryType.Table, + columns: [ + { name: 'timestamp', type: 'DateTime' }, + { name: 'text', type: 'String' }, + ], + limit: 1000, + filters: [ + { + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: 'created_at', + type: 'datetime', + condition: 'AND' + }, + { + filterType: 'custom', + key: 'event', + type: 'String', + condition: 'AND', + operator: FilterOperator.IsNotNull + } + ], + orderBy: [] + }; + const expectedSql = ( + 'SELECT "timestamp", "text" FROM "default"."data" ' + + 'WHERE ( created_at >= $__fromTime AND created_at <= $__toTime ) AND ( event IS NOT NULL ) ' + + 'LIMIT 1000' + ); + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSql); + }); + + it('excludes LIMIT when limit is 0', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'data', + queryType: QueryType.Table, + limit: 0 + }; + const expectedSql = ( + 'SELECT FROM "default"."data"' + ); + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSql); + }); + + it('excludes LIMIT when limit is excluded', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'data', + queryType: QueryType.Table + }; + const expectedSql = ( + 'SELECT FROM "default"."data"' + ); + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSql); + }); +}); + +describe('isAggregateQuery', () => { + it('returns true for aggregate query', () => { + const builderOptions = { aggregates: [{ column: 'foo', aggregateType: AggregateType.Count }] } as QueryBuilderOptions; + expect(isAggregateQuery(builderOptions)).toEqual(true); + }); + it('returns false for query without aggregates', () => { + const builderOptions = {} as QueryBuilderOptions; + expect(isAggregateQuery(builderOptions)).toEqual(false); + }); +}); + +describe('getColumnByHint', () => { + it('returns a selected column when present', () => { + const testColumn = { name: 'time', type: 'datetime', hint: ColumnHint.Time }; + const builderOptions = { columns: [testColumn] } as QueryBuilderOptions; + expect(getColumnByHint(builderOptions, ColumnHint.Time)).toMatchObject(testColumn); + }); + it('returns a undefined when column not present', () => { + const testColumn = { name: 'time', type: 'datetime' }; + const builderOptions = { columns: [testColumn] } as QueryBuilderOptions; + expect(getColumnByHint(builderOptions, ColumnHint.Time)).toBeUndefined(); + }); +}); + +describe('getColumnIndexByHint', () => { + it('returns a selected column index when present', () => { + const testColumns = [{}, { name: 'time', type: 'datetime', hint: ColumnHint.Time }]; + const builderOptions = { columns: testColumns } as QueryBuilderOptions; + expect(getColumnIndexByHint(builderOptions, ColumnHint.Time)).toEqual(1); + }); + it('returns -1 when column not present', () => { + const testColumn = { name: 'time', type: 'datetime' }; + const builderOptions = { columns: [testColumn] } as QueryBuilderOptions; + expect(getColumnIndexByHint(builderOptions, ColumnHint.Time)).toEqual(-1); + }); +}); + +describe('getColumnsByHints', () => { + it('returns selected columns when present', () => { + const testColumns = [ + { name: 'time', type: 'DateTime', hint: ColumnHint.Time }, + { name: 'level', type: 'String', hint: ColumnHint.LogLevel }, + ]; + const builderOptions = { columns: testColumns } as QueryBuilderOptions; + expect(getColumnsByHints(builderOptions, [ColumnHint.Time, ColumnHint.LogLevel])).toHaveLength(2); + }); + it('returns empty array when columns not present', () => { + const testColumn = { name: 'time', type: 'datetime' }; + const builderOptions = { columns: [testColumn] } as QueryBuilderOptions; + expect(getColumnsByHints(builderOptions, [ColumnHint.Time])).toHaveLength(0); + }); +}); diff --git a/src/data/sqlGenerator.ts b/src/data/sqlGenerator.ts new file mode 100644 index 00000000..6518ff8e --- /dev/null +++ b/src/data/sqlGenerator.ts @@ -0,0 +1,371 @@ +import { getSqlFromQueryBuilderOptions, getOrderBy } from 'components/queryBuilder/utils'; +import { BooleanFilter, ColumnHint, DateFilterWithValue, FilterOperator, MultiFilter, NumberFilter, QueryBuilderOptions, QueryType, SelectedColumn, StringFilter, TimeUnit } from 'types/queryBuilder'; + +export const generateSql = (options: QueryBuilderOptions): string => { + if (options.queryType === QueryType.Traces) { + return generateTraceQuery(options); + } else if (options.queryType === QueryType.Logs) { + return generateLogsQuery(options); + } + + return getSqlFromQueryBuilderOptions(options); +} + +/** + * Generates trace query with columns that fit Grafana's Trace panel + * Column aliases follow this structure: + * https://grafana.com/docs/grafana/latest/explore/trace-integration/#data-frame-structure + */ +const generateTraceQuery = (options: QueryBuilderOptions): string => { + const { database, table } = options; + + const queryParts: string[] = []; + + // TODO: these columns could be a map or some other convenience function + const selectParts: string[] = []; + const traceId = getColumnByHint(options, ColumnHint.TraceId); + if (traceId !== undefined) { + selectParts.push(`${escapeIdentifier(traceId.name)} as traceID`); + } + + const traceSpanId = getColumnByHint(options, ColumnHint.TraceSpanId); + if (traceSpanId !== undefined) { + selectParts.push(`${escapeIdentifier(traceSpanId.name)} as spanID`); + } + + const traceParentSpanId = getColumnByHint(options, ColumnHint.TraceParentSpanId); + if (traceParentSpanId !== undefined) { + selectParts.push(`${escapeIdentifier(traceParentSpanId.name)} as parentSpanID`); + } + + const traceServiceName = getColumnByHint(options, ColumnHint.TraceServiceName); + if (traceServiceName !== undefined) { + selectParts.push(`${escapeIdentifier(traceServiceName.name)} as serviceName`); + } + + const traceOperationName = getColumnByHint(options, ColumnHint.TraceOperationName); + if (traceOperationName !== undefined) { + selectParts.push(`${escapeIdentifier(traceOperationName.name)} as operationName`); + } + + const traceStartTime = getColumnByHint(options, ColumnHint.Time); + if (traceStartTime !== undefined) { + selectParts.push(`${escapeIdentifier(traceStartTime.name)} as startTime`); + } + + const traceDurationTime = getColumnByHint(options, ColumnHint.TraceDurationTime); + if (traceDurationTime !== undefined) { + const timeUnit = options.meta?.traceDurationUnit; + selectParts.push(getTraceDurationSelectSql(escapeIdentifier(traceDurationTime.name), timeUnit)); + } + + // TODO: for tags and serviceTags, consider the column type. They might not require mapping, they could already be JSON. + const traceTags = getColumnByHint(options, ColumnHint.TraceTags); + if (traceTags !== undefined) { + selectParts.push(`arrayMap(key -> map('key', key, 'value',${escapeIdentifier(traceTags.name)}[key]), mapKeys(${escapeIdentifier(traceTags.name)})) as tags`); + } + + const traceServiceTags = getColumnByHint(options, ColumnHint.TraceServiceTags); + if (traceServiceTags !== undefined) { + selectParts.push(`arrayMap(key -> map('key', key, 'value',${escapeIdentifier(traceServiceTags.name)}[key]), mapKeys(${escapeIdentifier(traceServiceTags.name)})) as serviceTags`); + } + const selectPartsSql = selectParts.join(', '); + + queryParts.push('SELECT'); + queryParts.push(selectPartsSql); + queryParts.push('FROM'); + queryParts.push(getTableIdentifier(database, table)); + + const hasTraceIdFilter = !options.meta?.isTraceSearchMode && options.meta?.traceId + const hasFilters = (options.filters?.length || 0) > 0; + + if (hasTraceIdFilter || hasFilters) { + queryParts.push('WHERE'); + } + + if (hasTraceIdFilter) { + const traceId = options.meta!.traceId; + queryParts.push(`traceID = '${traceId}'`); + } + + if (hasFilters) { + queryParts.push(getFilters(options)); + } + + if (traceStartTime !== undefined) { + queryParts.push('ORDER BY startTime ASC'); + } + + const limit = getLimit(options.limit); + if (limit !== '') { + queryParts.push(limit); + } + + return concatQueryParts(queryParts); +} + +/** + * Generates logs query with columns that fit Grafana's Logs panel + * Column aliases follow this structure: + * https://grafana.com/developers/plugin-tools/tutorials/build-a-logs-data-source-plugin#logs-data-frame-format + * + * note: column order seems to matter as well as alias name + */ +const generateLogsQuery = (options: QueryBuilderOptions): string => { + const { database, table } = options; + + const queryParts: string[] = []; + + // TODO: these columns could be a map or some other convenience function + const selectParts: string[] = []; + const logTime = getColumnByHint(options, ColumnHint.Time); + if (logTime !== undefined) { + // Must be first column in list. + logTime.alias = 'timestamp'; + selectParts.push(getColumnIdentifier(logTime)); + } + + const logMessage = getColumnByHint(options, ColumnHint.LogMessage); + if (logMessage !== undefined) { + // Must be second column in list. + logMessage.alias = 'body'; + selectParts.push(getColumnIdentifier(logMessage)); + } + + const logLevel = getColumnByHint(options, ColumnHint.LogLevel); + if (logLevel !== undefined) { + // TODO: "severity" should be a number, but "level" can be a string? Perhaps we can check the column type here? + logLevel.alias = 'level'; + selectParts.push(getColumnIdentifier(logLevel)); + } + + options.columns?. + filter(c => c.hint === undefined). // remove specialized columns + forEach(c => selectParts.push(getColumnIdentifier(c))); + + const selectPartsSql = selectParts.join(', '); + + queryParts.push('SELECT'); + queryParts.push(selectPartsSql); + queryParts.push('FROM'); + queryParts.push(getTableIdentifier(database, table)); + + if ((options.filters?.length || 0) > 0) { + queryParts.push('WHERE'); + queryParts.push(getFilters(options)); + } + + if ((options.orderBy?.length || 0) > 0) { + queryParts.push('ORDER BY'); + queryParts.push(getOrderBy(options.orderBy, false)); + } + + const limit = getLimit(options.limit); + if (limit !== '') { + queryParts.push(limit); + } + + return concatQueryParts(queryParts); +} + +export const isAggregateQuery = (builder: QueryBuilderOptions): boolean => (builder.aggregates?.length || 0) > 0; +export const getColumnByHint = (options: QueryBuilderOptions, hint: ColumnHint): SelectedColumn | undefined => options.columns?.find(c => c.hint === hint); +export const getColumnIndexByHint = (options: QueryBuilderOptions, hint: ColumnHint): number => options.columns?.findIndex(c => c.hint === hint) || -1; +export const getColumnsByHints = (options: QueryBuilderOptions, hints: readonly ColumnHint[]): readonly SelectedColumn[] => { + const columns = []; + + for (let hint of hints) { + const col = getColumnByHint(options, hint); + if (col !== undefined) { + columns.push(col); + } + } + + return columns; +} + +const getColumnIdentifier = (col: SelectedColumn): string => { + let colName = col.name; + + if (colName.includes(' ')) { + colName = escapeIdentifier(col.name); + } + + // allow for functions like count() + if (colName.includes('(') || colName.includes(')') || colName.includes('"') || colName.includes('"')) { + colName = col.name + } + + if (col.alias) { + return `${colName} as ${col.alias}` + } + + return colName; +} + +const getTableIdentifier = (database: string, table: string): string => { + const sep = (database === '' || table === '') ? '' : '.'; + return `${escapeIdentifier(database)}${sep}${escapeIdentifier(table)}`; +} + +const escapeIdentifier = (id: string): string => { + return id === '' ? '' : `"${id}"`; +} + +/** + * Returns the a SELECT column for trace duration. + * Time unit is used to convert the value to milliseconds, as is required by Grafana's Trace panel. + */ +const getTraceDurationSelectSql = (columnIdentifier: string, timeUnit?: TimeUnit): string => { + const alias = 'duration'; + switch (timeUnit) { + case TimeUnit.Seconds: + return `multiply(${columnIdentifier}, 1000) as ${alias}`; + case TimeUnit.Milliseconds: + return `${columnIdentifier} as ${alias}`; + case TimeUnit.Microseconds: + return `intDivOrZero(${columnIdentifier}, 1000) as ${alias}`; + case TimeUnit.Nanoseconds: + return `intDivOrZero(${columnIdentifier}, 1000000) as ${alias}`; + default: + return `${columnIdentifier} as ${alias}`; + } +} + +/** + * Concatenates query parts with no empty spaces. + */ +const concatQueryParts = (parts: readonly string[]): string => { + let query = ''; + for (let i = 0; i < parts.length; i++) { + const p = parts[i]; + if (!p) { + continue; + } + + query += p; + + if (i !== parts.length - 1) { + query += ' ' + } + } + + return query; +} + +const getLimit = (limit?: number | undefined): string => { + limit = Math.max(0, limit || 0); + if (limit > 0) { + return 'LIMIT ' + limit; + } + + return ''; +}; + +const getFilters = (options: QueryBuilderOptions): string => { + const filters = options.filters || []; + const builtFilters: string[] = []; + + for (const filter of filters) { + const filterParts: string[] = []; + + let column = filter.key; + let type = filter.type; + const hintedColumn = filter.hint && getColumnByHint(options, filter.hint); + if (hintedColumn) { + column = hintedColumn.name; + type = hintedColumn.type || type; + } + + if (!column) { + continue; + } + filterParts.push(column); + + let operator: string = filter.operator; + let negate = false; + if (filter.operator === FilterOperator.NotLike) { + operator = 'LIKE'; + negate = true; + } else if (filter.operator === FilterOperator.OutsideGrafanaTimeRange) { + operator = ''; + negate = true; + } else if (filter.operator === FilterOperator.WithInGrafanaTimeRange){ + operator = ''; + } + + if (operator) { + filterParts.push(operator); + } + + if (isNullFilter(filter.operator)) { + // empty + } else if (isBooleanFilter(type)) { + filterParts.push(String((filter as BooleanFilter).value)); + } else if (isNumberFilter(type)) { + filterParts.push(String((filter as NumberFilter).value || '0')); + } else if (isDateFilter(type)) { + if (isDateFilterWithoutValue(type, filter.operator)) { + if (isDateType(type)) { + filterParts.push('>=', '\$__fromTime', 'AND', column, '<=', '\$__toTime'); + } + } else { + switch ((filter as DateFilterWithValue).value) { + case 'GRAFANA_START_TIME': + if (isDateType(type)) { + filterParts.push('\$__fromTime'); + } + break; + case 'GRAFANA_END_TIME': + if (isDateType(type)) { + filterParts.push('\$__toTime'); + } + break; + default: + filterParts.push(String((filter as DateFilterWithValue).value || 'TODAY')); + } + } + } else if (isStringFilter(type, filter.operator)) { + if (filter.operator === FilterOperator.Like || filter.operator === FilterOperator.NotLike) { + filterParts.push(`'%${filter.value || ''}%'`); + } else { + filterParts.push(formatStringValue((filter as StringFilter).value || '')); + } + } else if (isMultiFilter(type, filter.operator)) { + filterParts.push(`(${(filter as MultiFilter).value?.map(v => formatStringValue(v)).join(', ')})`); + } else { + filterParts.push(formatStringValue((filter as StringFilter).value || '')); + } + + if (negate) { + filterParts.unshift('NOT', '('); + filterParts.push(')'); + } + + filterParts.unshift('('); + if (builtFilters.length > 0) { + filterParts.unshift(filter.condition); + } + filterParts.push(')'); + + const builtFilter = concatQueryParts(filterParts); + builtFilters.push(builtFilter); + } + + return concatQueryParts(builtFilters); +}; + +const isBooleanType = (type: string): boolean => (type?.toLowerCase().startsWith('boolean')); +const numberTypes = ['int', 'float', 'decimal']; +const isNumberType = (type: string): boolean => numberTypes.some(t => type?.toLowerCase().includes(t)); +const isDateType = (type: string): boolean => type?.toLowerCase().startsWith('date') || type?.toLowerCase().startsWith('nullable(date'); +// const isDateTimeType = (type: string): boolean => type?.toLowerCase().startsWith('datetime') || type?.toLowerCase().startsWith('nullable(datetime'); +const isStringType = (type: string): boolean => type === 'String' && !(isBooleanType(type) || isNumberType(type) || isDateType(type)); +const isNullFilter = (operator: FilterOperator): boolean => operator === FilterOperator.IsNull || operator === FilterOperator.IsNotNull; +const isBooleanFilter = (type: string): boolean => isBooleanType(type); +const isNumberFilter = (type: string): boolean => isNumberType(type); +const isDateFilterWithoutValue = (type: string, operator: FilterOperator): boolean => isDateType(type) && (operator === FilterOperator.WithInGrafanaTimeRange || operator === FilterOperator.OutsideGrafanaTimeRange); +const isDateFilter = (type: string): boolean => isDateType(type); +const isStringFilter = (type: string, operator: FilterOperator): boolean => isStringType(type) && !(operator === FilterOperator.In || operator === FilterOperator.NotIn); +const isMultiFilter = (type: string, operator: FilterOperator): boolean => isStringType(type) && (operator === FilterOperator.In || operator === FilterOperator.NotIn); +const formatStringValue = (filter: string): string => filter.startsWith('$') ? (filter || '') : `'${filter || ''}'`; diff --git a/src/data/utils.test.ts b/src/data/utils.test.ts new file mode 100644 index 00000000..8838f4d8 --- /dev/null +++ b/src/data/utils.test.ts @@ -0,0 +1,37 @@ +import { QueryBuilderOptions, QueryType } from "types/queryBuilder"; +import { columnLabelToPlaceholder, isBuilderOptionsRunnable } from "./utils"; + +describe('isBuilderOptionsRunnable', () => { + it('should return false for empty builder options', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'test', + queryType: QueryType.Table + }; + + const runnable = isBuilderOptionsRunnable(opts); + expect(runnable).toBe(false); + }); + + it('should return true for valid builder options', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'test', + queryType: QueryType.Table, + columns: [ + { name: 'valid_column' } + ] + }; + + const runnable = isBuilderOptionsRunnable(opts); + expect(runnable).toBe(true); + }); +}); + +describe('columnLabelToPlaceholder', () => { + it('converts to lowercase and removes multiple spaces', () => { + const expected = 'expected_test_output'; + const actual = columnLabelToPlaceholder('Expected TEST output'); + expect(actual).toEqual(expected); + }); +}); diff --git a/src/data/utils.ts b/src/data/utils.ts new file mode 100644 index 00000000..d64f46ed --- /dev/null +++ b/src/data/utils.ts @@ -0,0 +1,59 @@ +import { QueryBuilderOptions, QueryType } from "types/queryBuilder" + +/** + * Returns true if the builder options contain enough information to start showing a query + */ +export const isBuilderOptionsRunnable = (builderOptions: QueryBuilderOptions): boolean => { + return ( + (builderOptions.columns?.length || 0) > 0 || + (builderOptions.filters?.length || 0) > 0 || + (builderOptions.orderBy?.length || 0) > 0 || + (builderOptions.aggregates?.length || 0) > 0 || + (builderOptions.groupBy?.length || 0) > 0 + ); +}; + +/** + * Converts QueryType to Grafana format + * src: https://github.com/grafana/sqlds/blob/main/query.go#L20 + */ +export const mapQueryTypeToGrafanaFormat = (t?: QueryType): number => { + switch (t) { + case QueryType.Table: + return 1; + case QueryType.Logs: + return 2; + case QueryType.TimeSeries: + return 0; + case QueryType.Traces: + return 3; + default: + return 1 << 8; // an unused u32, defaults to timeseries/graph on plugin backend. + } +}; + +/** + * Converts Grafana format to builder QueryType + * src: https://github.com/grafana/sqlds/blob/main/query.go#L20 + */ +export const mapGrafanaFormatToQueryType = (f?: number): QueryType => { + switch (f) { + case 0: + return QueryType.TimeSeries; + case 1: + return QueryType.Table; + case 2: + return QueryType.Logs; + case 3: + return QueryType.Traces; + default: + return QueryType.Table; + } +}; + + +/** + * Converts label into sql-style column name. + * Example: "Test Column" -> "test_column" + */ +export const columnLabelToPlaceholder = (label: string) => label.toLowerCase().replace(/ /g, '_'); diff --git a/src/hooks/useBuilderOptionChanges.test.ts b/src/hooks/useBuilderOptionChanges.test.ts new file mode 100644 index 00000000..5d3fa9ab --- /dev/null +++ b/src/hooks/useBuilderOptionChanges.test.ts @@ -0,0 +1,26 @@ +import { renderHook } from "@testing-library/react"; +import { useBuilderOptionChanges } from "./useBuilderOptionChanges"; + +interface TestData { + x: number; + y: number; +} + +describe('useBuilderOptionChanges', () => { + it('calls onChange with merged object', async () => { + const onChange = jest.fn(); + const prevState: TestData = { + x: 1, + y: 2 + }; + const hook = renderHook(() => useBuilderOptionChanges(onChange, prevState)); + const applyChanges = hook.result.current; + + + expect(applyChanges).not.toBeUndefined(); + applyChanges('y')(3); + + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith({ x: 1, y: 3 }); + }); +}); diff --git a/src/hooks/useBuilderOptionChanges.ts b/src/hooks/useBuilderOptionChanges.ts new file mode 100644 index 00000000..c39d5174 --- /dev/null +++ b/src/hooks/useBuilderOptionChanges.ts @@ -0,0 +1,25 @@ +import React from 'react'; + +type onOptionChangeFn = (key: keyof T) => (nextValue: React.SetStateAction) => void; + +/** + * Returns a function that can apply changes with an object or a specific key in an object. When called + * will run another function with the changes applied. + * + * Does not deep clone the object. This is used for top level fields on the QueryBuilderOptions type. + * + * @param onChange a function that receives the updated state from the change function + * @param prevState the current (previous) state object + * @returns a function used to apply changes to individual fields + */ +export function useBuilderOptionChanges(onChange: (nextState: T) => void, prevState: T): onOptionChangeFn { + return (key: keyof T) => + (nextValue: React.SetStateAction) => { + const nextState: T = { + ...prevState, + [key]: nextValue + }; + + onChange(nextState); + }; +} diff --git a/src/hooks/useBuilderOptionsState.test.ts b/src/hooks/useBuilderOptionsState.test.ts new file mode 100644 index 00000000..34e8eb8a --- /dev/null +++ b/src/hooks/useBuilderOptionsState.test.ts @@ -0,0 +1,154 @@ +import { ColumnHint, QueryType } from "types/queryBuilder"; +import { setAllOptions, setColumnByHint, setDatabase, setOptions, setOtelEnabled, setOtelVersion, setQueryType, setTable, testFuncs } from "./useBuilderOptionsState"; +const { reducer, buildInitialState } = testFuncs; + +describe('reducer', () => { + it('applies SetOptions action', async () => { + const prevState = buildInitialState(); + const action = setOptions({ + limit: 100, + // Include meta to verify deep merge + meta: { + otelEnabled: true + } + }); + + const nextState = reducer(prevState, action); + expect(nextState.limit).toEqual(100); + expect(nextState.meta?.otelEnabled).toEqual(true); + }); + it('applies SetAllOptions action', async () => { + const prevState = buildInitialState({ + limit: 100 + }); + const action = setAllOptions({ + database: 'default', + table: 'test', + queryType: QueryType.Table + }); + + const nextState = reducer(prevState, action); + // SetAllOptions will overwrite with defaults + expect(nextState.limit).not.toEqual(100); + }); + it('run SetQueryType action with no changes', async () => { + const prevState = buildInitialState({ + queryType: QueryType.TimeSeries + }); + const action = setQueryType(QueryType.TimeSeries); + + const nextState = reducer(prevState, action); + expect(nextState.queryType).toEqual(QueryType.TimeSeries); + }); + it('applies SetQueryType to reset settings but preserve db/table', async () => { + const prevState = buildInitialState({ + database: 'prev_db', + table: 'prev_table', + queryType: QueryType.Table, + groupBy: ['will', 'be', 'reset'] + }); + const action = setQueryType(QueryType.Logs); + + const nextState = reducer(prevState, action); + expect(nextState.database).toEqual('prev_db'); + expect(nextState.table).toEqual('prev_table'); + expect(nextState.queryType).toEqual(QueryType.Logs); + expect(nextState.groupBy).toBeFalsy(); + }); + it('applies SetDatabase to reset settings but preserve query type', async () => { + const prevState = buildInitialState({ + database: 'prev_db', + table: 'prev_table', + queryType: QueryType.Logs, + groupBy: ['will', 'be', 'reset'] + }); + const action = setDatabase('next_db'); + + const nextState = reducer(prevState, action); + expect(nextState.database).toEqual('next_db'); + expect(nextState.table).toEqual(''); + expect(nextState.queryType).toEqual(QueryType.Logs); + expect(nextState.groupBy).toBeFalsy(); + }); + it('applies SetTable to reset settings but preserve db/queryType', async () => { + const prevState = buildInitialState({ + database: 'prev_db', + table: 'prev_table', + queryType: QueryType.Logs, + groupBy: ['will', 'be', 'reset'] + }); + const action = setTable('next_table'); + + const nextState = reducer(prevState, action); + expect(nextState.database).toEqual('prev_db'); + expect(nextState.table).toEqual('next_table'); + expect(nextState.queryType).toEqual(QueryType.Logs); + expect(nextState.groupBy).toBeFalsy(); + }); + it('applies SetOtelEnabled action', async () => { + const prevState = buildInitialState({ + limit: 50 + }); + const action = setOtelEnabled(true); + + const nextState = reducer(prevState, action); + expect(nextState.limit).toEqual(50); + expect(nextState.meta?.otelEnabled).toEqual(true); + }); + it('applies SetOtelVersion action', async () => { + const prevState = buildInitialState({ + limit: 50 + }); + const action = setOtelVersion('0.0.1'); + + const nextState = reducer(prevState, action); + expect(nextState.limit).toEqual(50); + expect(nextState.meta?.otelVersion).toEqual('0.0.1'); + }); + it('applies SetColumnByHint action, overwrites existing column', async () => { + const prevState = buildInitialState({ + columns: [ + { name: 'prev_timestamp', hint: ColumnHint.Time }, + { name: 'a' }, + { name: 'b' }, + { name: 'c' }, + ] + }); + const action = setColumnByHint({ name: 'next_timestamp', hint: ColumnHint.Time }); + + const nextState = reducer(prevState, action); + expect(nextState.columns).toHaveLength(4); + expect(nextState.columns![0].name).toEqual('a'); + expect(nextState.columns![1].name).toEqual('b'); + expect(nextState.columns![2].name).toEqual('c'); + // Updated column is filtered and pushed to end of array + expect(nextState.columns![3].name).toEqual('next_timestamp'); + }); +}); + + +describe('buildInitialState', () => { + it('builds initial state using defaults', async () => { + const state = buildInitialState(); + expect(state).not.toBeUndefined(); + expect(state.database).toEqual(''); + expect(state.table).toEqual(''); + expect(state.queryType).toEqual(QueryType.Table); + }); + + it('builds initial state and merge saved state', async () => { + const state = buildInitialState({ + table: 'saved_table', + limit: 50, + meta: { + otelEnabled: true + } + }); + expect(state).not.toBeUndefined(); + expect(state.database).toEqual(''); + expect(state.table).toEqual('saved_table'); + expect(state.limit).toEqual(50); + expect(state.queryType).toEqual(QueryType.Table); + expect(state.meta?.otelEnabled).toEqual(true); + }); +}); diff --git a/src/hooks/useBuilderOptionsState.ts b/src/hooks/useBuilderOptionsState.ts new file mode 100644 index 00000000..a5ee4a0a --- /dev/null +++ b/src/hooks/useBuilderOptionsState.ts @@ -0,0 +1,151 @@ +import { Reducer, useReducer } from "react"; +import { QueryBuilderOptions, QueryType, SelectedColumn } from "types/queryBuilder"; +import { defaultCHBuilderQuery } from "types/sql"; + +enum BuilderOptionsActionType { + SetOptions = 'set_options', + SetAllOptions = 'set_all_options', + SetQueryType = 'set_query_type', + SetDatabase = 'set_database', + SetTable = 'set_table', + SetOtelEnabled = 'set_otel_enabled', + SetOtelVersion = 'set_otel_version', + SetColumnByHint = 'set_column_by_hint', +}; + +type QueryBuilderOptionsReducerAction = { + type: BuilderOptionsActionType, + payload: Partial +}; + +type GenericReducerAction = { + type: BuilderOptionsActionType, + payload: any +}; + +export type BuilderOptionsReducerAction = QueryBuilderOptionsReducerAction | GenericReducerAction; + +const createAction = (type: BuilderOptionsActionType, payload: Partial): BuilderOptionsReducerAction => ({ type, payload }); +const createGenericAction = (type: BuilderOptionsActionType, payload: any): GenericReducerAction => ({ type, payload }); +export const setOptions = (options: Partial): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetOptions, options); +export const setAllOptions = (options: QueryBuilderOptions): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetAllOptions, options); +export const setQueryType = (queryType: QueryType): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetQueryType, { queryType }); +export const setDatabase = (database: string): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetDatabase, { database }); +export const setTable = (table: string): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetTable, { table }); +export const setOtelEnabled = (otelEnabled: boolean): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetOtelEnabled, { meta: { otelEnabled } }); +export const setOtelVersion = (otelVersion: string): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetOtelVersion, { meta: { otelVersion } }); +export const setColumnByHint = (column: SelectedColumn): GenericReducerAction => createGenericAction(BuilderOptionsActionType.SetColumnByHint, { column }); + +const reducer = (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + const actionFn = actions.get(action.type); + if (!actionFn) { + throw Error('missing function for BuilderOptionsActionType: ' + action.type); + } + + const nextState = actionFn(state, action); + // console.log('ACTION:', action.type, 'PAYLOAD:', action.payload, 'NEXT STATE:', nextState); + return nextState; +}; + +/** + * A mapping between action type and reducer function, used in reducer to apply action changes. + */ +const actions = new Map>([ + [BuilderOptionsActionType.SetOptions, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + // A catch-all action for applying option changes. + const nextOptions = action.payload as Partial; + return mergeBuilderOptionsState(state, nextOptions); + }], + [BuilderOptionsActionType.SetAllOptions, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + // Resets existing state with provided options. + const nextOptions = action.payload as Partial; + return buildInitialState(nextOptions); + }], + [BuilderOptionsActionType.SetQueryType, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + // If switching query type, reset the editor. + const nextQueryType = action.payload.queryType; + if (state.queryType !== nextQueryType) { + return buildInitialState({ + database: state.database, + table: state.table, + queryType: nextQueryType + }); + } + + return state; + }], + [BuilderOptionsActionType.SetDatabase, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + // Clear table and reset editor when database changes + return buildInitialState({ + database: action.payload.database, + table: '', + queryType: state.queryType + }); + }], + [BuilderOptionsActionType.SetTable, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + // Reset editor when table changes + return buildInitialState({ + database: state.database, + table: action.payload.table, + queryType: state.queryType + }); + }], + [BuilderOptionsActionType.SetOtelEnabled, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + return mergeBuilderOptionsState(state, { + meta: { + otelEnabled: Boolean(action.payload.meta?.otelEnabled), + } + }); + }], + [BuilderOptionsActionType.SetOtelVersion, (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { + return mergeBuilderOptionsState(state, { + meta: { + otelVersion: action.payload.meta?.otelVersion + } + }); + }], + [BuilderOptionsActionType.SetColumnByHint, (state: QueryBuilderOptions, action: GenericReducerAction): QueryBuilderOptions => { + const col = action.payload.column as SelectedColumn; + const nextColumns = (state.columns || []).filter(c => c.hint !== col.hint); + nextColumns.push(col); + + return mergeBuilderOptionsState(state, { + columns: nextColumns + }); + }], +]); + +const buildInitialState = (savedOptions?: Partial): QueryBuilderOptions => { + const defaultOptions = defaultCHBuilderQuery.builderOptions; + const initialState = { + ...defaultOptions, + ...savedOptions, + meta: { + ...defaultOptions.meta, + ...savedOptions?.meta, + } + }; + + return initialState; +}; + +const mergeBuilderOptionsState = (prevState: QueryBuilderOptions, nextState: Partial): QueryBuilderOptions => { + return { + ...prevState, + ...nextState, + meta: { + ...prevState.meta, + ...nextState.meta + } + }; +} + +export const useBuilderOptionsState = (savedOptions: QueryBuilderOptions): [QueryBuilderOptions, React.Dispatch] => { + const [state, dispatch] = useReducer(reducer, savedOptions, buildInitialState); + return [state as QueryBuilderOptions, dispatch]; +}; + +export const testFuncs = { + reducer, + buildInitialState +}; diff --git a/src/hooks/useColumns.test.ts b/src/hooks/useColumns.test.ts new file mode 100644 index 00000000..ac2720f5 --- /dev/null +++ b/src/hooks/useColumns.test.ts @@ -0,0 +1,59 @@ +import { renderHook } from '@testing-library/react'; +import { act } from 'react-dom/test-utils'; +import { Datasource } from 'data/CHDatasource'; +import useColumns from './useColumns'; +import { TableColumn } from 'types/queryBuilder'; + +describe('useColumns', () => { + it('should return empty array if datasource is invalid', async () => { + let result: { current: readonly TableColumn[] }; + await act(async () => { + const r = renderHook(() => useColumns(undefined!, 'db', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should return empty array if database string is empty', async () => { + const mockDs = {} as Datasource; + mockDs.fetchColumnsFull = jest.fn((db: string, table: string) => Promise.resolve([])); + let result: { current: readonly TableColumn[] }; + await act(async () => { + const r = renderHook(() => useColumns(mockDs, '', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should return empty array if table string is empty', async () => { + const mockDs = {} as Datasource; + mockDs.fetchColumnsFull = jest.fn((db: string, table: string) => Promise.resolve([])); + let result: { current: readonly TableColumn[] }; + await act(async () => { + const r = renderHook(() => useColumns(mockDs, 'db', '')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should fetch table columns', async () => { + const mockDs = {} as Datasource; + mockDs.fetchColumnsFull = jest.fn( + (db: string, table: string) => Promise.resolve([ + { name: 'a', type: 'string', picklistValues: [] }, + { name: 'b', type: 'string', picklistValues: [] }, + // { name: '*' } (an "all" column is added by the hook) + ])); + + let result: { current: readonly TableColumn[] }; + await act(async () => { + const r = renderHook(() => useColumns(mockDs, 'db', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(2); + }); +}); diff --git a/src/hooks/useColumns.ts b/src/hooks/useColumns.ts new file mode 100644 index 00000000..cd60322b --- /dev/null +++ b/src/hooks/useColumns.ts @@ -0,0 +1,41 @@ +import { useState, useEffect, useRef } from 'react'; +import { TableColumn } from 'types/queryBuilder'; +import { Datasource } from 'data/CHDatasource'; + +export default (datasource: Datasource, database: string, table: string): readonly TableColumn[] => { + const [columns, setColumns] = useState([]); + + useEffect(() => { + if (!datasource || !database || !table) { + return; + } + + let ignore = false; + datasource + .fetchColumnsFull(database, table) + .then(columns => { + if (ignore) { + return; + } + setColumns(columns); + }) + .catch((ex: any) => { + console.error(ex); + }); + + return () => { + ignore = true; + }; + }, [datasource, database, table]); + + // Immediately return empty array on change so columns aren't stale + const lastDbTable = useRef(''); + const dbTable = database + table; + if (dbTable !== lastDbTable.current) { + lastDbTable.current = dbTable; + setColumns([]); + return []; + } + + return columns; +}; diff --git a/src/hooks/useDatabases.test.ts b/src/hooks/useDatabases.test.ts new file mode 100644 index 00000000..22c0168a --- /dev/null +++ b/src/hooks/useDatabases.test.ts @@ -0,0 +1,29 @@ +import { renderHook } from '@testing-library/react'; +import { act } from 'react-dom/test-utils'; +import { Datasource } from 'data/CHDatasource'; +import useDatabases from './useDatabases'; + +describe('useDatabases', () => { + it('should return empty array if invalid datasource is provided', async () => { + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useDatabases(undefined!)); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should fetch databases', async () => { + const mockDs = {} as Datasource; + mockDs.fetchDatabases = jest.fn(() => Promise.resolve(['a', 'b'])); + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useDatabases(mockDs)); + result = r.result; + }); + + expect(result!.current).toHaveLength(2); + }); +}); diff --git a/src/hooks/useDatabases.ts b/src/hooks/useDatabases.ts new file mode 100644 index 00000000..5d2af5c1 --- /dev/null +++ b/src/hooks/useDatabases.ts @@ -0,0 +1,21 @@ +import { useState, useEffect } from 'react'; +import { Datasource } from 'data/CHDatasource'; + +export default (datasource: Datasource): readonly string[] => { + const [databases, setDatabases] = useState([]); + + useEffect(() => { + if (!datasource) { + return; + } + + datasource. + fetchDatabases(). + then(databases => setDatabases(databases)). + catch((ex: any) => { + console.error(ex); + }); + }, [datasource]); + + return databases; +} diff --git a/src/hooks/useIsNewQuery.test.ts b/src/hooks/useIsNewQuery.test.ts new file mode 100644 index 00000000..e5319b84 --- /dev/null +++ b/src/hooks/useIsNewQuery.test.ts @@ -0,0 +1,50 @@ +import { renderHook } from "@testing-library/react"; +import useIsNewQuery from "./useIsNewQuery"; +import { QueryBuilderOptions, QueryType } from "types/queryBuilder"; + +describe('useIsNewQuery', () => { + const newQueryOpts: QueryBuilderOptions = { + database: 'default', + table: 'test', + queryType: QueryType.Table + }; + + const existingQueryOpts: QueryBuilderOptions = { + database: 'default', + table: 'test', + queryType: QueryType.Table, + columns: [ + { name: 'valid_column' } + ] + }; + + it('should return true when new query is provided', async () => { + const hook = renderHook(() => useIsNewQuery(newQueryOpts)); + expect(hook.result.current).toBe(true); + }); + + it('should return false when existing query is provided', async () => { + const hook = renderHook(() => useIsNewQuery(existingQueryOpts)); + expect(hook.result.current).toBe(false); + }); + + it('should continue to return true when new query is updated', async () => { + const hook = renderHook(opts => useIsNewQuery(opts), { initialProps: newQueryOpts }); + const firstResult = hook.result.current; + hook.rerender(existingQueryOpts); + const secondResult = hook.result.current; + + expect(firstResult).toBe(true); + expect(secondResult).toBe(true); + }); + + it('should continue to return false when existing query is updated', async () => { + const hook = renderHook(opts => useIsNewQuery(opts), { initialProps: existingQueryOpts }); + const firstResult = hook.result.current; + hook.rerender(existingQueryOpts); + const secondResult = hook.result.current; + + expect(firstResult).toBe(false); + expect(secondResult).toBe(false); + }); +}); diff --git a/src/hooks/useIsNewQuery.ts b/src/hooks/useIsNewQuery.ts new file mode 100644 index 00000000..67425f45 --- /dev/null +++ b/src/hooks/useIsNewQuery.ts @@ -0,0 +1,14 @@ +import { isBuilderOptionsRunnable } from "data/utils"; +import { useRef } from "react" +import { QueryBuilderOptions } from "types/queryBuilder"; + +/** + * Returns true if the initial builderOptions represent a new query. + * Returns false if the query was loaded from a saved URL or dashboard. + * + * Does not update on re-renders + */ +export default (builderOptions: QueryBuilderOptions): boolean => { + const isNewQuery = useRef(!isBuilderOptionsRunnable(builderOptions)); + return isNewQuery.current; +} diff --git a/src/hooks/useTables.test.ts b/src/hooks/useTables.test.ts new file mode 100644 index 00000000..f3fc4665 --- /dev/null +++ b/src/hooks/useTables.test.ts @@ -0,0 +1,42 @@ +import { renderHook } from '@testing-library/react'; +import { act } from 'react-dom/test-utils'; +import { Datasource } from 'data/CHDatasource'; +import useTables from './useTables'; + +describe('useTables', () => { + it('should return empty array if invalid datasource is provided', async () => { + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useTables(undefined!, 'db')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should return empty array if empty database string is provided', async () => { + const mockDs = {} as Datasource; + mockDs.fetchTables = jest.fn((db: string) => Promise.resolve(['a', 'b'])); + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useTables(mockDs, '')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should fetch tables', async () => { + const mockDs = {} as Datasource; + mockDs.fetchTables = jest.fn((db: string) => Promise.resolve(['a', 'b'])); + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useTables(mockDs, 'db')); + result = r.result; + }); + + expect(result!.current).toHaveLength(2); + }); +}); diff --git a/src/hooks/useTables.ts b/src/hooks/useTables.ts new file mode 100644 index 00000000..cfdbd778 --- /dev/null +++ b/src/hooks/useTables.ts @@ -0,0 +1,39 @@ +import { useState, useEffect, useRef } from 'react'; +import { Datasource } from 'data/CHDatasource'; + +export default (datasource: Datasource, database: string): readonly string[] => { + const [tables, setTables] = useState([]); + + useEffect(() => { + if (!datasource || !database) { + return; + } + + let ignore = false; + datasource. + fetchTables(database). + then(tables => { + if (ignore) { + return; + } + setTables(tables); + }). + catch((ex: any) => { + console.error(ex); + }); + + return () => { + ignore = true; + }; + }, [datasource, database]); + + // Immediately return empty array on change so tables aren't stale + const lastDatabase = useRef(''); + if (database !== lastDatabase.current) { + lastDatabase.current = database; + setTables([]); + return []; + } + + return tables; +} diff --git a/src/labels.ts b/src/labels.ts new file mode 100644 index 00000000..7e891655 --- /dev/null +++ b/src/labels.ts @@ -0,0 +1,298 @@ +export default { + components: { + Config: { + DefaultDatabaseTableConfig: { + title: 'Default DB and table', + database: { + label: 'Default database', + description: 'the default database used by the query builder', + name: 'defaultDatabase', + placeholder: 'default' + }, + table: { + label: 'Default table', + description: 'the default table used by the query builder', + name: 'defaultTable', + placeholder: 'table' + }, + }, + QuerySettingsConfig: { + title: 'Query settings', + dialTimeout: { + label: 'Dial Timeout (seconds)', + tooltip: 'Timeout in seconds for connection', + name: 'dialTimeout', + placeholder: '10', + }, + queryTimeout: { + label: 'Query Timeout (seconds)', + tooltip: 'Timeout in seconds for read queries', + name: 'queryTimeout', + placeholder: '60', + }, + validateSql: { + label: 'Validate SQL', + tooltip: 'Validate SQL in the editor.', + } + }, + TracesConfig: { + title: 'Traces configuration', + description: '(Optional) Default settings for trace queries', + defaultDatabase: { + label: 'Default trace database', + description: 'the default database used by the trace query builder', + name: 'defaultDatabase', + placeholder: 'default' + }, + defaultTable: { + label: 'Default trace table', + description: 'the default table used by the trace query builder', + name: 'defaultTable', + placeholder: 'traces' + }, + columns: { + title: 'Default columns', + description: 'Default columns for trace queries. Leave empty to disable.', + + traceId: { + label: 'Trace ID column', + tooltip: 'Column for the trace ID' + }, + spanId: { + label: 'Span ID column', + tooltip: 'Column for the span ID' + }, + parentSpanId: { + label: 'Parent Span ID column', + tooltip: 'Column for the parent span ID' + }, + serviceName: { + label: 'Service Name column', + tooltip: 'Column for the service name' + }, + operationName: { + label: 'Operation Name column', + tooltip: 'Column for the operation name' + }, + startTime: { + label: 'Start Time column', + tooltip: 'Column for the start time' + }, + durationTime: { + label: 'Duration Time column', + tooltip: 'Column for the duration time' + }, + tags: { + label: 'Tags column', + tooltip: 'Column for the trace tags' + }, + serviceTags: { + label: 'Service Tags column', + tooltip: 'Column for the service tags' + } + } + }, + LogsConfig: { + title: 'Logs configuration', + description: '(Optional) default settings for log queries', + defaultDatabase: { + label: 'Default log database', + description: 'the default database used by the logs query builder', + name: 'defaultDatabase', + placeholder: 'default' + }, + defaultTable: { + label: 'Default log table', + description: 'the default table used by the logs query builder', + name: 'defaultTable', + placeholder: 'logs' + }, + columns: { + title: 'Default columns', + description: 'Default columns for log queries. Leave empty to disable.', + + time: { + label: 'Time column', + tooltip: 'Column for the log timestamp' + }, + level: { + label: 'Log Level column', + tooltip: 'Column for the log level' + }, + message: { + label: 'Log Message column', + tooltip: 'Column for log message' + } + } + } + }, + EditorTypeSwitcher: { + label: 'Editor Type', + tooltip: 'Switches between the raw SQL Editor and the Query Builder.', + switcher: { + title: 'Are you sure?', + body: 'Queries that are too complex for the Query Builder will be altered.', + confirmText: 'Continue', + dismissText: 'Cancel', + }, + cannotConvert: { + title: 'Cannot convert', + message: 'Do you want to delete your current query and use the query builder?', + confirmText: 'Yes', + }, + }, + QueryTypeSwitcher: { + label: 'Query Type', + tooltip: 'Sets the layout for the query builder', + sqlTooltip: 'Sets the panel type for explore view' + }, + DatabaseSelect: { + label: 'Database', + tooltip: 'ClickHouse database to query from', + empty: '', + }, + ColumnsEditor: { + label: 'Columns', + tooltip: 'A list of columns to include in the query' + }, + OtelVersionSelect: { + label: 'Use OTEL', + tooltip: 'Enables Open Telemetry schema versioning' + }, + LimitEditor: { + label: 'Limit', + tooltip: 'Limits the number of rows returned by the query' + }, + SqlPreview: { + label: 'SQL Preview', + tooltip: 'Preview of the generated SQL. You can safely switch to SQL Editor to customize the generated query', + }, + AggregatesEditor: { + label: 'Aggregates', + tooltip: 'Aggregate functions to use', + aliasLabel: 'as', + aliasTooltip: 'alias for this aggregate function', + addLabel: 'Aggregate', + }, + OrderByEditor: { + label: 'Order By', + tooltip: 'Order by column', + addLabel: 'Order By', + }, + FilterEditor: { + label: 'Filters', + tooltip: `List of filters`, + addLabel: 'Filter', + }, + GroupByEditor: { + label: 'Group By', + tooltip: 'Group the results by specific column', + }, + LogsQueryBuilder: { + logTimeColumn: { + label: 'Time', + tooltip: 'Column that contains the log timestamp' + }, + logLevelColumn: { + label: 'Log Level', + tooltip: 'Column that contains the log level' + }, + logMessageColumn: { + label: 'Message', + tooltip: 'Column that contains the log message' + }, + liveView: { + label: 'Live View', + tooltip: 'Enable to update logs in real time' + }, + }, + TimeSeriesQueryBuilder: { + simpleQueryModeLabel: 'Simple', + aggregateQueryModeLabel: 'Aggregate', + builderModeLabel: 'Builder Mode', + builderModeTooltip: 'Switches the query builder between the simple and aggregate modes', + timeColumn: { + label: 'Time', + tooltip: 'Column to use for the time series' + }, + }, + TableQueryBuilder: { + simpleQueryModeLabel: 'Simple', + aggregateQueryModeLabel: 'Aggregate', + builderModeLabel: 'Builder Mode', + builderModeTooltip: 'Switches the query builder between the simple and aggregate modes', + }, + TraceQueryBuilder: { + traceIdModeLabel: 'Trace ID', + traceSearchModeLabel: 'Trace Search', + traceModeLabel: 'Trace Mode', + traceModeTooltip: 'Switches between trace ID and trace search mode', + columnsSection: 'Columns', + filtersSection: 'Filters', + + columns: { + traceId: { + label: 'Trace ID Column', + tooltip: 'Column that contains the trace ID' + }, + spanId: { + label: 'Span ID Column', + tooltip: 'Column that contains the span ID' + }, + parentSpanId: { + label: 'Parent Span ID Column', + tooltip: 'Column that contains the parent span ID' + }, + serviceName: { + label: 'Service Name Column', + tooltip: 'Column that contains the service name' + }, + operationName: { + label: 'Operation Name Column', + tooltip: 'Column that contains the operation name' + }, + startTime: { + label: 'Start Time Column', + tooltip: 'Column that contains the start time' + }, + durationTime: { + label: 'Duration Time Column', + tooltip: 'Column that contains the duration time' + }, + durationUnit: { + label: 'Duration Unit', + tooltip: 'The unit of time used for the duration time' + }, + tags: { + label: 'Tags Column', + tooltip: 'Column that contains the trace tags' + }, + serviceTags: { + label: 'Service Tags Column', + tooltip: 'Column that contains the service tags' + }, + traceIdFilter: { + label: 'Trace ID', + tooltip: 'filter by a specific trace ID' + } + }, + } + }, + types: { + EditorType: { + sql: 'SQL Editor', + builder: 'Query Builder', + }, + QueryType: { + table: 'Table', + logs: 'Logs', + timeseries: 'Time Series', + traces: 'Traces', + } + } +} diff --git a/src/module.ts b/src/module.ts index 223e156f..61872949 100644 --- a/src/module.ts +++ b/src/module.ts @@ -2,7 +2,8 @@ import { DataSourcePlugin, DashboardLoadedEvent } from '@grafana/data'; import { Datasource } from './data/CHDatasource'; import { ConfigEditor } from './views/CHConfigEditor'; import { CHQueryEditor } from './views/CHQueryEditor'; -import { CHQuery, CHConfig } from './types'; +import { CHConfig } from 'types/config'; +import { CHQuery } from 'types/sql'; import { getAppEvents } from '@grafana/runtime'; import { analyzeQueries, trackClickhouseDashboardLoaded } from 'tracking'; import pluginJson from './plugin.json'; diff --git a/src/otel.ts b/src/otel.ts new file mode 100644 index 00000000..a8f45035 --- /dev/null +++ b/src/otel.ts @@ -0,0 +1,33 @@ +import { ColumnHint, TimeUnit } from "types/queryBuilder"; + +export interface OtelVersion { + name: string; + version: string; + logColumnMap: Map; + traceColumnMap: Map; + traceDurationUnit: TimeUnit.Nanoseconds; +} + +export const versions: readonly OtelVersion[] = [ + { + name: 'latest', + version: '1.26.0', + logColumnMap: new Map([ + [ColumnHint.Time, 'Timestamp'], + [ColumnHint.LogMessage, 'Body'], + [ColumnHint.LogLevel, 'SeverityText'], + ]), + traceColumnMap: new Map([ + [ColumnHint.Time, 'Timestamp'], + [ColumnHint.TraceId, 'TraceId'], + [ColumnHint.TraceSpanId, 'SpanId'], + [ColumnHint.TraceParentSpanId, 'ParentSpanId'], + [ColumnHint.TraceServiceName, 'ServiceName'], + [ColumnHint.TraceOperationName, 'SpanName'], + [ColumnHint.TraceDurationTime, 'Duration'], + [ColumnHint.TraceTags, 'SpanAttributes'], + [ColumnHint.TraceServiceTags, 'ResourceAttributes'], + ]), + traceDurationUnit: TimeUnit.Nanoseconds, + }, +]; diff --git a/src/plugin.json b/src/plugin.json index 64e38c07..9add3cde 100644 --- a/src/plugin.json +++ b/src/plugin.json @@ -6,6 +6,7 @@ "metrics": true, "backend": true, "logs": true, + "tracing": true, "alerting": true, "annotations": true, "executable": "gpx_clickhouse", diff --git a/src/selectors.ts b/src/selectors.ts index fb612de3..3f522401 100644 --- a/src/selectors.ts +++ b/src/selectors.ts @@ -54,29 +54,10 @@ export const Components = { label: 'Client Key', placeholder: 'Client Key. Begins with -----BEGIN RSA PRIVATE KEY-----', }, - DefaultDatabase: { - label: 'Default database', - placeholder: 'Default database', - tooltip: 'Default database to be used. Can be empty.', - }, - Timeout: { - label: 'Dial Timeout (seconds)', - placeholder: '10', - tooltip: 'Timeout in seconds for connection', - }, - QueryTimeout: { - label: 'Query Timeout (seconds)', - placeholder: '60', - tooltip: 'Timeout in seconds for read queries', - }, Secure: { label: 'Secure Connection', tooltip: 'Toggle on if the connection is secure', }, - Validate: { - label: 'Validate SQL', - tooltip: 'Validate Sql in the editor.', - }, SecureSocksProxy: { label: 'Enable Secure Socks Proxy', tooltip: 'Enable proxying the datasource connection through the secure socks proxy to a different network.', @@ -195,6 +176,23 @@ export const Components = { }, }, }, + QueryBuilder: { + AggregateEditor: { + sectionLabel: 'query-builder__aggregate-editor__section-label', + itemWrapper: 'query-builder__aggregate-editor__item-wrapper', + itemRemoveButton: 'query-builder__aggregate-editor-item-remove-button', + addButton: 'query-builder__aggregate-editor__add-button', + }, + ColumnsEditor: { + multiSelectWrapper: 'query-builder__columns-editor__multi-select-wrapper' + }, + GroupByEditor: { + multiSelectWrapper: 'query-builder__group-by__multi-select-wrapper' + }, + LimitEditor: { + input: 'query-builder__limit-editor__input' + } + } }; export const selectors: { components: E2ESelectors } = { components: Components, diff --git a/src/styles.ts b/src/styles.ts index 91f455ab..16a0f59c 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -2,6 +2,9 @@ import { css } from '@emotion/css'; export const styles = { Common: { + check: css` + margin-top: 5px; + `, wrapper: css` position: relative; width: 100%; @@ -10,9 +13,15 @@ export const styles = { margin-top: 5px; margin-inline: 5px; `, + selectWrapper: css` + width: 100%; + `, inlineSelect: css` margin-right: 5px; `, + firstLabel: css` + margin-right: 5px; + `, expand: css` position: absolute; top: 2px; @@ -21,4 +30,39 @@ export const styles = { color: gray; `, }, + ConfigEditor: { + container: css` + justify-content: space-between; + h5 { + line-height: 34px; + margin-bottom: 5px; + } + button { + margin-right: 5px; + } + `, + wide: css` + width: 75%; + `, + subHeader: css` + padding: 5px 0 5px 0; + `, + }, + QueryEditor: { + queryType: css` + justify-content: space-between; + span { + display: flex; + } + `, + inlineField: css` + margin-left: 7px; + ` + }, + FormatSelector: { + formatSelector: css` + display: flex; + `, + }, + VariablesEditor: {}, }; diff --git a/src/tracking.test.ts b/src/tracking.test.ts index 84027717..30068fa2 100644 --- a/src/tracking.test.ts +++ b/src/tracking.test.ts @@ -1,30 +1,30 @@ import { analyzeQueries } from 'tracking'; -import { BuilderMode, QueryType, CHQuery, Format } from 'types'; +import { CHQuery, EditorType } from 'types/sql'; +import { QueryType, BuilderMode } from 'types/queryBuilder'; describe('analyzeQueries', () => { [ - { - description: 'should count 1 sql query (with a default mode of auto)', - queries: [{ queryType: QueryType.SQL, selectedFormat: Format.AUTO }], - expectedCounters: { - sql_queries: 1, - sql_query_format_auto: 1, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, - builder_queries: 0, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, - }, - }, + // { + // description: 'should count 1 sql query (with a default mode of auto)', + // queries: [{ editorType: EditorType.SQL, QueryType: QueryType.Table }], + // expectedCounters: { + // sql_queries: 1, + // sql_query_format_auto: 1, + // sql_query_format_table: 0, + // sql_query_format_logs: 0, + // sql_query_format_time_series: 0, + // sql_query_format_trace: 0, + // builder_queries: 0, + // builder_table_queries: 0, + // builder_aggregate_queries: 0, + // builder_time_series_queries: 0, + // }, + // }, { description: 'should count 1 sql query with a mode of Table', - queries: [{ queryType: QueryType.SQL, selectedFormat: Format.TABLE }], + queries: [{ editorType: EditorType.SQL, queryType: QueryType.Table }], expectedCounters: { sql_queries: 1, - sql_query_format_auto: 0, sql_query_format_table: 1, sql_query_format_logs: 0, sql_query_format_time_series: 0, @@ -37,10 +37,9 @@ describe('analyzeQueries', () => { }, { description: 'should count 1 sql query with a mode of Logs', - queries: [{ queryType: QueryType.SQL, selectedFormat: Format.LOGS }], + queries: [{ editorType: EditorType.SQL, queryType: QueryType.Logs }], expectedCounters: { sql_queries: 1, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 1, sql_query_format_time_series: 0, @@ -53,10 +52,9 @@ describe('analyzeQueries', () => { }, { description: 'should count 1 sql query with a mode of Time Series', - queries: [{ queryType: QueryType.SQL, selectedFormat: Format.TIMESERIES }], + queries: [{ editorType: EditorType.SQL, queryType: QueryType.TimeSeries }], expectedCounters: { sql_queries: 1, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 0, sql_query_format_time_series: 1, @@ -68,11 +66,10 @@ describe('analyzeQueries', () => { }, }, { - description: 'should count 1 sql query with a mode of Trace', - queries: [{ queryType: QueryType.SQL, selectedFormat: Format.TRACE }], + description: 'should count 1 sql query with a mode of Traces', + queries: [{ editorType: EditorType.SQL, queryType: QueryType.Traces }], expectedCounters: { sql_queries: 1, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 0, sql_query_format_time_series: 0, @@ -85,10 +82,9 @@ describe('analyzeQueries', () => { }, { description: 'should count 1 builder query (with a default mode of Table)', - queries: [{ queryType: QueryType.Builder, builderOptions: { mode: BuilderMode.List } }], + queries: [{ editorType: EditorType.Builder, builderOptions: { mode: BuilderMode.List } }], expectedCounters: { sql_queries: 0, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 0, sql_query_format_time_series: 0, @@ -101,10 +97,9 @@ describe('analyzeQueries', () => { }, { description: 'should count 1 builder query with a mode of Aggregate', - queries: [{ queryType: QueryType.Builder, builderOptions: { mode: BuilderMode.Aggregate } }], + queries: [{ editorType: EditorType.Builder, builderOptions: { mode: BuilderMode.Aggregate } }], expectedCounters: { sql_queries: 0, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 0, sql_query_format_time_series: 0, @@ -117,10 +112,9 @@ describe('analyzeQueries', () => { }, { description: 'should count 1 builder query with a mode of Time Series', - queries: [{ queryType: QueryType.Builder, builderOptions: { mode: BuilderMode.Trend } }], + queries: [{ editorType: EditorType.Builder, builderOptions: { mode: BuilderMode.Trend } }], expectedCounters: { sql_queries: 0, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 0, sql_query_format_time_series: 0, diff --git a/src/tracking.ts b/src/tracking.ts index aa4083a3..a62a167c 100644 --- a/src/tracking.ts +++ b/src/tracking.ts @@ -1,5 +1,8 @@ import { reportInteraction } from '@grafana/runtime'; -import { BuilderMode, CHQuery, Format, QueryType } from 'types'; +import { CHQuery, EditorType } from 'types/sql'; +import { QueryType, BuilderMode } from 'types/queryBuilder'; + +// TODO: v4, determine new/updated fields to track export const trackClickhouseDashboardLoaded = (props: ClickhouseDashboardLoadedProps) => { reportInteraction('grafana_ds_clickhouse_dashboard_loaded', props); @@ -7,7 +10,6 @@ export const trackClickhouseDashboardLoaded = (props: ClickhouseDashboardLoadedP export type ClickhouseCounters = { sql_queries: number; - sql_query_format_auto: number; sql_query_format_table: number; sql_query_format_logs: number; sql_query_format_time_series: number; @@ -29,7 +31,6 @@ export interface ClickhouseDashboardLoadedProps extends ClickhouseCounters { export const analyzeQueries = (queries: CHQuery[]): ClickhouseCounters => { const counters = { sql_queries: 0, - sql_query_format_auto: 0, sql_query_format_table: 0, sql_query_format_logs: 0, sql_query_format_time_series: 0, @@ -40,23 +41,21 @@ export const analyzeQueries = (queries: CHQuery[]): ClickhouseCounters => { builder_time_series_queries: 0, }; - queries.forEach((query) => { - switch (query.queryType) { - case QueryType.SQL: + queries.forEach(query => { + switch (query.editorType) { + case EditorType.SQL: counters.sql_queries++; - if (query.selectedFormat === Format.AUTO) { - counters.sql_query_format_auto++; - } else if (query.selectedFormat === Format.TABLE) { + if (query.queryType === QueryType.Table) { counters.sql_query_format_table++; - } else if (query.selectedFormat === Format.LOGS) { + } else if (query.queryType === QueryType.Logs) { counters.sql_query_format_logs++; - } else if (query.selectedFormat === Format.TIMESERIES) { + } else if (query.queryType === QueryType.TimeSeries) { counters.sql_query_format_time_series++; - } else if (query.selectedFormat === Format.TRACE) { + } else if (query.queryType === QueryType.Traces) { counters.sql_query_format_trace++; } break; - case QueryType.Builder: + case EditorType.Builder: counters.builder_queries++; if (query.builderOptions.mode === BuilderMode.Aggregate) { counters.builder_aggregate_queries++; diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 0e40631c..00000000 --- a/src/types.ts +++ /dev/null @@ -1,272 +0,0 @@ -import { DataQuery, DataSourceJsonData } from '@grafana/data'; - -export const defaultQuery: Partial = {}; - -export interface CHConfig extends DataSourceJsonData { - username: string; - server: string; - protocol: Protocol; - port: number; - path: string; - defaultDatabase?: string; - tlsSkipVerify?: boolean; - tlsAuth?: boolean; - tlsAuthWithCACert?: boolean; - secure?: boolean; - validate?: boolean; - timeout?: string; - queryTimeout?: string; - customSettings?: CHCustomSetting[]; - enableSecureSocksProxy?: boolean; -} - -export interface CHCustomSetting { - setting: string; - value: string; -} - -export interface CHSecureConfig { - password: string; - tlsCACert?: string; - tlsClientCert?: string; - tlsClientKey?: string; -} - -export enum Protocol { - NATIVE = 'native', - HTTP = 'http', -} - -export enum Format { - TIMESERIES = 0, - TABLE = 1, - LOGS = 2, - TRACE = 3, - AUTO = 4, -} - -//#region Query -export enum QueryType { - SQL = 'sql', - Builder = 'builder', -} - -export interface CHQueryBase extends DataQuery {} - -export interface CHSQLQuery extends CHQueryBase { - queryType: QueryType.SQL; - rawSql: string; - meta?: { - timezone?: string; - // meta fields to be used just for building builder options when migrating back to QueryType.Builder - builderOptions?: SqlBuilderOptions; - }; - format: Format; - selectedFormat: Format; - expand?: boolean; -} - -export interface CHBuilderQuery extends CHQueryBase { - queryType: QueryType.Builder; - rawSql: string; - builderOptions: SqlBuilderOptions; - format: Format; - selectedFormat: Format; - meta?: { - timezone?: string; - }; -} - -export type CHQuery = CHSQLQuery | CHBuilderQuery; - -export enum BuilderMode { - List = 'list', - Aggregate = 'aggregate', - Trend = 'trend', -} - -/** - * @property {string} timeField Explore only: used for Logs Volume histogram - * @property {string} logLevelField Explore only: used for Logs Volume histogram - */ -export interface SqlBuilderOptionsList { - mode: BuilderMode.List; - database?: string; - table?: string; - fields?: string[]; - filters?: Filter[]; - orderBy?: OrderBy[]; - limit?: number; - timeField?: string; - logLevelField?: string; -} -export enum BuilderMetricFieldAggregation { - Sum = 'sum', - Average = 'avg', - Min = 'min', - Max = 'max', - Count = 'count', - Any = 'any', - // Count_Distinct = 'count_distinct', -} -export type BuilderMetricField = { - field: string; - aggregation: BuilderMetricFieldAggregation; - alias?: string; -}; -export interface SqlBuilderOptionsAggregate { - mode: BuilderMode.Aggregate; - database: string; - table: string; - fields: string[]; - metrics: BuilderMetricField[]; - groupBy?: string[]; - filters?: Filter[]; - orderBy?: OrderBy[]; - limit?: number; -} -export interface SqlBuilderOptionsTrend { - mode: BuilderMode.Trend; - database: string; - table: string; - fields: string[]; - metrics: BuilderMetricField[]; - filters?: Filter[]; - groupBy?: string[]; - timeField: string; - timeFieldType: string; - orderBy?: OrderBy[]; - limit?: number; -} - -export type SqlBuilderOptions = SqlBuilderOptionsList | SqlBuilderOptionsAggregate | SqlBuilderOptionsTrend; -export interface Field { - name: string; - type: string; - rel: string; - label: string; - ref: string[]; -} -export interface FullEntity { - name: string; - label: string; - custom: boolean; - queryable: boolean; -} -interface FullFieldPickListItem { - value: string; - label: string; -} -export interface FullField { - name: string; - label: string; - type: string; - picklistValues: FullFieldPickListItem[]; - filterable?: boolean; - sortable?: boolean; - groupable?: boolean; - aggregatable?: boolean; -} -export enum OrderByDirection { - ASC = 'ASC', - DESC = 'DESC', -} - -export interface OrderBy { - name: string; - dir: OrderByDirection; -} - -export enum FilterOperator { - IsNull = 'IS NULL', - IsNotNull = 'IS NOT NULL', - Equals = '=', - NotEquals = '!=', - LessThan = '<', - LessThanOrEqual = '<=', - GreaterThan = '>', - GreaterThanOrEqual = '>=', - Like = 'LIKE', - NotLike = 'NOT LIKE', - In = 'IN', - NotIn = 'NOT IN', - WithInGrafanaTimeRange = 'WITH IN DASHBOARD TIME RANGE', - OutsideGrafanaTimeRange = 'OUTSIDE DASHBOARD TIME RANGE', -} -export interface CommonFilterProps { - filterType: 'custom'; - key: string; - type: string; - condition: 'AND' | 'OR'; -} -export interface NullFilter extends CommonFilterProps { - operator: FilterOperator.IsNull | FilterOperator.IsNotNull; -} -export interface BooleanFilter extends CommonFilterProps { - type: 'boolean'; - operator: FilterOperator.Equals | FilterOperator.NotEquals; - value: boolean; -} -export interface StringFilter extends CommonFilterProps { - operator: FilterOperator.Equals | FilterOperator.NotEquals | FilterOperator.Like | FilterOperator.NotLike; - value: string; -} - -export interface NumberFilter extends CommonFilterProps { - operator: - | FilterOperator.Equals - | FilterOperator.NotEquals - | FilterOperator.LessThan - | FilterOperator.LessThanOrEqual - | FilterOperator.GreaterThan - | FilterOperator.GreaterThanOrEqual; - value: number; -} - -export interface DateFilterWithValue extends CommonFilterProps { - type: 'datetime' | 'date'; - operator: - | FilterOperator.Equals - | FilterOperator.NotEquals - | FilterOperator.LessThan - | FilterOperator.LessThanOrEqual - | FilterOperator.GreaterThan - | FilterOperator.GreaterThanOrEqual; - value: string; -} -export interface DateFilterWithoutValue extends CommonFilterProps { - type: 'datetime' | 'date'; - operator: FilterOperator.WithInGrafanaTimeRange | FilterOperator.OutsideGrafanaTimeRange; -} -export type DateFilter = DateFilterWithValue | DateFilterWithoutValue; - -export interface MultiFilter extends CommonFilterProps { - operator: FilterOperator.In | FilterOperator.NotIn; - value: string[]; -} - -export type Filter = NullFilter | BooleanFilter | NumberFilter | DateFilter | StringFilter | MultiFilter; - -//#endregion - -//#region Default Queries -export const defaultQueryType: QueryType = QueryType.Builder; -export const defaultCHBuilderQuery: Omit = { - queryType: QueryType.Builder, - rawSql: '', - builderOptions: { - mode: BuilderMode.List, - fields: [], - limit: 100, - }, - format: Format.TABLE, - selectedFormat: Format.AUTO, -}; -export const defaultCHSQLQuery: Omit = { - queryType: QueryType.SQL, - rawSql: '', - format: Format.TABLE, - selectedFormat: Format.AUTO, - expand: false, -}; -//#endregion diff --git a/src/types/config.ts b/src/types/config.ts new file mode 100644 index 00000000..9ed7ee84 --- /dev/null +++ b/src/types/config.ts @@ -0,0 +1,76 @@ +import { DataSourceJsonData } from '@grafana/data'; + +export interface CHConfig extends DataSourceJsonData { + host: string; + port: number; + protocol: Protocol; + secure?: boolean; + path?: string; + + tlsSkipVerify?: boolean; + tlsAuth?: boolean; + tlsAuthWithCACert?: boolean; + + username: string; + + defaultDatabase?: string; + defaultTable?: string; + + dialTimeout?: string; + queryTimeout?: string; + validateSql?: boolean; + + logs?: CHLogsConfig; + traces?: CHTracesConfig; + + customSettings?: CHCustomSetting[]; + enableSecureSocksProxy?: boolean; +} + +export interface CHCustomSetting { + setting: string; + value: string; +} + +export interface CHSecureConfig { + password: string; + tlsCACert?: string; + tlsClientCert?: string; + tlsClientKey?: string; +} + +export interface CHLogsConfig { + defaultDatabase?: string; + defaultTable?: string; + + otelEnabled?: boolean; + otelVersion?: string; + + timeColumn?: string; + levelColumn?: string; + messageColumn?: string; +} + +export interface CHTracesConfig { + defaultDatabase?: string; + defaultTable?: string; + + otelEnabled?: boolean; + otelVersion?: string; + + traceIdColumn?: string; + spanIdColumn?: string; + operationNameColumn?: string; + parentSpanIdColumn?: string; + serviceNameColumn?: string; + durationColumn?: string; + durationUnit?: string; + startTimeColumn?: string; + tagsColumn?: string; + serviceTagsColumn?: string; +} + +export enum Protocol { + Native = 'native', + Http = 'http', +} diff --git a/src/types/queryBuilder.ts b/src/types/queryBuilder.ts new file mode 100644 index 00000000..1b831376 --- /dev/null +++ b/src/types/queryBuilder.ts @@ -0,0 +1,241 @@ +export interface FieldLabel { + label: string; + tooltip: string; +} + +export enum BuilderMode { + List = 'list', + Aggregate = 'aggregate', + Trend = 'trend', +} + +/** + * QueryType determines the display/query format. + */ +export enum QueryType { + Table = 'table', + Logs = 'logs', + TimeSeries = 'timeseries', + Traces = 'traces', +} + +export interface QueryBuilderOptions { + database: string; + table: string; + queryType: QueryType; + + mode?: BuilderMode; // TODO: no longer required? + + columns?: SelectedColumn[]; + aggregates?: AggregateColumn[]; + filters?: Filter[]; + groupBy?: string[]; + orderBy?: OrderBy[]; + limit?: number; + + /** + * Contains metadata for editor-specific use cases. + */ + meta?: { + // Logs + liveView?: boolean; + + // Trace + isTraceSearchMode?: boolean; + traceDurationUnit?: TimeUnit; + traceId?: string; + + // Logs & Traces + otelEnabled?: boolean; + otelVersion?: string; + } +} + +export enum AggregateType { + Sum = 'sum', + Average = 'avg', + Min = 'min', + Max = 'max', + Count = 'count', + Any = 'any', + // Count_Distinct = 'count_distinct', +} + +export type AggregateColumn = { + aggregateType: AggregateType; + column: string; + alias?: string; +} + +export interface Field { + name: string; + type: string; + rel: string; + label: string; + ref: string[]; +} + +export interface FullEntity { + name: string; + label: string; + custom: boolean; + queryable: boolean; +} + +interface TableColumnPickListItem { + label: string; + value: string; +} + +/** + * Represents a column retrieved from ClickHouse + */ +export interface TableColumn { + name: string; + type: string; + picklistValues: TableColumnPickListItem[]; + filterable?: boolean; + sortable?: boolean; + groupable?: boolean; + aggregatable?: boolean; +} + +/** + * Some columns are used to enable certain features. + * This enum defines the different use cases that a column may be used for in the query generator. + * For example, "Time" would be used to identify the primary time column for a time series. + */ +export enum ColumnHint { + Time = 'time', + + LogLevel = 'log_level', + LogMessage = 'log_message', + + TraceId = 'trace_id', + TraceSpanId = 'trace_span_id', + TraceParentSpanId = 'trace_parent_span_id', + TraceServiceName = 'trace_service_name', + TraceOperationName = 'trace_operation_name', + TraceDurationTime = 'trace_duration_time', + TraceTags = 'trace_tags', + TraceServiceTags = 'trace_service_tags', +} + +/** + * TimeUnit determines a unit of time. + */ +export enum TimeUnit { + Seconds = 'seconds', + Milliseconds = 'milliseconds', + Microseconds = 'microseconds', + Nanoseconds = 'nanoseconds', +} + +/** + * Represents a column selection, including metadata for the query generator to use. + */ +export interface SelectedColumn { + name: string; + type?: string; + alias?: string; + custom?: boolean; + hint?: ColumnHint; +} + +export enum OrderByDirection { + ASC = 'ASC', + DESC = 'DESC', +} + +export interface OrderBy { + name: string; + dir: OrderByDirection; + default?: boolean; +} + +export enum FilterOperator { + IsNull = 'IS NULL', + IsNotNull = 'IS NOT NULL', + Equals = '=', + NotEquals = '!=', + LessThan = '<', + LessThanOrEqual = '<=', + GreaterThan = '>', + GreaterThanOrEqual = '>=', + Like = 'LIKE', + NotLike = 'NOT LIKE', + In = 'IN', + NotIn = 'NOT IN', + WithInGrafanaTimeRange = 'WITH IN DASHBOARD TIME RANGE', + OutsideGrafanaTimeRange = 'OUTSIDE DASHBOARD TIME RANGE', +} + +export interface CommonFilterProps { + filterType: 'custom'; + key: string; // Column name + type: string; + condition: 'AND' | 'OR'; + + /** + * Used to uniquely identify a dynamically added filter + * For example, might be set to 'timeRange' for the default added time range filter. + */ + id?: string; + /** + * If provided, SQL generator will ignore "key" and instead + * find the intended column by the hint + */ + hint?: ColumnHint; +} + +export interface NullFilter extends CommonFilterProps { + operator: FilterOperator.IsNull | FilterOperator.IsNotNull; +} + +export interface BooleanFilter extends CommonFilterProps { + type: 'boolean'; + operator: FilterOperator.Equals | FilterOperator.NotEquals; + value: boolean; +} + +export interface StringFilter extends CommonFilterProps { + operator: FilterOperator.Equals | FilterOperator.NotEquals | FilterOperator.Like | FilterOperator.NotLike; + value: string; +} + +export interface NumberFilter extends CommonFilterProps { + operator: + | FilterOperator.Equals + | FilterOperator.NotEquals + | FilterOperator.LessThan + | FilterOperator.LessThanOrEqual + | FilterOperator.GreaterThan + | FilterOperator.GreaterThanOrEqual; + value: number; +} + +export interface DateFilterWithValue extends CommonFilterProps { + type: 'datetime' | 'date'; + operator: + | FilterOperator.Equals + | FilterOperator.NotEquals + | FilterOperator.LessThan + | FilterOperator.LessThanOrEqual + | FilterOperator.GreaterThan + | FilterOperator.GreaterThanOrEqual; + value: string; +} + +export interface DateFilterWithoutValue extends CommonFilterProps { + type: 'datetime' | 'date'; + operator: FilterOperator.WithInGrafanaTimeRange | FilterOperator.OutsideGrafanaTimeRange; +} + +export type DateFilter = DateFilterWithValue | DateFilterWithoutValue; + +export interface MultiFilter extends CommonFilterProps { + operator: FilterOperator.In | FilterOperator.NotIn; + value: string[]; +} + +export type Filter = NullFilter | BooleanFilter | NumberFilter | DateFilter | StringFilter | MultiFilter; diff --git a/src/types/sql.ts b/src/types/sql.ts new file mode 100644 index 00000000..f10ae3da --- /dev/null +++ b/src/types/sql.ts @@ -0,0 +1,67 @@ +import { DataQuery } from '@grafana/schema'; +import { BuilderMode, QueryType, QueryBuilderOptions } from './queryBuilder'; + +/** + * EditorType determines the query editor type. + */ +export enum EditorType { + SQL = 'sql', + Builder = 'builder', +} + +export interface CHQueryBase extends DataQuery { + pluginVersion: string, + editorType: EditorType; + rawSql: string; + + /** + * REQUIRED by backend for auto selecting preferredVisualizationType. + * Only used in explore view. + * src: https://github.com/grafana/sqlds/blob/dda2dc0a54b128961fc9f7885baabf555f3ddfdc/query.go#L36 + */ + format?: number; +} + +export interface CHSqlQuery extends CHQueryBase { + editorType: EditorType.SQL; + queryType?: QueryType; // only used in explore view + meta?: { + timezone?: string; + // meta fields to be used just for building builder options when migrating back to EditorType.Builder + builderOptions?: QueryBuilderOptions; + }; + expand?: boolean; +} + +export interface CHBuilderQuery extends CHQueryBase { + editorType: EditorType.Builder; + builderOptions: QueryBuilderOptions; + meta?: { + timezone?: string; + }; +} + +export type CHQuery = CHSqlQuery | CHBuilderQuery; + +// TODO: these aren't really types +export const defaultEditorType: EditorType = EditorType.Builder; +export const defaultCHBuilderQuery: Omit = { + pluginVersion: '', + editorType: EditorType.Builder, + rawSql: '', + builderOptions: { + database: '', + table: '', + queryType: QueryType.Table, + mode: BuilderMode.List, + columns: [], + meta: {}, + limit: 1000 + }, +}; +export const defaultCHSqlQuery: Omit = { + pluginVersion: '', + editorType: EditorType.SQL, + rawSql: '', + expand: false, +}; diff --git a/src/utils/version.test.ts b/src/utils/version.test.ts index 52691b3b..88ae761f 100644 --- a/src/utils/version.test.ts +++ b/src/utils/version.test.ts @@ -45,6 +45,7 @@ describe('SemVersion', () => { { values: ['3.4.5', '4'], expected: false }, { values: ['3.4.5', '3.5'], expected: false }, { values: ['6.0.0', '5.2.0'], expected: true }, + { values: ['3.1.1', '4.0.0-beta'], expected: false }, ]; cases.forEach((testCase) => { expect(isVersionGtOrEq(testCase.values[0], testCase.values[1])).toBe(testCase.expected); diff --git a/src/utils/version.ts b/src/utils/version.ts index f7e6fccb..f92c5421 100644 --- a/src/utils/version.ts +++ b/src/utils/version.ts @@ -1,5 +1,8 @@ +import pluginPackage from '../../package.json'; import { isNumber } from 'lodash'; +export const pluginVersion = pluginPackage.version; + const versionPattern = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z\.]+))?/; export class SemVersion { diff --git a/src/views/CHConfigEditor.test.tsx b/src/views/CHConfigEditor.test.tsx index 2cf6f086..27c73f7e 100644 --- a/src/views/CHConfigEditor.test.tsx +++ b/src/views/CHConfigEditor.test.tsx @@ -1,10 +1,18 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { ConfigEditor } from './CHConfigEditor'; -import { mockConfigEditorProps } from '../__mocks__/ConfigEditor'; -import { Components } from './../selectors'; +import { mockConfigEditorProps } from '__mocks__/ConfigEditor'; +import { Components } from 'selectors'; import '@testing-library/jest-dom'; -import { Protocol } from '../types'; +import { Protocol } from 'types/config'; + +jest.mock('@grafana/runtime', () => { + const original = jest.requireActual('@grafana/runtime'); + return { + ...original, + config: { buildInfo: { version: '10.0.0' }, featureToggles: { secureSocksDSProxyEnabled: true } }, + }; +}); jest.mock('@grafana/runtime', () => { const original = jest.requireActual('@grafana/runtime'); @@ -71,7 +79,7 @@ describe('ConfigEditor', () => { {...mockConfigEditorProps()} options={{ ...mockConfigEditorProps().options, - jsonData: { ...mockConfigEditorProps().options.jsonData, protocol: Protocol.HTTP }, + jsonData: { ...mockConfigEditorProps().options.jsonData, protocol: Protocol.Http }, }} /> ); @@ -116,16 +124,12 @@ describe('ConfigEditor', () => { const jsonDataOverrides = { defaultDatabase: 'default', queryTimeout: '100', - timeout: '100', - validate: true, + dialTimeout: '100', + validateSql: true, enableSecureSocksProxy: true, customSettings: [{ setting: 'test-setting', value: 'test-value' }], }; render(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.DefaultDatabase.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.QueryTimeout.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.Timeout.placeholder)).toBeInTheDocument(); - expect(screen.getByText(Components.ConfigEditor.Validate.label)).toBeInTheDocument(); expect(screen.getByText(Components.ConfigEditor.SecureSocksProxy.label)).toBeInTheDocument(); expect(screen.getByDisplayValue(jsonDataOverrides.customSettings[0].setting)).toBeInTheDocument(); expect(screen.getByDisplayValue(jsonDataOverrides.customSettings[0].value)).toBeInTheDocument(); diff --git a/src/views/CHConfigEditor.tsx b/src/views/CHConfigEditor.tsx index e210dc44..83c2b53a 100644 --- a/src/views/CHConfigEditor.tsx +++ b/src/views/CHConfigEditor.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent, useMemo, useState } from 'react'; +import React, { ChangeEvent, useState } from 'react'; import { DataSourcePluginOptionsEditorProps, onUpdateDatasourceJsonDataOption, @@ -6,26 +6,36 @@ import { } from '@grafana/data'; import { RadioButtonGroup, Switch, Input, SecretInput, Button, Field, HorizontalGroup } from '@grafana/ui'; import { CertificationKey } from '../components/ui/CertificationKey'; -import { Components } from './../selectors'; -import { CHConfig, CHCustomSetting, CHSecureConfig, Protocol } from './../types'; -import { gte } from 'semver'; +import { Components } from 'selectors'; +import { CHConfig, CHCustomSetting, CHSecureConfig, CHLogsConfig, Protocol, CHTracesConfig } from 'types/config'; +import { gte as versionGte } from 'semver'; import { ConfigSection, ConfigSubSection, DataSourceDescription } from '@grafana/experimental'; import { config } from '@grafana/runtime'; import { Divider } from 'components/Divider'; +import { TimeUnit } from 'types/queryBuilder'; +import { DefaultDatabaseTableConfig } from 'components/configEditor/DefaultDatabaseTableConfig'; +import { QuerySettingsConfig } from 'components/configEditor/QuerySettingsConfig'; +import { LogsConfig } from 'components/configEditor/LogsConfig'; +import { TracesConfig } from 'components/configEditor/TracesConfig'; +import { useMigrateV3Config } from './CHConfigEditorHooks'; -export interface Props extends DataSourcePluginOptionsEditorProps {} +export interface ConfigEditorProps extends DataSourcePluginOptionsEditorProps {} -export const ConfigEditor: React.FC = (props) => { +export const ConfigEditor: React.FC = (props) => { const { options, onOptionsChange } = props; const { jsonData, secureJsonFields } = options; + const labels = Components.ConfigEditor; const secureJsonData = (options.secureJsonData || {}) as CHSecureConfig; const hasTLSCACert = secureJsonFields && secureJsonFields.tlsCACert; const hasTLSClientCert = secureJsonFields && secureJsonFields.tlsClientCert; const hasTLSClientKey = secureJsonFields && secureJsonFields.tlsClientKey; const protocolOptions = [ - { label: 'Native', value: Protocol.NATIVE }, - { label: 'HTTP', value: Protocol.HTTP }, + { label: 'Native', value: Protocol.Native }, + { label: 'HTTP', value: Protocol.Http }, ]; + + useMigrateV3Config(options, onOptionsChange); + const onPortChange = (port: string) => { onOptionsChange({ ...options, @@ -48,7 +58,7 @@ export const ConfigEditor: React.FC = (props) => { }); }; const onSwitchToggle = ( - key: keyof Pick, + key: keyof Pick, value: boolean ) => { onOptionsChange({ @@ -114,21 +124,46 @@ export const ConfigEditor: React.FC = (props) => { }, }); }; + const onLogsConfigChange = (key: keyof CHLogsConfig, value: string | boolean) => { + onOptionsChange({ + ...options, + jsonData: { + ...options.jsonData, + logs: { + ...options.jsonData.logs, + [key]: value + } + } + }); + }; + const onTracesConfigChange = (key: keyof CHTracesConfig, value: string | boolean) => { + onOptionsChange({ + ...options, + jsonData: { + ...options.jsonData, + traces: { + ...options.jsonData.traces, + durationUnit: options.jsonData.traces?.durationUnit || TimeUnit.Nanoseconds, + [key]: value + } + } + }); + }; const [customSettings, setCustomSettings] = useState(jsonData.customSettings || []); - const hasAdditionalSettings = useMemo( - () => - !!( - options.jsonData.defaultDatabase || - options.jsonData.queryTimeout || - options.jsonData.timeout || - options.jsonData.validate || - options.jsonData.enableSecureSocksProxy || - options.jsonData.customSettings - ), - [options] + const hasAdditionalSettings = !!( + options.jsonData.defaultDatabase || + options.jsonData.defaultTable || + options.jsonData.dialTimeout || + options.jsonData.queryTimeout || + options.jsonData.validateSql || + options.jsonData.enableSecureSocksProxy || + options.jsonData.customSettings || + options.jsonData.logs || + options.jsonData.traces ); + return ( <> = (props) => { @@ -168,12 +203,12 @@ export const ConfigEditor: React.FC = (props) => { type="number" value={jsonData.port || ''} onChange={(e) => onPortChange(e.currentTarget.value)} - label={Components.ConfigEditor.ServerPort.label} - aria-label={Components.ConfigEditor.ServerPort.label} - placeholder={Components.ConfigEditor.ServerPort.placeholder(jsonData.secure?.toString() || 'false')} + label={labels.ServerPort.label} + aria-label={labels.ServerPort.label} + placeholder={labels.ServerPort.placeholder(jsonData.secure?.toString() || 'false')} /> - + = (props) => { placeholder={Components.ConfigEditor.Path.placeholder} /> - - + options={protocolOptions} disabledOptions={[]} - value={jsonData.protocol || Protocol.NATIVE} + value={jsonData.protocol || Protocol.Native} onChange={(e) => onProtocolToggle(e!)} /> - + = (props) => { - - - - - - - - - - = (props) => { /> = (props) => { /> = (props) => { onCertificateChangeFactory('tlsCACert', e.currentTarget.value)} - placeholder={Components.ConfigEditor.TLSCACert.placeholder} - label={Components.ConfigEditor.TLSCACert.label} + placeholder={labels.TLSCACert.placeholder} + label={labels.TLSCACert.label} onClick={() => onResetClickFactory('tlsCACert')} /> )} @@ -280,14 +283,14 @@ export const ConfigEditor: React.FC = (props) => { onCertificateChangeFactory('tlsClientCert', e.currentTarget.value)} - placeholder={Components.ConfigEditor.TLSClientCert.placeholder} - label={Components.ConfigEditor.TLSClientCert.label} + placeholder={labels.TLSClientCert.placeholder} + label={labels.TLSClientCert.label} onClick={() => onResetClickFactory('tlsClientCert')} /> onCertificateChangeFactory('tlsClientKey', e.currentTarget.value)} onClick={() => onResetClickFactory('tlsClientKey')} /> @@ -296,65 +299,97 @@ export const ConfigEditor: React.FC = (props) => { - + - - - - - + - - - onSwitchToggle('validate', e.currentTarget.checked)} + label={labels.Password.label} + aria-label={labels.Password.label} + placeholder={labels.Password.placeholder} + value={secureJsonData.password || ''} + isConfigured={(secureJsonFields && secureJsonFields.password) as boolean} + onReset={onResetPassword} + onChange={onUpdateDatasourceSecureJsonDataOption(props, 'password')} /> + + + + + + + + + onSwitchToggle('validateSql', e.currentTarget.checked)} + /> + + + onLogsConfigChange('defaultDatabase', db)} + onDefaultTableChange={table => onLogsConfigChange('defaultTable', table)} + onOtelEnabledChange={v => onLogsConfigChange('otelEnabled', v)} + onOtelVersionChange={v => onLogsConfigChange('otelVersion', v)} + onTimeColumnChange={c => onLogsConfigChange('timeColumn', c)} + onLevelColumnChange={c => onLogsConfigChange('levelColumn', c)} + onMessageColumnChange={c => onLogsConfigChange('messageColumn', c)} + /> + + + onTracesConfigChange('defaultDatabase', db)} + onDefaultTableChange={table => onTracesConfigChange('defaultTable', table)} + onOtelEnabledChange={v => onTracesConfigChange('otelEnabled', v)} + onOtelVersionChange={v => onTracesConfigChange('otelVersion', v)} + onTraceIdColumnChange={c => onTracesConfigChange('traceIdColumn', c)} + onSpanIdColumnChange={c => onTracesConfigChange('spanIdColumn', c)} + onOperationNameColumnChange={c => onTracesConfigChange('operationNameColumn', c)} + onParentSpanIdColumnChange={c => onTracesConfigChange('parentSpanIdColumn', c)} + onServiceNameColumnChange={c => onTracesConfigChange('serviceNameColumn', c)} + onDurationColumnChange={c => onTracesConfigChange('durationColumn', c)} + onDurationUnitChange={c => onTracesConfigChange('durationUnit', c)} + onStartTimeColumnChange={c => onTracesConfigChange('startTimeColumn', c)} + onTagsColumnChange={c => onTracesConfigChange('tagsColumn', c)} + onServiceTagsColumnChange={c => onTracesConfigChange('serviceTagsColumn', c)} + /> - {config.featureToggles['secureSocksDSProxyEnabled'] && gte(config.buildInfo.version, '10.0.0') && ( + + {config.featureToggles['secureSocksDSProxyEnabled'] && versionGte(config.buildInfo.version, '10.0.0') && ( { + it('should not call onOptionsChange if no v3 fields are present', async () => { + const onOptionsChange = jest.fn(); + const options = { + jsonData: { + host: 'new', + dialTimeout: '8' + } + } as any as DataSourceSettings; + + renderHook(opts => useMigrateV3Config(opts, onOptionsChange), { initialProps: options }); + + expect(onOptionsChange).toHaveBeenCalledTimes(0); + }); + + it('should rename v3 fields to latest config names', async () => { + const onOptionsChange = jest.fn(); + const options = { + jsonData: { + server: 'address', + timeout: '8' + } + } as any as DataSourceSettings; + + renderHook(opts => useMigrateV3Config(opts, onOptionsChange), { initialProps: options }); + + const expectedOptions = { + jsonData: { + host: 'address', + dialTimeout: '8' + } + }; + expect(onOptionsChange).toHaveBeenCalledTimes(1); + expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); + }); + + it('should remove v3 fields without overwriting latest config names', async () => { + const onOptionsChange = jest.fn(); + const options = { + jsonData: { + server: 'old', + host: 'new', + timeout: '6', + dialTimeout: '8' + } + } as any as DataSourceSettings; + + renderHook(opts => useMigrateV3Config(opts, onOptionsChange), { initialProps: options }); + + const expectedOptions = { + jsonData: { + host: 'new', + dialTimeout: '8' + } + }; + expect(onOptionsChange).toHaveBeenCalledTimes(1); + expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); + }); +}); diff --git a/src/views/CHConfigEditorHooks.ts b/src/views/CHConfigEditorHooks.ts new file mode 100644 index 00000000..eea4908c --- /dev/null +++ b/src/views/CHConfigEditorHooks.ts @@ -0,0 +1,37 @@ +import { DataSourceSettings } from "@grafana/data"; +import { useEffect, useRef } from "react"; +import { CHConfig } from "types/config"; + +/** + * Migrates v3 config to latest config schema. + * Copies and removes old "server" to "host" field + * Copies and removes old "timeout" to "dialTimeout" field + */ +export const useMigrateV3Config = (options: DataSourceSettings, onOptionsChange: (opts: DataSourceSettings) => void) => { + const { jsonData } = options; + const v3ServerField = (jsonData as any)['server']; + const v3TimeoutField = (jsonData as any)['timeout']; + + const migrated = useRef(!v3ServerField && !v3TimeoutField); + useEffect(() => { + if (!migrated.current) { + const nextJsonOptions = { ...options.jsonData }; + + if (v3ServerField && !nextJsonOptions.host) { + nextJsonOptions.host = v3ServerField; + } + delete (nextJsonOptions as any)['server']; + + if (v3TimeoutField && !nextJsonOptions.dialTimeout) { + nextJsonOptions.dialTimeout = v3TimeoutField; + } + delete (nextJsonOptions as any)['timeout']; + + onOptionsChange({ + ...options, + jsonData: nextJsonOptions, + }); + migrated.current = true; + } + }, [v3ServerField, v3TimeoutField, options, onOptionsChange]); +}; diff --git a/src/views/CHQueryEditor.test.tsx b/src/views/CHQueryEditor.test.tsx index 1b08b69e..d134cddd 100644 --- a/src/views/CHQueryEditor.test.tsx +++ b/src/views/CHQueryEditor.test.tsx @@ -4,7 +4,7 @@ import '@testing-library/jest-dom'; import { CHQueryEditor } from './CHQueryEditor'; import * as ui from '@grafana/ui'; import { mockDatasource } from '__mocks__/datasource'; -import { QueryType } from 'types'; +import { EditorType } from 'types/sql'; jest.mock('@grafana/ui', () => ({ ...jest.requireActual('@grafana/ui'), @@ -25,7 +25,7 @@ describe('Query Editor', () => { const rawSql = 'foo'; render( ; +/** + * Top level query editor component + */ +export const CHQueryEditor = (props: CHQueryEditorProps) => { + const { query: savedQuery, onRunQuery } = props; + const query = migrateCHQuery(savedQuery); + + return ( + <> +
+ + +
+ + + ); +}; + const CHEditorByType = (props: CHQueryEditorProps) => { const { query, onChange, app } = props; - const onBuilderOptionsChange = (builderOptions: SqlBuilderOptions) => { - const sql = getSQLFromQueryOptions(builderOptions); - const format = - query.selectedFormat === Format.AUTO - ? builderOptions.mode === BuilderMode.Trend - ? Format.TIMESERIES - : Format.TABLE - : query.selectedFormat; - onChange({ ...query, queryType: QueryType.Builder, rawSql: sql, builderOptions, format }); - }; + const [builderOptions, builderOptionsDispatch] = useBuilderOptionsState((query as CHBuilderQuery).builderOptions); - switch (query.queryType) { - case QueryType.SQL: - return ( -
- -
- ); - case QueryType.Builder: - default: - let newQuery: CHBuilderQuery = { ...query }; - if (query.rawSql && !query.builderOptions) { - return ( -
- -
- ); - } - if (!query.rawSql || !query.builderOptions) { - newQuery = { - ...newQuery, - rawSql: defaultCHBuilderQuery.rawSql, - builderOptions: { - ...defaultCHBuilderQuery.builderOptions, - database: props.datasource.getDefaultDatabase() || 'default', - }, - }; - } - return ( -
- - -
- ); + /** + * Grafana will sometimes replace the builder options directly, so we need to sync in both directions. + * For example, selecting an entry from the query history will cause the local state to fall out of sync. + * The "key" property is present on these historical entries. + */ + const queryKey = query.key || '' + const lastKey = useRef(queryKey); + if (queryKey !== lastKey.current && query.editorType === EditorType.Builder) { + builderOptionsDispatch(setAllOptions((query as CHBuilderQuery).builderOptions || {})); + lastKey.current = queryKey; } -}; -export const CHQueryEditor = (props: CHQueryEditorProps) => { - const { query, onChange, onRunQuery } = props; + // Prevent trying to run empty query on load + const shouldSkipChanges = useRef(true); + if (isBuilderOptionsRunnable(builderOptions)) { + shouldSkipChanges.current = false; + } - React.useEffect(() => { - if (typeof query.selectedFormat === 'undefined' && query.queryType === QueryType.SQL) { - const selectedFormat = Format.AUTO; - const format = getFormat(query.rawSql, selectedFormat); - onChange({ ...query, selectedFormat, format }); + useEffect(() => { + if (shouldSkipChanges.current || query.editorType === EditorType.SQL) { + return; } - }, [query, onChange]); + + const sql = generateSql(builderOptions); + onChange({ + ...query, + pluginVersion, + editorType: EditorType.Builder, + rawSql: sql, + builderOptions, + format: mapQueryTypeToGrafanaFormat(builderOptions.queryType) + }); + + // TODO: fix dependency warning with "useEffectEvent" once added to stable version of react + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [builderOptions]); + + if (query.editorType === EditorType.SQL) { + return ( +
+ +
+ ); + } return ( - <> - - - + ); }; diff --git a/yarn.lock b/yarn.lock index ce320fe4..f9b91b9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,55 +2,25 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@adobe/css-tools@^4.0.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.1.tgz#abfccb8ca78075a2b6187345c26243c1a0842f28" integrity sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg== -"@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== - dependencies: - "@jridgewell/trace-mapping" "^0.3.0" - -"@ampproject/remapping@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" - integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== - dependencies: - "@babel/highlight" "^7.12.13" - -"@babel/code-frame@^7.10.4": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" - integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== - dependencies: - "@babel/highlight" "^7.16.0" - -"@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== - dependencies: - "@babel/highlight" "^7.16.7" - -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/code-frame@^7.22.13": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.13": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== @@ -58,20 +28,10 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" - integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== - -"@babel/compat-data@^7.18.8": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.13.tgz#6aff7b350a1e8c3e40b029e46cbe78e24a913483" - integrity sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw== - -"@babel/compat-data@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" - integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.9": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11" + integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ== "@babel/core@7.20.5": version "7.20.5" @@ -94,69 +54,28 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/core@^7.0.1", "@babel/core@^7.21.4": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.15.tgz#15d4fd03f478a459015a4b94cfbb3bd42c48d2f4" - integrity sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA== +"@babel/core@^7.0.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.21.4": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9" + integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.22.15" + "@babel/generator" "^7.23.3" "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-module-transforms" "^7.22.15" - "@babel/helpers" "^7.22.15" - "@babel/parser" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.2" + "@babel/parser" "^7.23.3" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.15" - "@babel/types" "^7.22.15" - convert-source-map "^1.7.0" + "@babel/traverse" "^7.23.3" + "@babel/types" "^7.23.3" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" - integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.3" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.3" - "@babel/types" "^7.21.3" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/generator@^7.17.9", "@babel/generator@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" - integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== - dependencies: - "@babel/types" "^7.22.15" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.20.5", "@babel/generator@^7.21.3", "@babel/generator@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" - integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== - dependencies: - "@babel/types" "^7.21.3" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.23.3": +"@babel/generator@^7.17.9", "@babel/generator@^7.20.5", "@babel/generator@^7.23.3", "@babel/generator@^7.7.2": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e" integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg== @@ -166,50 +85,21 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" - integrity sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== +"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" - semver "^6.3.0" + "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" + "@babel/types" "^7.22.15" -"@babel/helper-compilation-targets@^7.22.15": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== @@ -220,56 +110,29 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.13.tgz#63e771187bd06d234f95fdf8bd5f8b6429de6298" - integrity sha512-hDvXp+QYxSRL+23mpAlSGxHMDyIGChm0/AwTfTAAK5Ufe40nCsyNdaYCGuK91phn/fVu9kqayImRDkvNAgdrsA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.9" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" - integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-create-regexp-features-plugin@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.13.tgz#0996d370a92896c612ae41a4215544bd152579c0" - integrity sha512-XC+kiA0J3at6E85dL5UnCYfVOcIZ834QcAY0TIpgUVnz0zDzg+0TtvZTnJ4g9L1dPRGe30Qi03XCIS4tYCLtqw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - regexpu-core "^4.7.1" - -"@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" - integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" + integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" - integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" regexpu-core "^5.3.1" + semver "^6.3.1" "@babel/helper-define-polyfill-provider@^0.3.3": version "0.3.3" @@ -283,45 +146,12 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-environment-visitor@^7.22.20": +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.22.5": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== - -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== - dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-function-name@^7.23.0": +"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -329,13 +159,6 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -343,158 +166,60 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== - dependencies: - "@babel/types" "^7.18.9" - -"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" - integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== - dependencies: - "@babel/types" "^7.21.0" - -"@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== +"@babel/helper-member-expression-to-functions@^7.22.15": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.23.0" -"@babel/helper-module-imports@^7.22.15": +"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.20.2", "@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" - -"@babel/helper-module-transforms@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz#40ad2f6950f143900e9c1c72363c0b431a606082" - integrity sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ== +"@babel/helper-module-transforms@^7.20.2", "@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.15" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.12.13.tgz#174254d0f2424d8aefb4dd48057511247b0a9eeb" - integrity sha512-C+10MXCXJLiR6IeG9+Wiejt9jmtFpxUc3MQqCmPY8hfCjyUGl9kT+B2okzEZrtykiwrc4dbCPdDoz0A/HQbDaA== - -"@babel/helper-plugin-utils@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-plugin-utils@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== - -"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" - integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== - -"@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/helper-validator-identifier" "^7.22.20" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" - integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/types" "^7.22.5" -"@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== +"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== dependencies: - "@babel/types" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== +"@babel/helper-replace-supers@^7.22.20", "@babel/helper-replace-supers@^7.22.9": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== dependencies: - "@babel/types" "^7.20.2" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-simple-access@^7.22.5": version "7.22.5" @@ -503,26 +228,12 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== - dependencies: - "@babel/types" "^7.18.9" - -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" @@ -531,179 +242,68 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== - -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - "@babel/helper-string-parser@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== - -"@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== - -"@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.22.15", "@babel/helper-validator-identifier@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" - integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== - "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helper-validator-option@^7.22.15": +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== -"@babel/helper-wrap-function@^7.18.9": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz#bff23ace436e3f6aefb61f85ffae2291c80ed1fb" - integrity sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w== - dependencies: - "@babel/helper-function-name" "^7.18.9" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.11" - "@babel/types" "^7.18.10" - -"@babel/helpers@^7.20.5", "@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" - -"@babel/helpers@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" - integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== dependencies: + "@babel/helper-function-name" "^7.22.5" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/types" "^7.22.19" -"@babel/highlight@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" - integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== +"@babel/helpers@^7.20.5", "@babel/helpers@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" + integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" - integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" "@babel/highlight@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" - integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== dependencies: - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13": - version "7.12.14" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.14.tgz#4adb7c5eef1d437ef965ad1569cd826db8c11dc9" - integrity sha512-xcfxDq3OrBnDsA/Z8eK5/2iPcLD8qbOaSSfOw4RA6jp4i7e6dEQ7+wTwxItEwzcXPQcsry5nZk96gmVPKletjQ== - -"@babel/parser@^7.13.0", "@babel/parser@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160" - integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA== - -"@babel/parser@^7.14.7", "@babel/parser@^7.20.5", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" - integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== - -"@babel/parser@^7.18.10": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.13.tgz#5b2dd21cae4a2c5145f1fbd8ca103f9313d3b7e4" - integrity sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg== - -"@babel/parser@^7.23.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.13.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.5", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" + integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" + integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.23.3" "@babel/plugin-proposal-async-generator-functions@^7.20.1": version "7.20.7" @@ -800,15 +400,6 @@ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.20.7": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -826,16 +417,16 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" - integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + version "7.21.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" + integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-create-class-features-plugin" "^7.21.0" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.18.6": +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== @@ -843,14 +434,6 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba" - integrity sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -894,11 +477,11 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -915,11 +498,11 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -970,285 +553,279 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.14.5": +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" - integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.20.2": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" - integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.3.tgz#e99a3ff08f58edd28a8ed82481df76925a4ffca7" + integrity sha512-QPZxHrThbQia7UdvfpaRRlq/J9ciz1J4go0k+lPBXbgaNeY7IQrBj/9ceWjvMMI07/ZBzHl/F0R/2K0qH7jCVw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.20.2": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" - integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz#73380c632c095b03e8503c24fd38f95ad41ffacb" + integrity sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.18.9": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.15" "@babel/plugin-transform-destructuring@^7.20.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" - integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dotall-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" + integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad" - integrity sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" + integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" + integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-for-of@^7.18.8": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" - integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz#afe115ff0fbce735e02868d41489093c63e15559" + integrity sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-amd@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" + integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-commonjs@^7.19.6": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" - integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== dependencies: - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" "@babel/plugin-transform-modules-systemjs@^7.19.6": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" + integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" + integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" + integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + +"@babel/plugin-transform-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.3.tgz#92fc83f54aa3adc34288933fa27e54c13113f4be" + integrity sha512-zvL8vIfIUgMccIAK1lxjvNv572JHFJIKb4MWBz5OGdBQA0fB0Xluix5rmOby48exiJc987neOmP/m9Fnpkz3Tg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" - integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-regenerator@^7.18.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" - integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" + integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - regenerator-transform "^0.15.1" + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.2" "@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" + integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-spread@^7.19.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" + integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" + integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@7.20.2": version "7.20.2" @@ -1332,9 +909,9 @@ semver "^6.3.0" "@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + version "0.1.6" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6.tgz#31bcdd8f19538437339d17af00d177d854d9d458" + integrity sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" @@ -1347,60 +924,14 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.15.4", "@babel/runtime@^7.6.2": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" - integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d" - integrity sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.18.0", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/runtime@^7.18.3": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.19.4", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8" - integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.18.10", "@babel/template@^7.18.6": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" - -"@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/template@^7.22.15": +"@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.3.3": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== @@ -1409,16 +940,7 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/template@^7.3.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" - integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/parser" "^7.12.13" - "@babel/types" "^7.12.13" - -"@babel/traverse@^7.17.9", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.22.15": +"@babel/traverse@^7.17.9", "@babel/traverse@^7.20.5", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b" integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ== @@ -1434,51 +956,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.13", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.13.tgz#8be1aa8f2c876da11a9cf650c0ecf656913ad611" - integrity sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.16.7": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.13.tgz#30aeb9e514f4100f7c1cb6e5ba472b30e48f519a" - integrity sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ== - dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" - integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.15", "@babel/types@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.15.tgz#266cb21d2c5fd0b3931e7a91b6dd72d2f617d282" - integrity sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.15" - to-fast-properties "^2.0.0" - -"@babel/types@^7.23.0", "@babel/types@^7.23.3": +"@babel/types@^7.0.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== @@ -1911,24 +1389,7 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@emotion/babel-plugin@^11.10.5", "@emotion/babel-plugin@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead" - integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/serialize" "^1.1.1" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.1.3" - -"@emotion/babel-plugin@^11.11.0": +"@emotion/babel-plugin@^11.10.5", "@emotion/babel-plugin@^11.11.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== @@ -1945,18 +1406,7 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.10.5": - version "11.10.5" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12" - integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA== - dependencies: - "@emotion/memoize" "^0.8.0" - "@emotion/sheet" "^1.2.1" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - stylis "4.1.3" - -"@emotion/cache@^11.11.0": +"@emotion/cache@^11.10.5", "@emotion/cache@^11.11.0", "@emotion/cache@^11.4.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff" integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ== @@ -1967,17 +1417,6 @@ "@emotion/weak-memoize" "^0.3.1" stylis "4.2.0" -"@emotion/cache@^11.4.0": - version "11.10.3" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87" - integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ== - dependencies: - "@emotion/memoize" "^0.8.0" - "@emotion/sheet" "^1.2.0" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - stylis "4.0.13" - "@emotion/css@11.10.5": version "11.10.5" resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.5.tgz#ca01bb83ce60517bc3a5c01d27ccf552fed84d9d" @@ -1989,7 +1428,7 @@ "@emotion/sheet" "^1.2.1" "@emotion/utils" "^1.2.0" -"@emotion/css@11.11.2": +"@emotion/css@11.11.2", "@emotion/css@^11.1.3": version "11.11.2" resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.11.2.tgz#e5fa081d0c6e335352e1bc2b05953b61832dca5a" integrity sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew== @@ -2000,32 +1439,11 @@ "@emotion/sheet" "^1.2.2" "@emotion/utils" "^1.2.1" -"@emotion/css@^11.1.3": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.6.tgz#5d226fdd8ef2a46d28e4eb09f66dc01a3bda5a04" - integrity sha512-88Sr+3heKAKpj9PCqq5A1hAmAkoSIvwEq1O2TwDij7fUtsJpdkV4jMTISSTouFeRvsGvXIpuSuDQ4C1YdfNGXw== - dependencies: - "@emotion/babel-plugin" "^11.10.6" - "@emotion/cache" "^11.10.5" - "@emotion/serialize" "^1.1.1" - "@emotion/sheet" "^1.2.1" - "@emotion/utils" "^1.2.0" - -"@emotion/hash@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" - integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== - "@emotion/hash@^0.9.1": version "0.9.1" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== -"@emotion/memoize@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" - integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== - "@emotion/memoize@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" @@ -2045,7 +1463,7 @@ "@emotion/weak-memoize" "^0.3.0" hoist-non-react-statics "^3.3.1" -"@emotion/react@11.11.1": +"@emotion/react@11.11.1", "@emotion/react@^11.8.1": version "11.11.1" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157" integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== @@ -2059,32 +1477,7 @@ "@emotion/weak-memoize" "^0.3.1" hoist-non-react-statics "^3.3.1" -"@emotion/react@^11.8.1": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.6.tgz#dbe5e650ab0f3b1d2e592e6ab1e006e75fd9ac11" - integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.6" - "@emotion/cache" "^11.10.5" - "@emotion/serialize" "^1.1.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" - "@emotion/weak-memoize" "^0.3.0" - hoist-non-react-statics "^3.3.1" - -"@emotion/serialize@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0" - integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA== - dependencies: - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/unitless" "^0.8.0" - "@emotion/utils" "^1.2.0" - csstype "^3.0.2" - -"@emotion/serialize@^1.1.2": +"@emotion/serialize@^1.1.1", "@emotion/serialize@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== @@ -2095,57 +1488,27 @@ "@emotion/utils" "^1.2.1" csstype "^3.0.2" -"@emotion/sheet@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5" - integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w== - -"@emotion/sheet@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c" - integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA== - -"@emotion/sheet@^1.2.2": +"@emotion/sheet@^1.2.1", "@emotion/sheet@^1.2.2": version "1.2.2" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== -"@emotion/unitless@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" - integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== - "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== -"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" - integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== - -"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": +"@emotion/use-insertion-effect-with-fallbacks@^1.0.0", "@emotion/use-insertion-effect-with-fallbacks@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== -"@emotion/utils@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" - integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== - -"@emotion/utils@^1.2.1": +"@emotion/utils@^1.2.0", "@emotion/utils@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== -"@emotion/weak-memoize@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" - integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== - -"@emotion/weak-memoize@^0.3.1": +"@emotion/weak-memoize@^0.3.0", "@emotion/weak-memoize@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== @@ -2167,14 +1530,14 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.4.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.0.tgz#11195513186f68d42fbf449f9a7136b2c0c92005" - integrity sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg== + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== "@eslint/eslintrc@^2.0.3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" - integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== + version "2.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.3.tgz#797470a75fe0fbd5a53350ee715e85e87baff22d" + integrity sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -2191,56 +1554,64 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6" integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw== -"@floating-ui/core@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.4.tgz#89e6311b021190c9e121fcf20306e76ac66e4066" - integrity sha512-SQOeVbMwb1di+mVWWJLpsUTToKfqVNioXys011beCAhyOIFtS+GQoW4EQSneuxzmQKddExDwQ+X0hLl4lJJaSQ== +"@floating-ui/core@^1.4.2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c" + integrity sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg== + dependencies: + "@floating-ui/utils" "^0.1.3" "@floating-ui/dom@^1.0.1": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.5.tgz#c9ec259a24ce0958b1ea29674df4eee4455361a9" - integrity sha512-+sAUfpQ3Frz+VCbPCqj+cZzvEESy3fjSeT/pDWkYCWOBXYNNKZfuVsHuv8/JO2zze8+Eb/Q7a6hZVgzS81fLbQ== + version "1.5.3" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" + integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== dependencies: - "@floating-ui/core" "^1.2.4" + "@floating-ui/core" "^1.4.2" + "@floating-ui/utils" "^0.1.3" -"@formatjs/ecma402-abstract@1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.12.0.tgz#2fb5e8983d5fae2fad9ec6c77aec1803c2b88d8e" - integrity sha512-0/wm9b7brUD40kx7KSE0S532T8EfH06Zc41rGlinoNyYXnuusR6ull2x63iFJgVXgwahm42hAW7dcYdZ+llZzA== +"@floating-ui/utils@^0.1.3": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" + integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== + +"@formatjs/ecma402-abstract@1.18.0": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.0.tgz#e2120e7101020140661b58430a7ff4262705a2f2" + integrity sha512-PEVLoa3zBevWSCZzPIM/lvPCi8P5l4G+NXQMc/CjEiaCWgyHieUoo0nM7Bs0n/NbuQ6JpXEolivQ9pKSBHaDlA== dependencies: - "@formatjs/intl-localematcher" "0.2.31" - tslib "2.4.0" + "@formatjs/intl-localematcher" "0.5.2" + tslib "^2.4.0" -"@formatjs/fast-memoize@1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.6.tgz#a442970db7e9634af556919343261a7bbe5e88c3" - integrity sha512-9CWZ3+wCkClKHX+i5j+NyoBVqGf0pIskTo6Xl6ihGokYM2yqSSS68JIgeo+99UIHc+7vi9L3/SDSz/dWI9SNlA== +"@formatjs/fast-memoize@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz#33bd616d2e486c3e8ef4e68c99648c196887802b" + integrity sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA== dependencies: - tslib "2.4.0" + tslib "^2.4.0" -"@formatjs/icu-messageformat-parser@2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.7.tgz#35dc556c13a0544cc730300c8ddb730ba7f44bd4" - integrity sha512-KM4ikG5MloXMulqn39Js3ypuVzpPKq/DDplvl01PE2qD9rAzFO8YtaUCC9vr9j3sRXwdHPeTe8r3J/8IJgvYEQ== +"@formatjs/icu-messageformat-parser@2.7.3": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.3.tgz#c8c95e7c9f8141bdb93bea0e92e4fcace19d3c9f" + integrity sha512-X/jy10V9S/vW+qlplqhMUxR8wErQ0mmIYSq4mrjpjDl9mbuGcCILcI1SUYkL5nlM4PJqpc0KOS0bFkkJNPxYRw== dependencies: - "@formatjs/ecma402-abstract" "1.12.0" - "@formatjs/icu-skeleton-parser" "1.3.13" - tslib "2.4.0" + "@formatjs/ecma402-abstract" "1.18.0" + "@formatjs/icu-skeleton-parser" "1.7.0" + tslib "^2.4.0" -"@formatjs/icu-skeleton-parser@1.3.13": - version "1.3.13" - resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.13.tgz#f7e186e72ed73c3272d22a3aacb646e77368b099" - integrity sha512-qb1kxnA4ep76rV+d9JICvZBThBpK5X+nh1dLmmIReX72QyglicsaOmKEcdcbp7/giCWfhVs6CXPVA2JJ5/ZvAw== +"@formatjs/icu-skeleton-parser@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.7.0.tgz#796938d6d0ba8fc75bb9edee038d1350bfee32cb" + integrity sha512-Cfdo/fgbZzpN/jlN/ptQVe0lRHora+8ezrEeg2RfrNjyp+YStwBy7cqDY8k5/z2LzXg6O0AdzAV91XS0zIWv+A== dependencies: - "@formatjs/ecma402-abstract" "1.12.0" - tslib "2.4.0" + "@formatjs/ecma402-abstract" "1.18.0" + tslib "^2.4.0" -"@formatjs/intl-localematcher@0.2.31": - version "0.2.31" - resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.31.tgz#aada2b1e58211460cedba56889e3c489117eb6eb" - integrity sha512-9QTjdSBpQ7wHShZgsNzNig5qT3rCPvmZogS/wXZzKotns5skbXgs0I7J8cuN0PPqXyynvNVuN+iOKhNS2eb+ZA== +"@formatjs/intl-localematcher@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.2.tgz#5fcf029fd218905575e5080fa33facdcb623d532" + integrity sha512-txaaE2fiBMagLrR4jYhxzFO6wEdEG4TPMqrzBAcbr4HFUYzH/YC+lg6OIzKCHm8WgDdyQevxbAAV1OgcXctuGw== dependencies: - tslib "2.4.0" + tslib "^2.4.0" "@grafana/data@10.1.0": version "10.1.0" @@ -2363,26 +1734,18 @@ typescript "4.8.4" "@grafana/experimental@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@grafana/experimental/-/experimental-1.7.0.tgz#f05ce178f3201cd5268645eb14dc5bcfefa05a4c" - integrity sha512-3DfDzGTUnvG/v2U0lhBaB05j4x0bczrgylrg5Co6LMyRYh1kPA4XnK0dTshSxA6igjKqAjSacfwZQ40f4rLdqw== + version "1.7.4" + resolved "https://registry.yarnpkg.com/@grafana/experimental/-/experimental-1.7.4.tgz#5b5efe89abf38b1d3358251148d42b9111de539e" + integrity sha512-uYkv9HRx+cqJRktsY43ApG0+kgG4fNR8lv+bkaFvGyCg46dcTeNqokujoPnHp6lt9MEn+0Y3jKEQbvCXjcz2bA== dependencies: "@types/uuid" "^8.3.3" + semver "^7.5.4" uuid "^8.3.2" -"@grafana/faro-core@^1.0.0-beta2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@grafana/faro-core/-/faro-core-1.0.2.tgz#ac3d635a4ac464ab577c7e475c78211acd09bf02" - integrity sha512-6gOgyF0wxbubaDk/n3TceKF5Rgn+3rVKpHzD8ktseFfm+4c1oAc27d6XqrLm3cz/KSChs+k7amrZtGo6p+X4nA== - dependencies: - "@opentelemetry/api" "^1.4.0" - "@opentelemetry/api-metrics" "^0.33.0" - "@opentelemetry/otlp-transformer" "^0.35.0" - -"@grafana/faro-core@^1.1.0": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@grafana/faro-core/-/faro-core-1.1.4.tgz#45c2ea7a64db38f4efcdcda73b61b98f05cf039f" - integrity sha512-JpxtCXHsjkvuWE4gZ/EmsXAXY/buPtLaB3kWQsVMnlNglxoyFM+FXS0VB48Ip66cDmYZcdtxAW1NW3SqaW2O+g== +"@grafana/faro-core@^1.0.0-beta2", "@grafana/faro-core@^1.1.0": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@grafana/faro-core/-/faro-core-1.2.7.tgz#4bad7b12394d866d233b1925fb09857f2bb3381a" + integrity sha512-BgZngS/FAMk7ytp4OpYh5I08r7OP/gq3kyTXNtoVS4f8j9btNhqH+IIuFkxP8zhprEvJqEu15gZAgvxhKb8ktQ== dependencies: "@opentelemetry/api" "^1.4.1" "@opentelemetry/api-metrics" "^0.33.0" @@ -2585,11 +1948,11 @@ uuid "9.0.0" "@humanwhocodes/config-array@^0.11.10": - version "0.11.11" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" - integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" + "@humanwhocodes/object-schema" "^2.0.1" debug "^4.1.1" minimatch "^3.0.5" @@ -2598,48 +1961,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - -"@internationalized/date@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.0.1.tgz#66332e9ca8f59b7be010ca65d946bca430ba4b66" - integrity sha512-E/3lASs4mAeJ2Z2ye6ab7eUD0bPUfTeNVTAv6IS+ne9UtMu9Uepb9A1U2Ae0hDr6WAlBuvUtrakaxEdYB9TV6Q== - dependencies: - "@babel/runtime" "^7.6.2" - -"@internationalized/date@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.1.0.tgz#da48aeaa971df6ad410cd32597c174d6cab9a3b4" - integrity sha512-wjeur7K4AecT+YwoBmBXQ/+n5lP69tuZc34hw09s44EozZK7FZHSyfPvRp5/xEb2D6abLboskDY4jG+Nt0TNUQ== - dependencies: - "@swc/helpers" "^0.4.14" +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== -"@internationalized/date@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.4.0.tgz#e843ac40b04afafe99fe0a41bae7abdd221a9a44" - integrity sha512-QUDSGCsvrEVITVf+kv9VSAraAmCgjQmU5CiXtesUBBhBe374NmnEIIaOFBZ72t29dfGMBP0zF+v6toVnbcc6jg== +"@internationalized/date@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.5.0.tgz#67f1dd62355f05140cc80e324842e9bfb4553abe" + integrity sha512-nw0Q+oRkizBWMioseI8+2TeUPEyopJVz5YxoYVzR0W1v+2YytiYah7s/ot35F149q/xAg4F1gT/6eTd+tsUpFQ== dependencies: "@swc/helpers" "^0.5.0" -"@internationalized/message@^3.0.9": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@internationalized/message/-/message-3.0.9.tgz#52bc20debe5296375d66ffcf56c3df5d8118a37d" - integrity sha512-yHQggKWUuSvj1GznVtie4tcYq+xMrkd/lTKCFHp6gG18KbIliDw+UI7sL9+yJPGuWiR083xuLyyhzqiPbNOEww== - dependencies: - "@babel/runtime" "^7.6.2" - intl-messageformat "^10.1.0" - -"@internationalized/message@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@internationalized/message/-/message-3.1.0.tgz#b284014cd8bbb430a648b76c87c62bdca968b04c" - integrity sha512-Oo5m70FcBdADf7G8NkUffVSfuCdeAYVfsvNjZDi9ELpjvkc4YNJVTHt/NyTI9K7FgAVoELxiP9YmN0sJ+HNHYQ== - dependencies: - "@swc/helpers" "^0.4.14" - intl-messageformat "^10.1.0" - "@internationalized/message@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@internationalized/message/-/message-3.1.1.tgz#0f29c5a239b5dcd457b55f21dcd38d1a44a1236a" @@ -2648,41 +1981,13 @@ "@swc/helpers" "^0.5.0" intl-messageformat "^10.1.0" -"@internationalized/number@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.1.1.tgz#160584316741de4381689ab759001603ee17b595" - integrity sha512-dBxCQKIxvsZvW2IBt3KsqrCfaw2nV6o6a8xsloJn/hjW0ayeyhKuiiMtTwW3/WGNPP7ZRyDbtuiUEjMwif1ENQ== - dependencies: - "@babel/runtime" "^7.6.2" - -"@internationalized/number@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.2.0.tgz#dffb661cacd61a87b814c47b7d5240a286249066" - integrity sha512-GUXkhXSX1Ee2RURnzl+47uvbOxnlMnvP9Er+QePTjDjOPWuunmLKlEkYkEcLiiJp7y4l9QxGDLOlVr8m69LS5w== - dependencies: - "@swc/helpers" "^0.4.14" - -"@internationalized/number@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.2.1.tgz#570e4010544a84a8225e65b34a689a36187caaa8" - integrity sha512-hK30sfBlmB1aIe3/OwAPg9Ey0DjjXvHEiGVhNaOiBJl31G0B6wMaX8BN3ibzdlpyRNE9p7X+3EBONmxtJO9Yfg== +"@internationalized/number@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.4.0.tgz#1c3ebf6ac40ce649d3d97bb835ff0559957f2e1f" + integrity sha512-8TvotW3qVDHC4uv/BVoN6Qx0Dm8clHY1/vpH+dh+XRiPW/9NVpKn1P8d1A+WLphWrMwyqyWXI7uWehJPviaeIw== dependencies: "@swc/helpers" "^0.5.0" -"@internationalized/string@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@internationalized/string/-/string-3.0.0.tgz#de563871e1b19e4d0ce3246ec18d25da1a73db73" - integrity sha512-NUSr4u+mNu5BysXFeVWZW4kvjXylPkU/YYqaWzdNuz1eABfehFiZTEYhWAAMzI3U8DTxfqF9PM3zyhk5gcfz6w== - dependencies: - "@babel/runtime" "^7.6.2" - -"@internationalized/string@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@internationalized/string/-/string-3.1.0.tgz#0b365906a8c3f44800b0db52c2e990cff345abce" - integrity sha512-TJQKiyUb+wyAfKF59UNeZ/kELMnkxyecnyPCnBI1ma4NaXReJW+7Cc2mObXAqraIBJUVv7rgI46RLKrLgi35ng== - dependencies: - "@swc/helpers" "^0.4.14" - "@internationalized/string@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@internationalized/string/-/string-3.1.1.tgz#2ab7372d58bbb7ffd3de62fc2a311e4690186981" @@ -2714,31 +2019,31 @@ resolve-from "^5.0.0" "@istanbuljs/schema@^0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" - integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.4.tgz#a7e2d84516301f986bba0dd55af9d5fe37f46527" - integrity sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.6.3" - jest-util "^29.6.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.4.tgz#265ebee05ec1ff3567757e7a327155c8d6bdb126" - integrity sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^29.6.4" - "@jest/reporters" "^29.6.4" - "@jest/test-result" "^29.6.4" - "@jest/transform" "^29.6.4" + "@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" @@ -2746,21 +2051,21 @@ ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.6.3" - jest-config "^29.6.4" - jest-haste-map "^29.6.4" - jest-message-util "^29.6.3" + 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.6.4" - jest-resolve-dependencies "^29.6.4" - jest-runner "^29.6.4" - jest-runtime "^29.6.4" - jest-snapshot "^29.6.4" - jest-util "^29.6.3" - jest-validate "^29.6.3" - jest-watcher "^29.6.4" + 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.6.3" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" @@ -2771,69 +2076,62 @@ dependencies: "@jest/types" "^27.5.1" -"@jest/environment@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.4.tgz#78ec2c9f8c8829a37616934ff4fea0c028c79f4f" - integrity sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@jest/fake-timers" "^29.6.4" + "@jest/fake-timers" "^29.7.0" "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^29.6.3" + jest-mock "^29.7.0" -"@jest/expect-utils@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" - integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== - dependencies: - jest-get-type "^29.4.3" - -"@jest/expect-utils@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.4.tgz#17c7dfe6cec106441f218b0aff4b295f98346679" - integrity sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: jest-get-type "^29.6.3" -"@jest/expect@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.4.tgz#1d6ae17dc68d906776198389427ab7ce6179dba6" - integrity sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA== +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - expect "^29.6.4" - jest-snapshot "^29.6.4" + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@jest/fake-timers@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.4.tgz#45a27f093c43d5d989362a3e7a8c70c83188b4f6" - integrity sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: "@jest/types" "^29.6.3" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.6.3" - jest-mock "^29.6.3" - jest-util "^29.6.3" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/globals@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.4.tgz#4f04f58731b062b44ef23036b79bdb31f40c7f63" - integrity sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@jest/environment" "^29.6.4" - "@jest/expect" "^29.6.4" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" "@jest/types" "^29.6.3" - jest-mock "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.4.tgz#9d6350c8a2761ece91f7946e97ab0dabc06deab7" - integrity sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.6.4" - "@jest/test-result" "^29.6.4" - "@jest/transform" "^29.6.4" + "@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" "*" @@ -2847,21 +2145,14 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.6.3" - jest-util "^29.6.3" - jest-worker "^29.6.4" + 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" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== - dependencies: - "@sinclair/typebox" "^0.25.16" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -2878,30 +2169,30 @@ callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.4.tgz#adf5c79f6e1fb7405ad13d67d9e2b6ff54b54c6b" - integrity sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^29.6.4" + "@jest/console" "^29.7.0" "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz#86aef66aaa22b181307ed06c26c82802fb836d7b" - integrity sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^29.6.4" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^29.6.4" + jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^29.6.4": - version "29.6.4" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.4.tgz#a6bc799ef597c5d85b2e65a11fd96b6b239bab5a" - integrity sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" "@jest/types" "^29.6.3" @@ -2911,36 +2202,14 @@ convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.6.4" + jest-haste-map "^29.7.0" jest-regex-util "^29.6.3" - jest-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" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - -"@jest/types@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.5.tgz#420765c052605e75686982d24b061b4cbba22132" - integrity sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - "@jest/types@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" @@ -2952,18 +2221,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" - integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== - dependencies: - "@jest/schemas" "^29.4.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" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -2976,39 +2233,21 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== - -"@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== @@ -3021,17 +2260,7 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== - -"@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -3044,38 +2273,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - -"@jridgewell/trace-mapping@^0.3.18": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" - integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@leeoniya/ufuzzy@0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@leeoniya/ufuzzy/-/ufuzzy-0.9.0.tgz#efb8f19f64ef6ff754fc49935c9ad53ab99712c1" @@ -3089,7 +2294,7 @@ "@mapbox/jsonlint-lines-primitives@~2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" - integrity sha1-zlblOfg1UrWNENZy6k1vya3HsjQ= + integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ== "@mapbox/mapbox-gl-style-spec@^13.23.1": version "13.28.0" @@ -3108,29 +2313,22 @@ "@mapbox/point-geometry@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" - integrity sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI= + integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ== "@mapbox/unitbezier@^0.0.0": version "0.0.0" resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e" - integrity sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4= + integrity sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA== "@mochajs/json-file-reporter@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@mochajs/json-file-reporter/-/json-file-reporter-1.3.0.tgz#63a53bcda93d75f5c5c74af60e45da063931370b" integrity sha512-evIxpeP8EOixo/T2xh5xYEIzwbEHk8YNJfRUm1KeTs8F3bMjgNn2580Ogze9yisXNlTxu88JiJJYzXjjg5NdLA== -"@monaco-editor/loader@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.2.tgz#04effbb87052d19cd7d3c9d81c0635490f9bb6d8" - integrity sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g== - dependencies: - state-local "^1.0.6" - -"@monaco-editor/loader@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca" - integrity sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q== +"@monaco-editor/loader@^1.3.2", "@monaco-editor/loader@^1.3.3": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558" + integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg== dependencies: state-local "^1.0.6" @@ -3149,14 +2347,6 @@ dependencies: "@monaco-editor/loader" "^1.3.3" -"@nodelib/fs.scandir@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" - integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== - dependencies: - "@nodelib/fs.stat" "2.0.4" - run-parallel "^1.1.9" - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -3165,25 +2355,12 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" - integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== - -"@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" - integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== - dependencies: - "@nodelib/fs.scandir" "2.1.4" - fastq "^1.6.0" - -"@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -3205,34 +2382,17 @@ dependencies: "@opentelemetry/api" "^1.0.0" -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.4.0", "@opentelemetry/api@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" - integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== +"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.4.1": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.7.0.tgz#b139c81999c23e3c8d3c0a7234480e945920fc40" + integrity sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw== "@opentelemetry/core@1.15.2": version "1.15.2" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/semantic-conventions" "1.15.2" - -"@opentelemetry/core@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.9.1.tgz#e343337e1a7bf30e9a6aef3ef659b9b76379762a" - integrity sha512-6/qon6tw2I8ZaJnHAQUUn4BqhTbTNRS0WP8/bA0ynaX+Uzp/DDbd0NS0Cq6TMlh8+mrlsyqDE7mO50nmv2Yvlg== - dependencies: - "@opentelemetry/semantic-conventions" "1.9.1" - -"@opentelemetry/otlp-transformer@^0.35.0": - version "0.35.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.35.1.tgz#d4333b71324b83dbb1b0b3a4cfd769b3e214c6f9" - integrity sha512-c0HXcJ49MKoWSaA49J8PXlVx48CeEFpL0odP6KBkVT+Bw6kAe8JlI3mIezyN05VCDJGtS2I5E6WEsE+DJL1t9A== - dependencies: - "@opentelemetry/core" "1.9.1" - "@opentelemetry/resources" "1.9.1" - "@opentelemetry/sdk-metrics" "1.9.1" - "@opentelemetry/sdk-trace-base" "1.9.1" + "@opentelemetry/semantic-conventions" "1.15.2" "@opentelemetry/otlp-transformer@^0.41.2": version "0.41.2" @@ -3254,14 +2414,6 @@ "@opentelemetry/core" "1.15.2" "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/resources@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.9.1.tgz#5ad3d80ba968a3a0e56498ce4bc82a6a01f2682f" - integrity sha512-VqBGbnAfubI+l+yrtYxeLyOoL358JK57btPMJDd3TCOV3mV5TNBmzvOfmesM4NeTyXuGJByd3XvOHvFezLn3rQ== - dependencies: - "@opentelemetry/core" "1.9.1" - "@opentelemetry/semantic-conventions" "1.9.1" - "@opentelemetry/sdk-logs@0.41.2": version "0.41.2" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.41.2.tgz#c3eeb6793bdfa52351d66e2e66637e433abed672" @@ -3279,15 +2431,6 @@ "@opentelemetry/resources" "1.15.2" lodash.merge "^4.6.2" -"@opentelemetry/sdk-metrics@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.9.1.tgz#babc162a81df9884c16b1e67c2dd26ab478f3080" - integrity sha512-AyhKDcA8NuV7o1+9KvzRMxNbATJ8AcrutKilJ6hWSo9R5utnzxgffV4y+Hp4mJn84iXxkv+CBb99GOJ2A5OMzA== - dependencies: - "@opentelemetry/core" "1.9.1" - "@opentelemetry/resources" "1.9.1" - lodash.merge "4.6.2" - "@opentelemetry/sdk-trace-base@1.15.2": version "1.15.2" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" @@ -3297,50 +2440,32 @@ "@opentelemetry/resources" "1.15.2" "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/sdk-trace-base@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.9.1.tgz#c349491b432a7e0ae7316f0b48b2d454d79d2b84" - integrity sha512-Y9gC5M1efhDLYHeeo2MWcDDMmR40z6QpqcWnPCm4Dmh+RHAMf4dnEBBntIe1dDpor686kyU6JV1D29ih1lZpsQ== - dependencies: - "@opentelemetry/core" "1.9.1" - "@opentelemetry/resources" "1.9.1" - "@opentelemetry/semantic-conventions" "1.9.1" - "@opentelemetry/semantic-conventions@1.15.2": version "1.15.2" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== -"@opentelemetry/semantic-conventions@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz#ad3367684a57879392513479e0a436cb2ac46dad" - integrity sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg== - "@petamoriken/float16@^3.4.7": - version "3.5.11" - resolved "https://registry.yarnpkg.com/@petamoriken/float16/-/float16-3.5.11.tgz#69744fde033692c09dbb02191e0c391f8c857e30" - integrity sha512-aKJaQhvWcP4XRo4eb34VygcqNsE1+Ej5687oexkK+qYWC7tejxaWRkAfE54Ze3xQGnvwXHZ5Ahx6CWq5sS4q7Q== + version "3.8.4" + resolved "https://registry.yarnpkg.com/@petamoriken/float16/-/float16-3.8.4.tgz#cd3c02a7fe39f10ae3dd24ed33bd082053aadd66" + integrity sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ== "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@popperjs/core@2.11.6", "@popperjs/core@^2.11.5": +"@popperjs/core@2.11.6": version "2.11.6" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@rc-component/portal@^1.0.0-6": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.0.tgz#6b94450d2c2b00d50b141bd7a0be23bd96503dbe" - integrity sha512-tbXM9SB1r5FOuZjRCljERFByFiEUcMmCWMXLog/NmgCzlAzreXyf23Vei3ZpSMxSMavzPnhCovfZjZdmxS3d1w== - dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" +"@popperjs/core@^2.11.5": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": +"@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== @@ -3350,17 +2475,16 @@ rc-util "^5.24.4" "@rc-component/trigger@^1.0.4", "@rc-component/trigger@^1.5.0": - version "1.15.6" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-1.15.6.tgz#ccb71f16229e832e15b3869817cbe24f5e59b54c" - integrity sha512-Tl19KaGsShf4yzqxumsXVT4c7j0l20Dxe5hgP5S0HmxyhCg3oKen28ntGavRCIPW7cl7wgsGotntqcIokgDHzg== + version "1.18.2" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-1.18.2.tgz#dc52c4c66fa8aaccaf0710498f2429fc05454e3b" + integrity sha512-jRLYgFgjLEPq3MvS87fIhcfuywFSRDaDrYw1FLku7Cm4esszvzTbA0JBsyacAyLrK9rF3TiHFcvoEDMzoD3CTA== dependencies: - "@babel/runtime" "^7.18.3" + "@babel/runtime" "^7.23.2" "@rc-component/portal" "^1.1.0" classnames "^2.3.2" - rc-align "^4.0.0" rc-motion "^2.0.0" rc-resize-observer "^1.3.1" - rc-util "^5.33.0" + rc-util "^5.38.0" "@react-aria/button@3.6.1": version "3.6.1" @@ -3424,7 +2548,7 @@ "@swc/helpers" "^0.5.0" clsx "^1.1.1" -"@react-aria/focus@3.8.0", "@react-aria/focus@^3.8.0": +"@react-aria/focus@3.8.0": version "3.8.0" resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.8.0.tgz#b292df7e35ed1b57af43f98df8135b00c4667d17" integrity sha512-XuaLFdqf/6OyILifkVJo++5k2O+wlpNvXgsJkRWn/wSmB77pZKURm2MMGiSg2u911NqY+829UrSlpmhCZrc8RA== @@ -3435,96 +2559,39 @@ "@react-types/shared" "^3.14.1" clsx "^1.1.1" -"@react-aria/focus@^3.11.0": - version "3.11.0" - resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.11.0.tgz#8124b2341e8d43af72f3da85b08567bda45421b7" - integrity sha512-yPuWs9bAR9CMfIwyOPm2oXLPF19gNkUqW+ozSPhWbLMTEa8Ma09eHW1br4xbN+4ONOm/dCJsIkxTNPUkiLdQoA== - dependencies: - "@react-aria/interactions" "^3.14.0" - "@react-aria/utils" "^3.15.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - clsx "^1.1.1" - -"@react-aria/focus@^3.13.0", "@react-aria/focus@^3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.14.0.tgz#56b26227102c8492bfe35510033a48fda36f6ec7" - integrity sha512-Xw7PxLT0Cqcz22OVtTZ8+HvurDogn9/xntzoIbVjpRFWzhlYe5WHnZL+2+gIiKf7EZ18Ma9/QsCnrVnvrky/Kw== +"@react-aria/focus@^3.13.0", "@react-aria/focus@^3.15.0", "@react-aria/focus@^3.8.0": + version "3.15.0" + resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.15.0.tgz#acca3cfe94e0ba0c00276e74c6cca06975f75f87" + integrity sha512-nnxRyfqHuAjRwdQ4BpQyZPtGFKZmRU6cnaIb3pqWFCqEyJQensV7MA3TJ4Jhadq67cy1Ji5SYSlr1duBwjoYvw== dependencies: - "@react-aria/interactions" "^3.17.0" - "@react-aria/utils" "^3.19.0" - "@react-types/shared" "^3.19.0" + "@react-aria/interactions" "^3.20.0" + "@react-aria/utils" "^3.22.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" clsx "^1.1.1" -"@react-aria/i18n@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@react-aria/i18n/-/i18n-3.6.0.tgz#0caf4d2173de411839ee55c1d4591aaf3919d6dc" - integrity sha512-FbdoBpMPgO0uldrpn43vCm8Xcveb46AklvUmh+zIUYRSIyIl2TKs5URTnwl9Sb1aloawoHQm2A5kASj5+TCxuA== +"@react-aria/i18n@^3.6.0", "@react-aria/i18n@^3.8.0", "@react-aria/i18n@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@react-aria/i18n/-/i18n-3.9.0.tgz#7aa74e02e74e348de3a34b7599e71ff6920b73ee" + integrity sha512-ebGP/sVG0ZtNF4RNFzs/W01tl7waYpBManh1kKWgA4roDPFt/odkgkDBzKGl+ggBb7TQRHsfUFHuqKsrsMy9TA== dependencies: - "@babel/runtime" "^7.6.2" - "@internationalized/date" "^3.0.1" - "@internationalized/message" "^3.0.9" - "@internationalized/number" "^3.1.1" - "@internationalized/string" "^3.0.0" - "@react-aria/ssr" "^3.3.0" - "@react-aria/utils" "^3.13.3" - "@react-types/shared" "^3.14.1" - -"@react-aria/i18n@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@react-aria/i18n/-/i18n-3.7.0.tgz#c1dfcd7a76a5161cbccbd6980ecf201c9b4d1826" - integrity sha512-PZCWmhO9mJvelwiYlsXLY6W4L2o+oza3xnDx0cZDVqp/Hf+OwMAPHWtZsFRTKdjk4TaOPB/ISc9HknWn6UpY4w== - dependencies: - "@internationalized/date" "^3.1.0" - "@internationalized/message" "^3.1.0" - "@internationalized/number" "^3.2.0" - "@internationalized/string" "^3.1.0" - "@react-aria/ssr" "^3.5.0" - "@react-aria/utils" "^3.15.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-aria/i18n@^3.8.0", "@react-aria/i18n@^3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@react-aria/i18n/-/i18n-3.8.1.tgz#9bb4db74cee7cae1f2eff8132a2eafe4ff605aed" - integrity sha512-ftH3saJlhWaHoHEDb/YjYqP8I4/9t4Ksf0D0kvPDRfRcL98DKUSHZD77+EmbjsmzJInzm76qDeEV0FYl4oj7gg== - dependencies: - "@internationalized/date" "^3.4.0" + "@internationalized/date" "^3.5.0" "@internationalized/message" "^3.1.1" - "@internationalized/number" "^3.2.1" + "@internationalized/number" "^3.4.0" "@internationalized/string" "^3.1.1" - "@react-aria/ssr" "^3.7.1" - "@react-aria/utils" "^3.19.0" - "@react-types/shared" "^3.19.0" + "@react-aria/ssr" "^3.9.0" + "@react-aria/utils" "^3.22.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" -"@react-aria/interactions@^3.11.0": - version "3.11.0" - resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.11.0.tgz#aa6118af58ff443670152393edab97e403d6d359" - integrity sha512-ZjK4m8u6FlV7Q9/1h9P2Ii6j/NwKR3BmTeGeBQssS2i4dV2pJeOPePnGzVQQGG8FzGQ+TcNRvZPXKaU4AlnBjw== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/utils" "^3.13.3" - "@react-types/shared" "^3.14.1" - -"@react-aria/interactions@^3.14.0": - version "3.14.0" - resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.14.0.tgz#d6480985048c009d58b8572428010dc61093cfcc" - integrity sha512-e1Tkr0UTuYFpV21PJrXy7jEY542Vl+C2Fo70oukZ1fN+wtfQkzodsTIzyepXb7kVMGmC34wDisMJUrksVkfY2w== - dependencies: - "@react-aria/utils" "^3.15.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-aria/interactions@^3.16.0", "@react-aria/interactions@^3.17.0": - version "3.17.0" - resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.17.0.tgz#a2f51115963fbc4b82212fb4209879fac88748d1" - integrity sha512-v4BI5Nd8gi8s297fHpgjDDXOyufX+FPHJ31rkMwY6X1nR5gtI0+2jNOL4lh7s+cWzszpA0wpwIrKUPGhhLyUjQ== +"@react-aria/interactions@^3.11.0", "@react-aria/interactions@^3.16.0", "@react-aria/interactions@^3.20.0": + version "3.20.0" + resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.20.0.tgz#8db350541004f50c0479cc52b82597d8248ac5db" + integrity sha512-JCCEyK2Nb4mEHucrgmqhTHTNAEqhsiM07jJmmY22eikxnCQnsEfdwXyg9cgZLG79D5V7jyqVRqOp2OsG7Qx7kQ== dependencies: - "@react-aria/ssr" "^3.7.1" - "@react-aria/utils" "^3.19.0" - "@react-types/shared" "^3.19.0" + "@react-aria/ssr" "^3.9.0" + "@react-aria/utils" "^3.22.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" "@react-aria/menu@3.10.0": @@ -3597,86 +2664,40 @@ "@react-types/shared" "^3.18.1" "@swc/helpers" "^0.5.0" -"@react-aria/overlays@^3.10.1": - version "3.13.0" - resolved "https://registry.yarnpkg.com/@react-aria/overlays/-/overlays-3.13.0.tgz#f5a8cdce4754b4fd487baf9f356a5a3cdd731c12" - integrity sha512-hRZyhAYzrlCcEWQ2k2jP24b0wc5/355Xl5w5FZHRmPeU1U4XlFwKX/eFlBs/li9Sprm1bTDXrCY480Kl6UsKDA== - dependencies: - "@react-aria/focus" "^3.11.0" - "@react-aria/i18n" "^3.7.0" - "@react-aria/interactions" "^3.14.0" - "@react-aria/ssr" "^3.5.0" - "@react-aria/utils" "^3.15.0" - "@react-aria/visually-hidden" "^3.7.0" - "@react-stately/overlays" "^3.5.0" - "@react-types/button" "^3.7.1" - "@react-types/overlays" "^3.7.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-aria/overlays@^3.15.0": - version "3.16.0" - resolved "https://registry.yarnpkg.com/@react-aria/overlays/-/overlays-3.16.0.tgz#47ef9f6876e2a9d3d596771b47c3a4ae6ade4622" - integrity sha512-jclyCqs1U4XqDA1DAdZaiijKtHLVZ78FV0+IzL4QQfrvzCPC+ba+MC8pe/tw8dMQzXBSnTx/IEqOHu07IwrESQ== - dependencies: - "@react-aria/focus" "^3.14.0" - "@react-aria/i18n" "^3.8.1" - "@react-aria/interactions" "^3.17.0" - "@react-aria/ssr" "^3.7.1" - "@react-aria/utils" "^3.19.0" - "@react-aria/visually-hidden" "^3.8.3" - "@react-stately/overlays" "^3.6.1" - "@react-types/button" "^3.7.4" - "@react-types/overlays" "^3.8.1" - "@react-types/shared" "^3.19.0" +"@react-aria/overlays@^3.10.1", "@react-aria/overlays@^3.15.0": + version "3.19.0" + resolved "https://registry.yarnpkg.com/@react-aria/overlays/-/overlays-3.19.0.tgz#0568d808c61e923174e896fc342a1529538da545" + integrity sha512-VN5GkB8+uZ2cfXljBtkqmrsAhBdGoj4un/agH0Qyihi2dazsMeafczSNnqzbpVgB4Zt2UHPJUkKwihgzXRxJJA== + dependencies: + "@react-aria/focus" "^3.15.0" + "@react-aria/i18n" "^3.9.0" + "@react-aria/interactions" "^3.20.0" + "@react-aria/ssr" "^3.9.0" + "@react-aria/utils" "^3.22.0" + "@react-aria/visually-hidden" "^3.8.7" + "@react-stately/overlays" "^3.6.4" + "@react-types/button" "^3.9.1" + "@react-types/overlays" "^3.8.4" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" -"@react-aria/selection@^3.10.1": - version "3.13.1" - resolved "https://registry.yarnpkg.com/@react-aria/selection/-/selection-3.13.1.tgz#770c5ff2ace1db397c09576deb39c576c0fede30" - integrity sha512-YI5mFkk3JI3Ec01SzyBFGrdPInkoW5B0AavwLkN5QtehBUgdw9A1gPKADW4tiLfKUOl0rkqstP13n+v/GcBoTg== - dependencies: - "@react-aria/focus" "^3.11.0" - "@react-aria/i18n" "^3.7.0" - "@react-aria/interactions" "^3.14.0" - "@react-aria/utils" "^3.15.0" - "@react-stately/collections" "^3.6.0" - "@react-stately/selection" "^3.12.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-aria/selection@^3.16.0": - version "3.16.1" - resolved "https://registry.yarnpkg.com/@react-aria/selection/-/selection-3.16.1.tgz#7fbad204a5049952e9637b0edda8be6dc947c085" - integrity sha512-mOoAeNjq23H5p6IaeoyLHavYHRXOuNUlv8xO4OzYxIEnxmAvk4PCgidGLFYrr4sloftUMgTTL3LpCj21ylBS9A== - dependencies: - "@react-aria/focus" "^3.14.0" - "@react-aria/i18n" "^3.8.1" - "@react-aria/interactions" "^3.17.0" - "@react-aria/utils" "^3.19.0" - "@react-stately/collections" "^3.10.0" - "@react-stately/selection" "^3.13.3" - "@react-types/shared" "^3.19.0" +"@react-aria/selection@^3.10.1", "@react-aria/selection@^3.16.0": + version "3.17.2" + resolved "https://registry.yarnpkg.com/@react-aria/selection/-/selection-3.17.2.tgz#74b798344df1eb90e3fdae9bc880c0488468ae3b" + integrity sha512-AXXY3eOIWnITabMn6c0bpLPXkSX7040LOZU+7pQgtZJwDdZorLuKw4i7WS5i71LcV71ywG4mtqc9mOb/GfhUbg== + dependencies: + "@react-aria/focus" "^3.15.0" + "@react-aria/i18n" "^3.9.0" + "@react-aria/interactions" "^3.20.0" + "@react-aria/utils" "^3.22.0" + "@react-stately/selection" "^3.14.1" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" -"@react-aria/ssr@^3.2.0", "@react-aria/ssr@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.5.0.tgz#40c1270a75868185f72a88cafe37bd1392f690cb" - integrity sha512-h0MJdSWOd1qObLnJ8mprU31wI8tmKFJMuwT22MpWq6psisOOZaga6Ml4u6Ee6M6duWWISjXvqO4Sb/J0PBA+nQ== - dependencies: - "@swc/helpers" "^0.4.14" - -"@react-aria/ssr@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.3.0.tgz#25e81daf0c7a270a4a891159d8d984578e4512d8" - integrity sha512-yNqUDuOVZIUGP81R87BJVi/ZUZp/nYOBXbPsRe7oltJOfErQZD+UezMpw4vM2KRz18cURffvmC8tJ6JTeyDtaQ== - dependencies: - "@babel/runtime" "^7.6.2" - -"@react-aria/ssr@^3.7.0", "@react-aria/ssr@^3.7.1": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.7.1.tgz#11d0fac17e50028459aad325c2d093dbb2960f68" - integrity sha512-ovVPSD1WlRpZHt7GI9DqJrWG3OIYS+NXQ9y5HIewMJpSe+jPQmMQfyRmgX4EnvmxSlp0u04Wg/7oItcoSIb/RA== +"@react-aria/ssr@^3.2.0", "@react-aria/ssr@^3.3.0", "@react-aria/ssr@^3.7.0", "@react-aria/ssr@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.9.0.tgz#457310129e1447b09d2f4aa2fdd62ab0e668d88c" + integrity sha512-Bz6BqP6ZorCme9tSWHZVmmY+s7AU8l6Vl2NUYmBzezD//fVHHfFo4lFBn5tBuAaJEm3AuCLaJQ6H2qhxNSb7zg== dependencies: "@swc/helpers" "^0.5.0" @@ -3702,96 +2723,35 @@ "@swc/helpers" "^0.5.0" clsx "^1.1.1" -"@react-aria/utils@^3.13.3": - version "3.13.3" - resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.13.3.tgz#1b27912e4630f0db6a7b39eb1013f6c4f710075c" - integrity sha512-wqjGNFX4TrXriUU1gvCaoqRhuckdoYogUWN0iyQRkTmzvb7H/NNzQzHou5ggWAdts/NzJUInwKarBWM9hCZZbg== +"@react-aria/utils@^3.13.3", "@react-aria/utils@^3.18.0", "@react-aria/utils@^3.22.0": + version "3.22.0" + resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.22.0.tgz#962a45ae95fdc21de7f22dda68253b0fb2470d06" + integrity sha512-Qi/m65GFFljXA/ayj1m5g3KZdgbZY3jacSSqD5vNUOEGiKsn4OQcsw8RfC2c0SgtLV1hLzsfvFI1OiryPlGCcw== dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/ssr" "^3.3.0" - "@react-stately/utils" "^3.5.1" - "@react-types/shared" "^3.14.1" - clsx "^1.1.1" - -"@react-aria/utils@^3.15.0": - version "3.15.0" - resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.15.0.tgz#a836674dd40ec7f15e9f7a69b1a14f19b1ee42e6" - integrity sha512-aJZBG++iz1UwTW5gXFaHicKju4p0MPhAyBTcf2awHYWeTUUslDjJcEnNg7kjBYZBOrOSlA2rAt7/7C5CCURQPg== - dependencies: - "@react-aria/ssr" "^3.5.0" - "@react-stately/utils" "^3.6.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - clsx "^1.1.1" - -"@react-aria/utils@^3.18.0", "@react-aria/utils@^3.19.0": - version "3.19.0" - resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.19.0.tgz#ee7fe77b37181fdf09fdd2e50ae818e4be858f47" - integrity sha512-5GXqTCrUQtr78aiLVHZoeeGPuAxO4lCM+udWbKpSCh5xLfCZ7zFlZV9Q9FS0ea+IQypUcY8ngXCLsf22nSu/yg== - dependencies: - "@react-aria/ssr" "^3.7.1" - "@react-stately/utils" "^3.7.0" - "@react-types/shared" "^3.19.0" + "@react-aria/ssr" "^3.9.0" + "@react-stately/utils" "^3.9.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" clsx "^1.1.1" -"@react-aria/visually-hidden@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.4.1.tgz#cd87eece49eddc89e93b9616741d6d5a6e738785" - integrity sha512-dx7OSdnQvvR8awpxwiHHBdk0N3UGyGEBI17vogmO09685J+MRW8UuJuXRNl4Eg5FnWoFHxWRnmHmXin7fdGU+w== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/interactions" "^3.11.0" - "@react-aria/utils" "^3.13.3" - "@react-types/shared" "^3.14.1" - clsx "^1.1.1" - -"@react-aria/visually-hidden@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.7.0.tgz#4056b8bb33f30ca8c5192dfcf706ac8bb2beb61a" - integrity sha512-v/0ujJ67H6LjwY8J7mIGPVB1K8suBArLV+w8UGdX/wFXRL7H4r2fiqlrwAElWSmNbhDQl5BDm/Zh/ub9jB9yzA== - dependencies: - "@react-aria/interactions" "^3.14.0" - "@react-aria/utils" "^3.15.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - clsx "^1.1.1" - -"@react-aria/visually-hidden@^3.8.2", "@react-aria/visually-hidden@^3.8.3": - version "3.8.3" - resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.8.3.tgz#09db58c639a8c2baed23a66a56f90527d41ba856" - integrity sha512-Ln3rqUnPF/UiiPjj8Xjc5FIagwNvG16qtAR2Diwnsju+X9o2xeDEZhN/5fg98PxH2JBS3IvtsmMZRzPT9mhpmg== +"@react-aria/visually-hidden@^3.4.1", "@react-aria/visually-hidden@^3.8.2", "@react-aria/visually-hidden@^3.8.7": + version "3.8.7" + resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.8.7.tgz#059699c70cc354ccb3699151b09071b3fc43fa82" + integrity sha512-OuIGMVQIt7GC43h4x35BgkZid8lhoPu7Xz4TQRP8nvOJWb1lH7ehrRRuGdUsK3y90nwpxTdNdg4DILblg+VaLw== dependencies: - "@react-aria/interactions" "^3.17.0" - "@react-aria/utils" "^3.19.0" - "@react-types/shared" "^3.19.0" + "@react-aria/interactions" "^3.20.0" + "@react-aria/utils" "^3.22.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" - clsx "^1.1.1" -"@react-stately/collections@^3.10.0", "@react-stately/collections@^3.9.0": - version "3.10.0" - resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.10.0.tgz#5c772e5eae8d21ae8d1c023fb9b9ae6fa35b1092" - integrity sha512-PyJEFmt9X0kDMF7D4StGnTdXX1hgyUcTXvvXU2fEw6OyXLtmfWFHmFARRtYbuelGKk6clmJojYmIEds0k8jdww== +"@react-stately/collections@^3.10.3", "@react-stately/collections@^3.4.3", "@react-stately/collections@^3.9.0": + version "3.10.3" + resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.10.3.tgz#c80bd30df3bf5d2a9c6fdf25f6313c5187d0154d" + integrity sha512-fA28HIApAIz9sNGeOVXZJPgV5Kig6M72KI1t9sUbnRUr9Xq9OMJTR6ElDMXNe0iTeZffRFDOPYyqnX9zkxof6Q== dependencies: - "@react-types/shared" "^3.19.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" -"@react-stately/collections@^3.4.3": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.4.3.tgz#aaff67e697006a7c38dfb639180b79df4b202b46" - integrity sha512-xK3KPBCFcptpbTH/gsBT2bqVdGFruYvznBvUwzwgjb5x+vF2hXuIfaClD3/g6NckIo11MWpYGKO6iiPb1ytKeg== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-types/shared" "^3.14.1" - -"@react-stately/collections@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.6.0.tgz#4fbecbbbb354badbc46618262e91743754257673" - integrity sha512-znkaqCPo7F1yyzEKDAB5MpX1Vw5UHcUQhDNrys5YOqAkX6/G/AChnBz0B63UxS3fjyqgnuJylRRmUp9nTqO21w== - dependencies: - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - "@react-stately/menu@3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@react-stately/menu/-/menu-3.4.1.tgz#47f23996927ffa605d725e68902e27ef848fe27a" @@ -3814,248 +2774,103 @@ "@react-types/shared" "^3.18.1" "@swc/helpers" "^0.5.0" -"@react-stately/menu@^3.4.1": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-stately/menu/-/menu-3.5.0.tgz#04fda461dc3971bc84bf49436625b3e044bca3bc" - integrity sha512-JL6TcT+SbYdlxNLOS84SXp6njDNZuXfkt05o4rS51evmjM2+hlYaB9+yUMqrCb/J2nW7vVAg51TDAhLgmGTYKg== - dependencies: - "@react-stately/overlays" "^3.5.0" - "@react-stately/utils" "^3.6.0" - "@react-types/menu" "^3.8.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-stately/menu@^3.5.3": - version "3.5.4" - resolved "https://registry.yarnpkg.com/@react-stately/menu/-/menu-3.5.4.tgz#3c4a6383af97c6c5ab984f4c545003c7ab61fb9e" - integrity sha512-+Q71fMDhMM1iARPFtwqpXY/8qkb0dN4PBJbcjwjGCumGs+ja2YbZxLBHCP0DYBElS9l6m3ssF47RKNMtF/Oi5w== - dependencies: - "@react-stately/overlays" "^3.6.1" - "@react-stately/utils" "^3.7.0" - "@react-types/menu" "^3.9.3" - "@react-types/shared" "^3.19.0" - "@swc/helpers" "^0.5.0" - -"@react-stately/overlays@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@react-stately/overlays/-/overlays-3.4.1.tgz#e6b095c7dae96b2c969ed7e029ab5d9f74149051" - integrity sha512-3LybriKQfpR85QAdm5soDUD4bo9W4TiZpSbxXqazXKno8zLOy9vGI3lcQHC1Gpcf4E+Q+Hq5y3qFcRFicp/j7A== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-stately/utils" "^3.5.1" - "@react-types/overlays" "^3.6.3" - -"@react-stately/overlays@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-stately/overlays/-/overlays-3.5.0.tgz#3c33a6f678f728497c94729e8a861ddf1e8704bb" - integrity sha512-r+U/G0Y4tCfI5wyBeIu+hmcZVRN8ChoK2zM1srPH9nDKsijQard2goX+9YmKng2LJ01Re/P6F8S8jYbpfEdLfQ== - dependencies: - "@react-stately/utils" "^3.6.0" - "@react-types/overlays" "^3.7.0" - "@swc/helpers" "^0.4.14" - -"@react-stately/overlays@^3.6.0", "@react-stately/overlays@^3.6.1": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@react-stately/overlays/-/overlays-3.6.1.tgz#ad2e398d04b5974907f929b6cdb78774f5e36c39" - integrity sha512-c/Mda4ZZmFO4e3XZFd7kqt5wuh6Q/7wYJ+0oG59MfDoQstFwGcJTUnx7S8EUMujbocIOCeOmVPA1eE3DNPC2/A== - dependencies: - "@react-stately/utils" "^3.7.0" - "@react-types/overlays" "^3.8.1" - "@swc/helpers" "^0.5.0" - -"@react-stately/selection@^3.12.0": - version "3.12.0" - resolved "https://registry.yarnpkg.com/@react-stately/selection/-/selection-3.12.0.tgz#feb5ad753ea93870566f2c0b07f0387b690bd860" - integrity sha512-qgUaPwqtAl7YaZxxGdb55ZaVuMB1rG+Vr+9fgG8dPtDYCNaPeIlg7ndC4ylzDhCWIx8D5qZotcrqCA4+93TwdA== +"@react-stately/menu@^3.4.1", "@react-stately/menu@^3.5.3": + version "3.5.7" + resolved "https://registry.yarnpkg.com/@react-stately/menu/-/menu-3.5.7.tgz#3232598399b4baebfc577d5f56b4bd5570f400c2" + integrity sha512-bzTmAqzcMNatvyruWlvOdZSmMhz3+mkdxtqaZzYHq+DpR6ka57lIRj8dBnZWQGwV3RypMZfz+X6aIX4kruGVbw== dependencies: - "@react-stately/collections" "^3.6.0" - "@react-stately/utils" "^3.6.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-stately/selection@^3.13.3": - version "3.13.3" - resolved "https://registry.yarnpkg.com/@react-stately/selection/-/selection-3.13.3.tgz#ee4b457753b5631f90573fd298fa1bac2c4f3857" - integrity sha512-+CmpZpyIXfbxEwd9eBvo5Jatc2MNX7HinBcW3X8GfvqNzkbgOXETsmXaW6jlKJekvLLE13Is78Ob8NNzZVxQYg== - dependencies: - "@react-stately/collections" "^3.10.0" - "@react-stately/utils" "^3.7.0" - "@react-types/shared" "^3.19.0" + "@react-stately/overlays" "^3.6.4" + "@react-types/menu" "^3.9.6" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" -"@react-stately/toggle@^3.4.1": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-stately/toggle/-/toggle-3.5.0.tgz#fee5a29d7699e43867c52981834af5393f47c1c4" - integrity sha512-vKwLLkFsiIve4pXIQC/dqLAz7Z+qtzJ8+D00EXXO1Nf8YHcyIMDkTmi3NTM8Qtvmt4xX2hbJFiPDF6WvF6mBIg== - dependencies: - "@react-stately/utils" "^3.6.0" - "@react-types/checkbox" "^3.4.2" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-stately/toggle@^3.6.0": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@react-stately/toggle/-/toggle-3.6.1.tgz#3c14b0015aa7e306920bc14fd1a44932fbb4efb2" - integrity sha512-UUWtuI6gZlX6wpF9/bxBikjyAW1yQojRPCJ4MPkjMMBQL0iveAm3WEQkXRLNycEiOCeoaVFBwAd1L9h9+fuCFg== +"@react-stately/overlays@^3.4.1", "@react-stately/overlays@^3.6.0", "@react-stately/overlays@^3.6.4": + version "3.6.4" + resolved "https://registry.yarnpkg.com/@react-stately/overlays/-/overlays-3.6.4.tgz#1d0d974413fa3f13d97eec2cac5b48c49978d1a0" + integrity sha512-tHEaoAGpE9dSnsskqLPVKum59yGteoSqsniTopodM+miQozbpPlSjdiQnzGLroy5Afx5OZYClE616muNHUILXA== dependencies: - "@react-stately/utils" "^3.7.0" - "@react-types/checkbox" "^3.5.0" - "@react-types/shared" "^3.19.0" + "@react-stately/utils" "^3.9.0" + "@react-types/overlays" "^3.8.4" "@swc/helpers" "^0.5.0" -"@react-stately/tree@^3.3.3": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-stately/tree/-/tree-3.5.0.tgz#1d0dffd93c17b004953c27817be1ae2da9f96e5b" - integrity sha512-5+MzMQUFq3+lbGkZC0SlcIDrYmPvxBKuC8xL5W6SuFekbrrxrS6IJexRe4QulaaAliDpX2/9DVZTt38eVfyf0A== - dependencies: - "@react-stately/collections" "^3.6.0" - "@react-stately/selection" "^3.12.0" - "@react-stately/utils" "^3.6.0" - "@react-types/shared" "^3.17.0" - "@swc/helpers" "^0.4.14" - -"@react-stately/tree@^3.7.0": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@react-stately/tree/-/tree-3.7.1.tgz#67ab6bbf47f86005ca584be3333c84a106ef2abd" - integrity sha512-D0BWcLTRx7EOTdAJCgYV6zm18xpNDxmv4meKJ/WmYSFq1bkHPN75NLv7VPf5Uvsm66xshbO/B3A4HB2/ag1yPA== +"@react-stately/selection@^3.14.1": + version "3.14.1" + resolved "https://registry.yarnpkg.com/@react-stately/selection/-/selection-3.14.1.tgz#798b4fbfda940778ae1dc1f2d2390cee97cce2c6" + integrity sha512-96/CerrB6yH4Ad9FkzBzyVerSPjcIj1NBTWTFHo1N+oHECvyGsDxZl7Y4LQR++teFK66FhX5KjCJQGae4IZd6A== dependencies: - "@react-stately/collections" "^3.10.0" - "@react-stately/selection" "^3.13.3" - "@react-stately/utils" "^3.7.0" - "@react-types/shared" "^3.19.0" + "@react-stately/collections" "^3.10.3" + "@react-stately/utils" "^3.9.0" + "@react-types/shared" "^3.22.0" "@swc/helpers" "^0.5.0" -"@react-stately/utils@^3.5.0", "@react-stately/utils@^3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.6.0.tgz#f273e7fcb348254347d2e88c8f0c45571060c207" - integrity sha512-rptF7iUWDrquaYvBAS4QQhOBQyLBncDeHF03WnHXAxnuPJXNcr9cXJtjJPGCs036ZB8Q2hc9BGG5wNyMkF5v+Q== - dependencies: - "@swc/helpers" "^0.4.14" - -"@react-stately/utils@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.5.1.tgz#502de762e5d33e892347c5f58053674e06d3bc92" - integrity sha512-INeQ5Er2Jm+db8Py4upKBtgfzp3UYgwXYmbU/XJn49Xw27ktuimH9e37qP3bgHaReb5L3g8IrGs38tJUpnGPHA== - dependencies: - "@babel/runtime" "^7.6.2" - -"@react-stately/utils@^3.7.0": +"@react-stately/toggle@^3.4.1", "@react-stately/toggle@^3.6.0": version "3.7.0" - resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.7.0.tgz#ea99c2c4b5fba7e5079434a1de1ef53fbb21f6a8" - integrity sha512-VbApRiUV2rhozOfk0Qj9xt0qjVbQfLTgAzXLdrfeZSBnyIgo1bFRnjDpnDZKZUUCeGQcJJI03I9niaUtY+kwJQ== + resolved "https://registry.yarnpkg.com/@react-stately/toggle/-/toggle-3.7.0.tgz#abe2f08f37a0f41e6513d4fde3d46f49500bb5cc" + integrity sha512-TRksHkCJk/Xogq4181g3CYgJf+EfsJCqX5UZDSw1Z1Kgpvonjmdf6FAfQfCh9QR2OuXUL6hOLUDVLte5OPI+5g== dependencies: + "@react-stately/utils" "^3.9.0" + "@react-types/checkbox" "^3.6.0" "@swc/helpers" "^0.5.0" -"@react-types/button@^3.6.1": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.6.1.tgz#0bc75fe4129966673cf239df7a7aea83b6c68585" - integrity sha512-F7m3/MVmzChkBqD5gO7rIglPRHY6KZg/RaU8f8VqZuEOAHuQ1CtTEfpc6r9artBSs2Gdw7yNWxfCI2dP95lYow== - dependencies: - "@react-types/shared" "^3.14.1" - -"@react-types/button@^3.7.1": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.7.1.tgz#a51d2617a593d9862c72306b3bf0c4b5bff4793d" - integrity sha512-c+8xjmqWSjI5/mEHVLbVSp0eh/z2UU8Ga+wqjbEUZUjm8uopYj1PaCAwZ7YgcAebyQrL/21GyjK6tFHKzuUdJQ== - dependencies: - "@react-types/shared" "^3.17.0" - -"@react-types/button@^3.7.3", "@react-types/button@^3.7.4": +"@react-stately/tree@^3.3.3", "@react-stately/tree@^3.7.0": version "3.7.4" - resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.7.4.tgz#97aae5f1e8ffdb7c252643f82b0bf471c605191e" - integrity sha512-y1JOnJ3pqg2ezZz/fdwMMToPj+8fgj/He7z1NRWtIy1/I7HP+ilSK6S/MLO2jRsM2QfCq8KSw5MQEZBPiPWsjw== - dependencies: - "@react-types/shared" "^3.19.0" - -"@react-types/checkbox@^3.4.2": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@react-types/checkbox/-/checkbox-3.4.2.tgz#6089e9ef2d023415a5f871e312f30bae54143ba5" - integrity sha512-/NWFCEQLvVgo25afPt2jv4syxYvZeY/D/n2Y92IGtoNV4akdz4AuQ65+1X+JOhQc/ZbAblWw5fFWUZoQs3CLZg== - dependencies: - "@react-types/shared" "^3.17.0" - -"@react-types/checkbox@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-types/checkbox/-/checkbox-3.5.0.tgz#64cb3bf45eae7da731a2a191fbecdbe50589df7a" - integrity sha512-fCisTdqFKkz7FvxNoexXIiVsTBt0ZwIyeIZz/S41M6hzIZM38nKbh6yS/lveQ+/877Dn7+ngvbpJ8QYnXYVrIQ== + resolved "https://registry.yarnpkg.com/@react-stately/tree/-/tree-3.7.4.tgz#57cc57863837092f13b7a3887e1b5c56330b5cac" + integrity sha512-0yvVODBS8WnSivLFX5ccEjCl2NA/8lbEt1E48wVcY1xcXgISNpw5MSGK5jC6YrtJPIqVolQIkNSbMreXGBktIg== dependencies: - "@react-types/shared" "^3.19.0" - -"@react-types/dialog@^3.4.3": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@react-types/dialog/-/dialog-3.5.0.tgz#51d942ba377ac1483c90db69e84bfdc3d5f65129" - integrity sha512-QsHqAK8zE4QSCQTJcRm/e6vweSE8S62o4GGvm+e+crMro/doA4it1Y4udE94Yy3WyDQEMFxyfd2P1Q6bI9JuyQ== - dependencies: - "@react-types/overlays" "^3.7.0" - "@react-types/shared" "^3.17.0" - -"@react-types/dialog@^3.5.3": - version "3.5.4" - resolved "https://registry.yarnpkg.com/@react-types/dialog/-/dialog-3.5.4.tgz#075d550a442325ee76919d3e144538181ff08e9e" - integrity sha512-WCEkUf93XauGaPaF1efTJ8u04Z5iUgmmzRbFnGLrske7rQJYfryP3+26zCxtKKlOTgeFORq5AHeH6vqaMKOhhg== - dependencies: - "@react-types/overlays" "^3.8.1" - "@react-types/shared" "^3.19.0" + "@react-stately/collections" "^3.10.3" + "@react-stately/selection" "^3.14.1" + "@react-stately/utils" "^3.9.0" + "@react-types/shared" "^3.22.0" + "@swc/helpers" "^0.5.0" -"@react-types/menu@^3.7.1": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@react-types/menu/-/menu-3.7.1.tgz#79955fc63f3bb7c867594bcbead5dd37dc47848d" - integrity sha512-5a+vfu+oX+bMl4La4pzy6hx3pzBors1Kxcy3gykOUPQ/1zWQKnv8bhcqXTVtUQ9TItg+N6L4axXH/1VPvnzAJg== +"@react-stately/utils@^3.5.0", "@react-stately/utils@^3.5.1", "@react-stately/utils@^3.7.0", "@react-stately/utils@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.9.0.tgz#9cb2c8eea5dd1b58256ecb436b963c01526bae37" + integrity sha512-yPKFY1F88HxuZ15BG2qwAYxtpE4HnIU0Ofi4CuBE0xC6I8mwo4OQjDzi+DZjxQngM9D6AeTTD6F1V8gkozA0Gw== dependencies: - "@react-types/overlays" "^3.6.3" - "@react-types/shared" "^3.14.1" + "@swc/helpers" "^0.5.0" -"@react-types/menu@^3.8.0": - version "3.8.0" - resolved "https://registry.yarnpkg.com/@react-types/menu/-/menu-3.8.0.tgz#578d4697caa838e72b2652a81f1241a9cb7815cc" - integrity sha512-1nwGUwKNHJf60vOsg7p48NPQIzMsSprxw8VXfStr8eE5uU4vvKfVNQNUgvpkRmHmel8BrYdh1WnERXJJ3yKUgQ== +"@react-types/button@^3.6.1", "@react-types/button@^3.7.3", "@react-types/button@^3.9.1": + version "3.9.1" + resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.9.1.tgz#eb54745133bdaad345d8d589021b67ef2882e1c5" + integrity sha512-bf9iTar3PtqnyV9rA+wyFyrskZKhwmOuOd/ifYIjPs56YNVXWH5Wfqj6Dx3xdFBgtKx8mEVQxVhoX+WkHX+rtw== dependencies: - "@react-types/overlays" "^3.7.0" - "@react-types/shared" "^3.17.0" + "@react-types/shared" "^3.22.0" -"@react-types/menu@^3.9.2", "@react-types/menu@^3.9.3": - version "3.9.3" - resolved "https://registry.yarnpkg.com/@react-types/menu/-/menu-3.9.3.tgz#5a03fb4545bc766fc3fcccaddaa4cc33561d3902" - integrity sha512-0dgIIM9z3hzjFltT+1/L8Hj3oDEcdYkexQhaA+jv6xBHUI5Bqs4SaJAeSGrGz5u6tsrHBPEgf/TLk9Dg9c7XMA== +"@react-types/checkbox@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@react-types/checkbox/-/checkbox-3.6.0.tgz#ba702be25555c1520f78be39c8260354638792b6" + integrity sha512-vgbuJzQpVCNT5AZWV0OozXCnihqrXxoZKfJFIw0xro47pT2sn3t5UC4RA9wfjDGMoK4frw1K/4HQLsQIOsPBkw== dependencies: - "@react-types/overlays" "^3.8.1" - "@react-types/shared" "^3.19.0" + "@react-types/shared" "^3.22.0" -"@react-types/overlays@^3.6.3": - version "3.6.3" - resolved "https://registry.yarnpkg.com/@react-types/overlays/-/overlays-3.6.3.tgz#ba2204dd4be1948e8d2ab38995eb51d81dfd498f" - integrity sha512-89gqlEiY/b8HdEK/y074Ahsfvv5DmbhZP85ln6ORG62orwX2J0UrDYBhHDLmX96fqZ9FoOCb+Dez0z22R3sxew== +"@react-types/dialog@^3.4.3", "@react-types/dialog@^3.5.3": + version "3.5.7" + resolved "https://registry.yarnpkg.com/@react-types/dialog/-/dialog-3.5.7.tgz#3fd93875ff317d6014e814b6e1a2abb87272a1ef" + integrity sha512-geYoqAyQaTLG43AaXdMUVqZXYgkSifrD9cF7lR2kPAT0uGFv0YREi6ieU+aui8XJ83EW0xcxP+EPWd2YkN4D4w== dependencies: - "@react-types/shared" "^3.14.1" + "@react-types/overlays" "^3.8.4" + "@react-types/shared" "^3.22.0" -"@react-types/overlays@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@react-types/overlays/-/overlays-3.7.0.tgz#22dcd1bbc1f8e17b0d7a757414c50ce580ae0d26" - integrity sha512-LstucncZ8dM+xJYEijI1V6jGH20w5XO/T60r7JTrgQElMC86phPeoWkMTN4c2lsRikybolDbvXL6XsF76YO56A== +"@react-types/menu@^3.7.1", "@react-types/menu@^3.9.2", "@react-types/menu@^3.9.6": + version "3.9.6" + resolved "https://registry.yarnpkg.com/@react-types/menu/-/menu-3.9.6.tgz#1b36842cbdb4590dfff78437316aec4a3f47b1f6" + integrity sha512-w/RbFInOf4nNayQDv5c2L8IMJbcFOkBhsT3xvvpTy+CHvJcQdjggwaV1sRiw7eF/PwB81k2CwigmidUzHJhKDg== dependencies: - "@react-types/shared" "^3.17.0" + "@react-types/overlays" "^3.8.4" + "@react-types/shared" "^3.22.0" -"@react-types/overlays@^3.8.0", "@react-types/overlays@^3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@react-types/overlays/-/overlays-3.8.1.tgz#3f189daa1a97406e912fddfe98927151bae3ed79" - integrity sha512-aDI/K3E2XACkey8SCBmAerLhYSUFa8g8tML4SoQbfEJPRj+jJztbHbg9F7b3HKDUk4ZOjcUdQRfz1nFHORdbtQ== +"@react-types/overlays@^3.6.3", "@react-types/overlays@^3.8.0", "@react-types/overlays@^3.8.4": + version "3.8.4" + resolved "https://registry.yarnpkg.com/@react-types/overlays/-/overlays-3.8.4.tgz#a538f6f2fb9826f1da78d3b4f0f6326a709ce37d" + integrity sha512-pfgNlQnbF6RB/R2oSxyqAP3Uzz0xE/k5q4n5gUeCDNLjY5qxFHGE8xniZZ503nZYw6VBa9XMN1efDOKQyeiO0w== dependencies: - "@react-types/shared" "^3.19.0" - -"@react-types/shared@^3.13.1", "@react-types/shared@^3.17.0": - version "3.17.0" - resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.17.0.tgz#b7c5e318664aadb315d305a27dd2a209d1837d95" - integrity sha512-1SNZ/RhVrrQ1e6yE0bPV7d5Sfp+Uv0dfUEhwF9MAu2v5msu7AMewnsiojKNA0QA6Ing1gpDLjHCxtayQfuxqcg== + "@react-types/shared" "^3.22.0" -"@react-types/shared@^3.14.1": - version "3.14.1" - resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.14.1.tgz#8fe25f729426e8043054e442eb5392364200e028" - integrity sha512-yPPgVRWWanXqbdxFTgJmVwx0JlcnEK3dqkKDIbVk6mxAHvEESI9+oDnHvO8IMHqF+GbrTCzVtAs0zwhYI/uHJA== - -"@react-types/shared@^3.18.1", "@react-types/shared@^3.19.0": - version "3.19.0" - resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.19.0.tgz#060e547d6e8c3ec84043d62f61cada1a00df1348" - integrity sha512-h852l8bWhqUxbXIG8vH3ab7gE19nnP3U1kuWf6SNSMvgmqjiRN9jXKPIFxF/PbfdvnXXm0yZSgSMWfUCARF0Cg== +"@react-types/shared@^3.13.1", "@react-types/shared@^3.14.1", "@react-types/shared@^3.18.1", "@react-types/shared@^3.22.0": + version "3.22.0" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.22.0.tgz#70f85aad46cd225f7fcb29f1c2b5213163605074" + integrity sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA== "@sentry/browser@6.19.7": version "6.19.7" @@ -4109,29 +2924,24 @@ "@sentry/types" "6.19.7" tslib "^1.9.3" -"@sinclair/typebox@^0.25.16": - version "0.25.24" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" - integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" - integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^2.0.0" + "@sinonjs/commons" "^3.0.0" "@swc/core-darwin-arm64@1.3.75": version "1.3.75" @@ -4199,17 +3009,10 @@ "@swc/core-win32-ia32-msvc" "1.3.75" "@swc/core-win32-x64-msvc" "1.3.75" -"@swc/helpers@^0.4.14": - version "0.4.14" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" - integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== - dependencies: - tslib "^2.4.0" - -"@swc/helpers@^0.5.0": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" - integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== +"@swc/helpers@^0.5.0", "@swc/helpers@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.3.tgz#98c6da1e196f5f08f977658b80d6bd941b5f294f" + integrity sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A== dependencies: tslib "^2.4.0" @@ -4221,18 +3024,18 @@ "@jest/create-cache-key-function" "^27.4.2" jsonc-parser "^3.2.0" -"@testing-library/dom@>=7", "@testing-library/dom@^8.0.0": - version "8.11.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.1.tgz#03fa2684aa09ade589b460db46b4c7be9fc69753" - integrity sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg== +"@testing-library/dom@>=7", "@testing-library/dom@^9.0.0": + version "9.3.3" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.3.tgz#108c23a5b0ef51121c26ae92eb3179416b0434f5" + integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" - aria-query "^5.0.0" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" chalk "^4.1.0" dom-accessibility-api "^0.5.9" - lz-string "^1.4.4" + lz-string "^1.5.0" pretty-format "^27.0.2" "@testing-library/jest-dom@^5.16.5": @@ -4250,14 +3053,14 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^12.1.4": - version "12.1.5" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" - integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== +"@testing-library/react@^14.0.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.1.0.tgz#01d64915111db99b50f8361d51d7217606805989" + integrity sha512-hcvfZEEyO0xQoZeHmUbuMs7APJCGELpilL7bY+BaJaMP57aWc6q1etFwScnoZDheYjk4ESdlzPdQ33IbsKAK/A== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" - "@types/react-dom" "<18.0.0" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" "@testing-library/user-event@^14.5.1": version "14.5.1" @@ -4285,19 +3088,19 @@ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== "@types/babel__core@^7.1.14": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" - integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + version "7.20.4" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.4.tgz#26a87347e6c6f753b3668398e34496d6d9ac6ac0" + integrity sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" @@ -4306,72 +3109,59 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" - integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== + version "7.6.7" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.7.tgz#a7aebf15c7bc0eb9abd638bdb5c0b8700399c9d0" + integrity sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.0" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.0.tgz#0c888dd70b3ee9eebb6e4f200e809da0076262be" - integrity sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.0.tgz#b9a1efa635201ba9bc850323a8793ee2d36c04a0" - integrity sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg== + version "7.20.4" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.4.tgz#ec2c06fed6549df8bc0eb4615b683749a4a92e1b" + integrity sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA== dependencies: - "@babel/types" "^7.3.0" + "@babel/types" "^7.20.7" "@types/d3-color@*": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.0.tgz#6594da178ded6c7c3842f3cc0ac84b156f12f2d4" - integrity sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA== + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2" + integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A== "@types/d3-interpolate@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz#e7d17fa4a5830ad56fe22ce3b4fac8541a9572dc" - integrity sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c" + integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA== dependencies: "@types/d3-color" "*" "@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" -"@types/eslint@*": - version "7.2.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" - integrity sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/eslint@^8.37.0": - version "8.44.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.2.tgz#0d21c505f98a89b8dd4d37fa162b09da6089199a" - integrity sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg== +"@types/eslint@*", "@types/eslint@^8.37.0": + version "8.44.7" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.7.tgz#430b3cc96db70c81f405e6a08aebdb13869198f5" + integrity sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== - -"@types/estree@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" - integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/glob@^8.0.0": version "8.1.0" @@ -4382,9 +3172,9 @@ "@types/node" "*" "@types/graceful-fs@^4.1.3": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" - integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" @@ -4394,44 +3184,36 @@ integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== "@types/hoist-non-react-statics@^3.3.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== dependencies: "@types/react" "*" hoist-non-react-statics "^3.3.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" - integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*": - version "26.0.20" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307" - integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA== - dependencies: - jest-diff "^26.0.0" - pretty-format "^26.0.0" - -"@types/jest@^29.5.0": - version "29.5.4" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" - integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== +"@types/jest@*", "@types/jest@^29.5.0": + version "29.5.8" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.8.tgz#ed5c256fe2bc7c38b1915ee5ef1ff24a3427e120" + integrity sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -4450,27 +3232,22 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@*": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - -"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/lodash.memoize@^4.1.7": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.7.tgz#aff94ab32813c557cbc1104e127030e3d60a3b27" - integrity sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz#9f8912d39b6e450c0d342a2b74c99d331bf2016b" + integrity sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw== dependencies: "@types/lodash" "*" "@types/lodash@*", "@types/lodash@^4.14.194": - version "4.14.198" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.198.tgz#4d27465257011aedc741a809f1269941fa2c5d4c" - integrity sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg== + version "4.14.201" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.201.tgz#76f47cb63124e806824b6c18463daf3e1d480239" + integrity sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ== "@types/minimatch@^5.1.2": version "5.1.2" @@ -4478,41 +3255,45 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "14.14.22" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" - integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== + version "20.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.0.tgz#bfcdc230583aeb891cf51e73cfdaacdd8deae298" + integrity sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw== + dependencies: + undici-types "~5.26.4" "@types/node@^14.14.31": - version "14.18.58" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.58.tgz#547e64027defb95f34824794574dabf5417bc615" - integrity sha512-Y8ETZc8afYf6lQ/mVp096phIVsgD/GmDxtm3YaPcc+71jmi/J6zdwbwaUU4JvS56mq6aSfbpkcKhQ5WugrWFPw== + version "14.18.63" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" + integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== "@types/node@^18.15.11": - version "18.17.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.14.tgz#a621ad26e7eb076d6846dd3d39557ddf9d89f04b" - integrity sha512-ZE/5aB73CyGqgQULkLG87N9GnyGe5TcQjv34pwS8tfBs1IkCh0ASM69mydb2znqd6v0eX+9Ytvk6oQRqu8T1Vw== + version "18.18.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.9.tgz#5527ea1832db3bba8eb8023ce8497b7d3f299592" + integrity sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== "@types/prop-types@*": - version "15.7.3" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" - integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + version "15.7.10" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.10.tgz#892afc9332c4d62a5ea7e897fe48ed2085bbb08a" + integrity sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A== -"@types/react-dom@<18.0.0": - version "17.0.19" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.19.tgz#36feef3aa35d045cacd5ed60fe0eef5272f19492" - integrity sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ== +"@types/react-dom@^18.0.0": + version "18.2.15" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.15.tgz#921af67f9ee023ac37ea84b1bc0cc40b898ea522" + integrity sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg== dependencies: - "@types/react" "^17" + "@types/react" "*" "@types/react-redux@^7.1.20": - version "7.1.20" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.20.tgz#42f0e61ababb621e12c66c96dda94c58423bd7df" - integrity sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw== + version "7.1.30" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.30.tgz#4f9731cc987eb6285fe2b472231fb42120efc22c" + integrity sha512-i2kqM6YaUwFKduamV6QM/uHbb0eCP8f8ZQ/0yWf+BsAVVsZPRYJ9eeGWZ3uxLfWwwA0SrPRMTPTqsPFkY3HZdA== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" @@ -4537,38 +3318,30 @@ "@types/react" "*" "@types/react-transition-group@^4.4.0": - version "4.4.5" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" - integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + version "4.4.9" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.9.tgz#12a1a1b5b8791067198149867b0823fbace31579" + integrity sha512-ZVNmWumUIh5NhH8aMD9CR2hdW0fNuYInlocZHaZ+dgk/1K49j1w/HoAuK1ki+pgscQrOFRTlXeoURtuzEkV3dg== dependencies: "@types/react" "*" "@types/react@*": - version "17.0.1" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.1.tgz#eb1f1407dea8da3bc741879c1192aa703ab9975b" - integrity sha512-w8t9f53B2ei4jeOqf/gxtc2Sswnc3LBK5s0DyJcg5xd10tMHXts2N31cKjWfH9IC/JvEPa/YF1U4YeP1t4R6HQ== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - -"@types/react@^17": - version "17.0.53" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" - integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== + version "18.2.37" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.37.tgz#0f03af69e463c0f19a356c2660dbca5d19c44cae" + integrity sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + version "0.16.6" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.6.tgz#eb26db6780c513de59bee0b869ef289ad3068711" + integrity sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA== "@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + version "7.5.5" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35" + integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg== "@types/sinonjs__fake-timers@8.1.1": version "8.1.1" @@ -4576,19 +3349,19 @@ integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== "@types/sizzle@^2.3.2": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" - integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + version "2.3.6" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.6.tgz#e39b7123dac4631001939bd4c2a26d46010f2275" + integrity sha512-m04Om5Gz6kbjUwAQ7XJJQ30OdEFsSmAVsvn4NYwcTRyMVpKKa1aPuESw1n2CxS5fYkOQv3nHgDKeNa8e76fUkw== "@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== + version "0.1.5" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.5.tgz#1ec07192b889558a410833b237e595a906e5e349" + integrity sha512-cHBTLeIGIREJx839cDfMLKWao+FaJOlaPz4mnFHXUzShS8sXhzw6irhvIpYvp28TbTmTeAt3v+QgHMANsGbQtA== "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/string-hash@1.1.1": version "1.1.1" @@ -4596,26 +3369,26 @@ integrity sha512-ijt3zdHi2DmZxQpQTmozXszzDo78V4R3EdvX0jFMfnMH2ZzQSmCbaWOMPGXFUYSzSIdStv78HDjg32m5dxc+tA== "@types/tapable@^1": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" - integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.11.tgz#934bb63dc2b1c869c4800970a64be236fb4f2947" + integrity sha512-R3ltemSqZ/TKOBeyy+GBfZCLX3AYpxqarIbUMNe7+lxdazJp4iWLFpmjgBeZoRiKrWNImer1oWOlG2sDR6vGaw== "@types/testing-library__jest-dom@^5.9.1": - version "5.9.5" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0" - integrity sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ== + version "5.14.9" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" + integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== dependencies: "@types/jest" "*" "@types/tough-cookie@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" - integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== "@types/uglify-js@*": - version "3.17.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.1.tgz#e0ffcef756476410e5bce2cb01384ed878a195b5" - integrity sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g== + version "3.17.4" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.4.tgz#3c70021f08023e5a760ce133d22966f200e1d31c" + integrity sha512-Hm/T0kV3ywpJyMGNbsItdivRhYNCQQf1IIsYsXnoVPES4t+FMLyDe0/K+Ea7ahWtMtSNb22ZdY7MIyoD9rqARg== dependencies: source-map "^0.6.1" @@ -4632,18 +3405,18 @@ "@types/webpack" "^4" "@types/webpack-sources@*": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" - integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== + version "3.2.3" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.3.tgz#b667bd13e9fa15a9c26603dce502c7985418c3d8" + integrity sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw== dependencies: "@types/node" "*" "@types/source-list-map" "*" source-map "^0.7.3" "@types/webpack@^4": - version "4.41.33" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" - integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== + version "4.41.36" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.36.tgz#30d039ca41d155d8aeba6cd2bcbe32e78bb5bef2" + integrity sha512-pF+DVW1pMLmgsPXqJr5QimdxIzOhe8oGKB98gdqAm0egKBy1lOLD5mRxbYboMQRkpYcG7BYcpqYblpKyvE7vhQ== dependencies: "@types/node" "*" "@types/tapable" "^1" @@ -4653,35 +3426,28 @@ source-map "^0.6.0" "@types/yargs-parser@*": - version "20.2.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" - integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== - -"@types/yargs@^15.0.0": - version "15.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" - integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== - dependencies: - "@types/yargs-parser" "*" + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + version "16.0.8" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.8.tgz#0d57a5a491d85ae75d372a32e657b1779b86c65d" + integrity sha512-1GwLEkmFafeb/HbE6pC7tFlgYSQ4Iqh2qlWCq8xN+Qfaiaxr2PcLfuhfRFRYqI6XJyeFoLYyKnhFbNsst9FMtQ== dependencies: "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.23" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.23.tgz#a7db3a2062c95ca1a5e0d5d5ddb6521cbc649e35" - integrity sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ== + version "17.0.31" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.31.tgz#8fd0089803fd55d8a285895a18b88cb71a99683c" + integrity sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg== dependencies: "@types/yargs-parser" "*" "@types/yauzl@^2.9.1": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" - integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== dependencies: "@types/node" "*" @@ -4905,15 +3671,10 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== -"@wojtekmaj/date-utils@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz#2dcfd92881425c5923e429c2aec86fb3609032a1" - integrity sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw== - -"@wojtekmaj/date-utils@^1.1.3": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.5.0.tgz#6e8e5be857f3b66d0a99876e4c2295f1009a7560" - integrity sha512-0mq88lCND6QiffnSDWp+TbOxzJSwy2V/3XN+HwWZ7S2n19QAgR5dy5hRVhlECXvQIq2r+VcblBu+S9V+yMcxXw== +"@wojtekmaj/date-utils@^1.0.2", "@wojtekmaj/date-utils@^1.1.3": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz#c3cd67177ac781cfa5736219d702a55a2aea5f2b" + integrity sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww== "@xobotyi/scrollbar-width@^1.9.5": version "1.9.5" @@ -4954,19 +3715,14 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.0.2, acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^8.1.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + version "8.3.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" + integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== -acorn@^8.8.2, acorn@^8.9.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" - integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +acorn@^8.1.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== add-dom-event-listener@^1.1.0: version "1.1.0" @@ -4978,7 +3734,7 @@ add-dom-event-listener@^1.1.0: add-px-to-style@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-px-to-style/-/add-px-to-style-1.0.0.tgz#d0c135441fa8014a8137904531096f67f28f263a" - integrity sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo= + integrity sha512-YMyxSlXpPjD8uWekCQGuN40lV4bnZagUwqa2m/uFv1z/tNImSk9fnXVMUI5qwME/zzI3MMQRvjZ+69zyfSSyew== agent-base@6: version "6.0.2" @@ -5007,7 +3763,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== @@ -5024,7 +3780,7 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.0: +ajv@^8.0.0, ajv@^8.9.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -5044,21 +3800,14 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== - dependencies: - type-fest "^0.11.0" - -ansi-escapes@^4.3.0: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -5097,7 +3846,7 @@ ansicolor@1.1.100: resolved "https://registry.yarnpkg.com/ansicolor/-/ansicolor-1.1.100.tgz#811f1afbf726edca3aafb942a14df8351996304a" integrity sha512-Jl0pxRfa9WaQVUX57AB8/V2my6FJxrOR1Pp2qqFbig20QB4HzUoQ48THTKAgHlUCJeQm/s2WoOPcoIDhyCL/kw== -anymatch@^3.0.0, anymatch@^3.1.1: +anymatch@^3.0.0, anymatch@^3.0.3, anymatch@^3.1.1, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -5105,22 +3854,6 @@ anymatch@^3.0.0, anymatch@^3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -anymatch@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - arch@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -5148,10 +3881,19 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + aria-query@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" - integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" array-buffer-byte-length@^1.0.0: version "1.0.0" @@ -5161,17 +3903,6 @@ array-buffer-byte-length@^1.0.0: call-bind "^1.0.2" is-array-buffer "^3.0.1" -array-includes@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - array-includes@^3.1.6: version "3.1.7" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" @@ -5198,28 +3929,38 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array.prototype.flat@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" array.prototype.tosorted@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" - integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" + integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.1" -arraybuffer.prototype.slice@^1.0.1: +arraybuffer.prototype.slice@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== @@ -5235,19 +3976,19 @@ arraybuffer.prototype.slice@^1.0.1: asap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/asap/-/asap-1.0.0.tgz#b2a45da5fdfa20b0496fc3768cc27c12fa916a7d" - integrity sha1-sqRdpf36ILBJb8N2jMJ8EvqRan0= + integrity sha512-Ej9qjcXY+8Tuy1cNqiwNMwFRXOy9UwgTeMA8LxreodygIPV48lx8PU1ecFxb5ZeU1DpMKxiq6vGLTxcitWZPbA== asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== astral-regex@^2.0.0: version "2.0.0" @@ -5262,14 +4003,14 @@ async@^2.6.4: lodash "^4.17.14" async@^3.2.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== at-least-node@^1.0.0: version "1.0.0" @@ -5289,19 +4030,19 @@ available-typed-arrays@^1.0.5: aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + version "1.12.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" + integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== -babel-jest@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.4.tgz#98dbc45d1c93319c82a8ab4a478b670655dd2585" - integrity sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@jest/transform" "^29.6.4" + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" babel-preset-jest "^29.6.3" @@ -5400,15 +4141,15 @@ babel-preset-jest@^29.6.3: babel-runtime@6.x, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== dependencies: core-js "^2.4.0" regenerator-runtime "^0.11.0" balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" @@ -5418,7 +4159,7 @@ base64-js@^1.3.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" @@ -5435,7 +4176,7 @@ binary-extensions@^2.0.0: blink-diff@1.0.13: version "1.0.13" resolved "https://registry.yarnpkg.com/blink-diff/-/blink-diff-1.0.13.tgz#80e3df69de804b30d40c70f041e983841ecda899" - integrity sha1-gOPfad6ASzDUDHDwQemDhB7NqJk= + integrity sha512-2hIEnGq8wruXfje9GvDV41VXo+4YdjrjL5ZMlVJT3Wi5k1jjz20fCTlVejSXoERirhEVsFYz9NmgdUYgQ41Giw== dependencies: pngjs-image "~0.11.5" preceptor-core "~0.10.0" @@ -5481,7 +4222,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -5493,35 +4234,15 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== - dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" - -browserslist@^4.20.2, browserslist@^4.21.3: - version "4.21.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" - integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== - dependencies: - caniuse-lite "^1.0.30001370" - electron-to-chromium "^1.4.202" - node-releases "^2.0.6" - update-browserslist-db "^1.0.5" - -browserslist@^4.21.9: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== - dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" +browserslist@^4.14.5, browserslist@^4.21.9, browserslist@^4.22.1: + version "4.22.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" node-releases "^2.0.13" - update-browserslist-db "^1.0.11" + update-browserslist-db "^1.0.13" bser@2.1.1: version "2.1.1" @@ -5536,9 +4257,9 @@ buffer-crc32@~0.2.3: integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^5.6.0: version "5.7.1" @@ -5566,15 +4287,16 @@ cachedir@^2.3.0: calculate-size@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/calculate-size/-/calculate-size-1.1.1.tgz#ae7caa1c7795f82c4f035dc7be270e3581dae3ee" - integrity sha1-rnyqHHeV+CxPA13HvicONYHa4+4= + integrity sha512-jJZ7pvbQVM/Ss3VO789qpsypN3xmnepg242cejOAslsmlZLYw2dnj7knnNowabQ0Kzabzx56KFTy2Pot/y6FmA== -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" @@ -5586,35 +4308,20 @@ camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" - integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== - -camelcase@^6.2.0: +camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001370: - version "1.0.30001387" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001387.tgz#90d2b9bdfcc3ab9a5b9addee00a25ef86c9e2e1e" - integrity sha512-fKDH0F1KOJvR+mWSOvhj8lVRr/Q/mc5u5nabU2vi1/sgvlSqEsE8dOq0Hy/BqVbDkCYQPRRHB1WRjW6PGB/7PA== - -caniuse-lite@^1.0.30001449: - version "1.0.30001469" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz#3dd505430c8522fdc9f94b4a19518e330f5c945a" - integrity sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g== - -caniuse-lite@^1.0.30001517: - version "1.0.30001527" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz#813826554828245ccee776c850566dce12bdeaba" - integrity sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ== +caniuse-lite@^1.0.30001541: + version "1.0.30001562" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz#9d16c5fd7e9c592c4cd5e304bc0f75b0008b2759" + integrity sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== chalk-template@^1.1.0: version "1.1.0" @@ -5623,7 +4330,7 @@ chalk-template@^1.1.0: dependencies: chalk "^5.2.0" -chalk@^2.0.0, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5692,37 +4399,25 @@ chrome-remote-interface@0.32.0: ws "^7.2.0" chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== ci-info@^3.2.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" - integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== -classnames@2.3.2, classnames@^2.3.2: +classnames@2.3.2, classnames@2.x, classnames@^2.2.1, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.5, classnames@^2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" - integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== - -classnames@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -5787,12 +4482,7 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clsx@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" - integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== - -clsx@^1.2.1: +clsx@^1.1.1, clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -5800,12 +4490,12 @@ clsx@^1.2.1: co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -5824,19 +4514,14 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - -colorette@^2.0.16: +colorette@^2.0.14, colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -5907,24 +4592,24 @@ common-tags@^1.8.0: commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== component-classes@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691" - integrity sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE= + integrity sha512-hPFGULxdwugu1QWW3SvVOCUHLzO34+a2J6Wqy0c5ASQkfi9/8nZcBB0ZohaEbXOQlCflMAEMmEWk7u7BVs4koA== dependencies: component-indexof "0.0.3" component-indexof@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" - integrity sha1-EdCRMSI5648yyPJa6csAL/6NPCQ= + integrity sha512-puDQKvx/64HZXb4hBwIcvQLaLgux8o1CbWl39s41hrIIZDl1lJiD5jc22gj3RBeGK0ovxALDYpIbyjqDUUl0rw== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== configstore@^6.0.0: version "6.0.0" @@ -5942,12 +4627,10 @@ continuable-cache@^0.3.1: resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" integrity sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA== -convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" +convert-source-map@^1.5.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== convert-source-map@^2.0.0: version "2.0.0" @@ -5955,9 +4638,9 @@ convert-source-map@^2.0.0: integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== copy-to-clipboard@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" - integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== dependencies: toggle-selection "^1.0.6" @@ -5974,11 +4657,11 @@ copy-webpack-plugin@^11.0.0: serialize-javascript "^6.0.0" core-js-compat@^3.25.1: - version "3.29.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" - integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== + version "3.33.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.33.2.tgz#3ea4563bfd015ad4e4b52442865b02c62aba5085" + integrity sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw== dependencies: - browserslist "^4.21.5" + browserslist "^4.22.1" core-js@3.27.1: version "3.27.1" @@ -5995,12 +4678,12 @@ core-js@^2.4.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== -core-util-is@^1.0.3: +core-util-is@^1.0.3, core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== @@ -6015,18 +4698,7 @@ cosmiconfig@8.0.0: parse-json "^5.0.0" path-type "^4.0.0" -cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -cosmiconfig@^7.0.1: +cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== @@ -6037,6 +4709,19 @@ cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + 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" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -6191,13 +4876,12 @@ css-box-model@^1.2.0: dependencies: tiny-invariant "^1.0.6" -css-in-js-utils@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" - integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== +css-in-js-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" + integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== dependencies: - hyphenate-style-name "^1.0.2" - isobject "^3.0.1" + hyphenate-style-name "^1.0.3" css-loader@^6.7.3: version "6.8.1" @@ -6214,9 +4898,9 @@ css-loader@^6.7.3: semver "^7.3.8" css-tree@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5" - integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ== + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: mdn-data "2.0.14" source-map "^0.6.1" @@ -6224,12 +4908,12 @@ css-tree@^1.1.2: css.escape@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" - integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== csscolorparser@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b" - integrity sha1-s085HupNqPPpgjHizNjfnAQfFxs= + integrity sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w== cssesc@^3.0.0: version "3.0.0" @@ -6239,7 +4923,7 @@ cssesc@^3.0.0: cssfilter@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" - integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4= + integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== cssom@^0.5.0: version "0.5.0" @@ -6258,15 +4942,10 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@^3.0.2: - version "3.0.6" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef" - integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw== - -csstype@^3.0.6: - version "3.0.10" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" - integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== +csstype@^3.0.2, csstype@^3.0.6: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== cypress-file-upload@5.0.8: version "5.0.8" @@ -6322,9 +5001,9 @@ cypress@9.5.1: yauzl "^2.10.0" "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.3.tgz#39f1f4954e4a09ff69ac597c2d61906b04e84740" - integrity sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ== + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== dependencies: internmap "1 - 2" @@ -6364,9 +5043,9 @@ d3-contour@4: d3-array "^3.2.0" d3-delaunay@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92" - integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ== + version "6.0.4" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b" + integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== dependencies: delaunator "5" @@ -6604,7 +5283,7 @@ d3@7.8.5: dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== dependencies: assert-plus "^1.0.0" @@ -6632,21 +5311,14 @@ date-fns@2.30.0: date-format@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/date-format/-/date-format-0.0.0.tgz#09206863ab070eb459acea5542cbd856b11966b3" - integrity sha1-CSBoY6sHDrRZrOpVQsvYVrEZZrM= + integrity sha512-kAmAdtsjW5nQ02FERwI1bP4xe6HQBPwy5kpAF4CRSLOMUs/vgMIEEwpy6JqUs7NitTyhZiImxwAjgPpnteycHg== dayjs@^1.10.4: - version "1.11.9" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" - integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== -debug@4: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@4.3.4, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -6656,7 +5328,7 @@ debug@4.3.4, debug@^4.3.2, debug@^4.3.4: debug@^0.7.2: version "0.7.4" resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" - integrity sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk= + integrity sha512-EohAb3+DSHSGx8carOSKJe8G0ayV5/i609OD0J2orCkuyae7SyZSz2aoLmQF2s0Pj5gITDebwPH7GFBlqOUQ1Q== debug@^2.2.0: version "2.6.9" @@ -6672,13 +5344,6 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - decamelize@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" @@ -6694,36 +5359,55 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + 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" -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" has-property-descriptors "^1.0.0" - object-keys "^1.1.1" -define-properties@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: + define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -6737,7 +5421,12 @@ delaunator@5: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== detect-newline@^3.0.0: version "3.1.0" @@ -6749,16 +5438,6 @@ devtools-protocol@0.0.1065144: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1065144.tgz#3ebceef449c5a611ef0972bf8b9b635e840b1a9b" integrity sha512-SFQz0ecyNnXZlCGiVVpS6vkx/MOkIrsLHiuIvGSHH74KXdGtWPnh/WN3jwis77kepHzO6MufYmRrULLB/TWiXw== -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== - -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -6784,7 +5463,7 @@ dir-glob@^3.0.1: direction@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c" - integrity sha1-zl15f5fib4vnvv9T99xA4cGp7Ew= + integrity sha512-HceXsAluGbXKCz2qCVbXFUH4Vn4eNMWxY5gzydMFMnS1zKSwvDASqLwcrYLIFDpwuZ63FUAqjDLEP1eicHt8DQ== discontinuous-range@1.0.0: version "1.0.0" @@ -6806,28 +5485,28 @@ doctrine@^3.0.0: esutils "^2.0.2" dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: - version "0.5.10" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz#caa6d08f60388d0bb4539dd75fe458a9a1d0014c" - integrity sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g== + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== dom-align@^1.7.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.0.tgz#56fb7156df0b91099830364d2d48f88963f5a29c" - integrity sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA== + version "1.12.4" + resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.4.tgz#3503992eb2a7cfcb2ed3b2a6d21e0b9c00d54511" + integrity sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw== dom-css@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dom-css/-/dom-css-2.1.0.tgz#fdbc2d5a015d0a3e1872e11472bbd0e7b9e6a202" - integrity sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI= + integrity sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q== dependencies: add-px-to-style "1.0.0" prefix-style "2.0.1" to-camel-case "1.0.0" dom-helpers@^5.0.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b" - integrity sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ== + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== dependencies: "@babel/runtime" "^7.8.7" csstype "^3.0.2" @@ -6864,25 +5543,15 @@ eastasianwidth@^0.2.0: ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.4.202: - version "1.4.239" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.239.tgz#5b04acb39c16b897a508980d1be95ba5f0201771" - integrity sha512-XbhfzxPIFzMjJm17T7yUGZEyYh5XuUjrA/FQ7JUy2bEd4qQ7MvFTaKpZ6zXZog1cfVttESo2Lx0ctnf7eQOaAQ== - -electron-to-chromium@^1.4.284: - version "1.4.334" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.334.tgz#eacdcb1145534202d569610c5915b63a3fec0eb9" - integrity sha512-laZ1odk+TRen6q0GeyQx/JEkpD3iSZT7ewopCpKqg9bTjP1l8XRfU3Bg20CFjNPZkp5+NDBl3iqd4o/kPO+Vew== - -electron-to-chromium@^1.4.477: - version "1.4.508" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz#5641ff2f5ba11df4bd960fe6a2f9f70aa8b9af96" - integrity sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg== +electron-to-chromium@^1.4.535: + version "1.4.586" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.586.tgz#68683163ed52a111213e2482ff847e76a5c6e891" + integrity sha512-qMa+E6yf1fNQbg3G66pHLXeJUP5CCCzNat1VPczOZOqgI2w4u+8y9sQnswMdGs5m4C1rOePq37EVBr/nsPQY7w== emittery@^0.13.1: version "0.13.1" @@ -6937,14 +5606,14 @@ enquirer@^2.3.6: strip-ansi "^6.0.1" entities@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" - integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== errno@^0.1.3: version "0.1.8" @@ -6961,11 +5630,11 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" error-stack-parser@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" - integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== dependencies: - stackframe "^1.1.1" + stackframe "^1.3.4" error@^7.0.0: version "7.2.1" @@ -6974,51 +5643,26 @@ error@^7.0.0: dependencies: string-template "~0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.1" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" - is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" - -es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.5" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" internal-slot "^1.0.5" is-array-buffer "^3.0.2" is-callable "^1.2.7" @@ -7026,85 +5670,59 @@ es-abstract@^1.20.4: is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" + is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.3" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.13" -es-abstract@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" - integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.1" - available-typed-arrays "^1.0.5" call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.1" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" + get-intrinsic "^1.1.3" has-symbols "^1.0.3" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.3" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.0" - safe-array-concat "^1.0.0" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-buffer "^1.0.0" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.10" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" es-module-lexer@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" - integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" + get-intrinsic "^1.2.2" has-tostringtag "^1.0.0" + hasown "^2.0.0" es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== dependencies: - has "^1.0.3" + hasown "^2.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -7128,7 +5746,7 @@ escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" @@ -7136,14 +5754,13 @@ escape-string-regexp@^2.0.0: integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== dependencies: esprima "^4.0.1" estraverse "^5.2.0" esutils "^2.0.2" - optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" @@ -7209,12 +5826,7 @@ eslint-scope@^7.2.0: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint-visitor-keys@^3.4.1: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -7306,19 +5918,14 @@ esrecurse@^4.3.0: esrever@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/esrever/-/esrever-0.2.0.tgz#96e9d28f4f1b1a76784cd5d490eaae010e7407b8" - integrity sha1-lunSj08bGnZ4TNXUkOquAQ50B7g= + integrity sha512-1e9YJt6yQkyekt2BUjTky7LZWWVyC2cIpgdnsTAvMcnzXIZvlW/fTMPkxBcZoYhgih4d+EC+iw+yv9GIkz7vrw== estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -estraverse@^5.3.0: +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -7388,34 +5995,23 @@ executable@^4.1.1: exenv@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" - integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= + integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw== exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= - -expect@^29.0.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" - integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== - dependencies: - "@jest/expect-utils" "^29.5.0" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.4.tgz#a6e6f66d4613717859b2fe3da98a739437b6f4b8" - integrity sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA== +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/expect-utils" "^29.6.4" + "@jest/expect-utils" "^29.7.0" jest-get-type "^29.6.3" - jest-matcher-utils "^29.6.4" - jest-message-util "^29.6.3" - jest-util "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" extend@~3.0.2: version "3.0.2" @@ -7436,12 +6032,12 @@ extract-zip@2.0.1: extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -7458,7 +6054,7 @@ fast-equals@^5.0.1: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d" integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== -fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -7474,10 +6070,15 @@ fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-loops@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75" + integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== fast-shallow-equal@^1.0.0: version "1.0.0" @@ -7500,9 +6101,9 @@ fastest-stable-stringify@^2.0.2: integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== fastq@^1.6.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.10.1.tgz#8b8f2ac8bf3632d67afcd65dac248d5fdc45385e" - integrity sha512-AWuv6Ery3pM+dY7LYS8YIaCiQvUaos9OB1RyNgaOWnaX+Tik7Onvcsf8x8c+YtDeT0maYLniBip2hox5KtEXXA== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" @@ -7514,9 +6115,9 @@ faye-websocket@~0.10.0: websocket-driver ">=0.5.1" fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" @@ -7600,15 +6201,7 @@ find-up@^6.3.0: locate-path "^7.1.0" path-exists "^5.0.0" -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flat-cache@^3.1.1: +flat-cache@^3.0.4, flat-cache@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== @@ -7622,11 +6215,6 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - flatted@^3.2.9: version "3.2.9" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" @@ -7650,7 +6238,7 @@ foreground-child@^3.1.0: forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== fork-ts-checker-webpack-plugin@^8.0.0: version "8.0.0" @@ -7707,37 +6295,37 @@ fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-monkey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" - integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== +fs-monkey@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" + integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -7766,9 +6354,9 @@ geotiff@2.0.4: xml-utils "^1.0.2" geotiff@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/geotiff/-/geotiff-2.0.7.tgz#358e578233af70bfb0b4dee62d599ad78fc5cfca" - integrity sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q== + version "2.1.0" + resolved "https://registry.yarnpkg.com/geotiff/-/geotiff-2.1.0.tgz#8e06fd1aa950fba8910ac63dc65cb77b766f4b58" + integrity sha512-B/iFJuFfRpmPHXf8aIRPRgUWwfaNb6dlsynkM8SWeHAPu7CpyvfqEa43KlBt7xxq5OTVysQacFHxhCn3SZhRKQ== dependencies: "@petamoriken/float16" "^3.4.7" lerc "^3.0.0" @@ -7777,6 +6365,7 @@ geotiff@^2.0.7: quick-lru "^6.1.1" web-worker "^1.2.0" xml-utils "^1.0.2" + zstddec "^0.1.0" get-caller-file@^2.0.5: version "2.0.5" @@ -7786,35 +6375,17 @@ get-caller-file@^2.0.5: get-document@1: version "1.0.0" resolved "https://registry.yarnpkg.com/get-document/-/get-document-1.0.0.tgz#4821bce66f1c24cb0331602be6cb6b12c4f01c4b" - integrity sha1-SCG85m8cJMsDMWAr5strEsTwHEs= - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" + integrity sha512-8E7H2Xxibav+/rQTTtm6gFlSQwDoAQg667yheA+vWQr/amxEuswChzGo4MIbOJJoR0SMpDyhbUqWp3FpIfwD9A== -get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + function-bind "^1.1.2" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-package-type@^0.1.0: version "0.1.0" @@ -7847,16 +6418,16 @@ get-symbol-description@^1.0.0: get-intrinsic "^1.1.1" get-user-locale@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-1.4.0.tgz#a2c4b5da46feec9f03c9b07d197b1620490a5370" - integrity sha512-gQo03lP1OArHLKlnoglqrGGl7b04u2EP9Xutmp72cMdtrrSD7ZgIsCsUKZynYWLDkVJW33Cj3pliP7uP0UonHQ== + version "1.5.1" + resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-1.5.1.tgz#18a9ba2cfeed0e713ea00968efa75d620523a5ea" + integrity sha512-WiNpoFRcHn1qxP9VabQljzGwkAQDrcpqUtaP0rNBEkFxJdh4f3tik6MfZsMYZc+UgQJdGCxWEjL9wnCUlRQXag== dependencies: - lodash.once "^4.1.1" + lodash.memoize "^4.1.1" get-user-locale@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-2.3.0.tgz#7656315a963a824048883f7dab4d7dc74773d1e0" - integrity sha512-I3rQvAUwu2nauRD9YyQBSXVFJZixNouwA+eZld51Sn4Pn0N1qFbgcgOi/nPigJPQlNY519mT95fiSPRgflQiTA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-2.3.1.tgz#fc7319429c8a70fac01b3b2a0b08b0c71c1d3fe2" + integrity sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ== dependencies: "@types/lodash.memoize" "^4.1.7" lodash.memoize "^4.1.1" @@ -7878,7 +6449,7 @@ getos@^3.2.1: getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: assert-plus "^1.0.0" @@ -7914,25 +6485,25 @@ glob@7.2.0: path-is-absolute "^1.0.0" glob@^10.2.7: - version "10.3.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.4.tgz#c85c9c7ab98669102b6defda76d35c5b1ef9766f" - integrity sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ== + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== dependencies: foreground-child "^3.1.0" - jackspeak "^2.0.3" + jackspeak "^2.3.5" minimatch "^9.0.1" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" glob@^7.1.3, glob@^7.1.4: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -7949,9 +6520,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + version "13.23.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== dependencies: type-fest "^0.20.2" @@ -7975,13 +6546,13 @@ globby@^11.1.0: slash "^3.0.0" globby@^13.1.1: - version "13.1.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" - integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== dependencies: dir-glob "^3.0.1" - fast-glob "^3.2.11" - ignore "^5.2.0" + fast-glob "^3.3.0" + ignore "^5.2.4" merge2 "^1.4.1" slash "^4.0.0" @@ -7992,12 +6563,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== - -graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -8015,7 +6581,7 @@ graphemer@^1.4.0: har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== har-validator@~5.1.3: version "5.1.5" @@ -8030,12 +6596,7 @@ harmony-reflect@^1.4.6: resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" integrity sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g== -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== - -has-bigints@^1.0.2: +has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== @@ -8043,7 +6604,7 @@ has-bigints@^1.0.2: has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" @@ -8056,28 +6617,18 @@ has-own-prop@^2.0.0: integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ== has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-symbols@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -8089,12 +6640,12 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" he@1.2.0: version "1.2.0" @@ -8161,7 +6712,7 @@ http-proxy-agent@^5.0.0: http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" @@ -8194,22 +6745,22 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -hyphenate-style-name@^1.0.2: +hyphenate-style-name@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== i18next-browser-languagedetector@^7.0.2: - version "7.1.0" - resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.1.0.tgz#01876fac51f86b78975e79b48ccb62e2313a2d7d" - integrity sha512-cr2k7u1XJJ4HTOjM9GyOMtbOA47RtUoWRAtt52z43r3AoMs2StYKyjS3URPhzHaf+mn10hY9dZWamga5WPQjhA== + version "7.2.0" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.0.tgz#de0321cba6881be37d82e20e4d6f05aa75f6e37f" + integrity sha512-U00DbDtFIYD3wkWsr2aVGfXGAj2TgnELzOX9qv8bT0aJtvPV9CRO77h+vgmHFBMe7LAxdwvT/7VkCWGya6L3tA== dependencies: - "@babel/runtime" "^7.19.4" + "@babel/runtime" "^7.23.2" i18next@^22.0.0: - version "22.4.13" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.13.tgz#02e291ab0056eab13b7d356fb454ff991923eaa0" - integrity sha512-GX7flMHRRqQA0I1yGLmaZ4Hwt1JfLqagk8QPDPZsqekbKtXsuIngSVWM/s3SLgNkrEXjA+0sMGNuOEkkmyqmWg== + version "22.5.1" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.5.1.tgz#99df0b318741a506000c243429a7352e5f44d424" + integrity sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA== dependencies: "@babel/runtime" "^7.20.6" @@ -8244,21 +6795,26 @@ ieee754@^1.1.12, ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== immutable@4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.2.2.tgz#2da9ff4384a4330c36d4d1bc88e90f9e0b0ccd16" integrity sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og== -immutable@4.3.0, immutable@^4.0.0: +immutable@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== +immutable@^4.0.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== + import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -8268,9 +6824,9 @@ import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: resolve-from "^4.0.0" import-local@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -8283,22 +6839,17 @@ import-meta-resolve@^3.1.1: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -8314,28 +6865,20 @@ ini@2.0.0: integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== inline-style-prefixer@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" - integrity sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ== - dependencies: - css-in-js-utils "^2.0.0" - -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + version "6.0.4" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz#4290ed453ab0e4441583284ad86e41ad88384f44" + integrity sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg== dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" + css-in-js-utils "^3.1.0" + fast-loops "^1.1.3" -internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== +internal-slot@^1.0.4, internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" + get-intrinsic "^1.2.2" + hasown "^2.0.0" side-channel "^1.0.4" "internmap@1 - 2": @@ -8349,14 +6892,14 @@ interpret@^3.1.1: integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== intl-messageformat@^10.1.0: - version "10.1.4" - resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.1.4.tgz#bf5ad48e357e3f3ab6559599296f54c175b22a92" - integrity sha512-tXCmWCXhbeHOF28aIf5b9ce3kwdwGyIiiSXVZsyDwksMiGn5Tp0MrMvyeuHuz4uN1UL+NfGOztHmE+6aLFp1wQ== + version "10.5.8" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.8.tgz#7184da425f360a53a5483a6194e16d666b011fc0" + integrity sha512-NRf0jpBWV0vd671G5b06wNofAN8tp7WWDogMZyaU8GUAsmbouyvgwmFJI7zLjfAMpm3zK+vSwRP3jzaoIcMbaA== dependencies: - "@formatjs/ecma402-abstract" "1.12.0" - "@formatjs/fast-memoize" "1.2.6" - "@formatjs/icu-messageformat-parser" "2.1.7" - tslib "2.4.0" + "@formatjs/ecma402-abstract" "1.18.0" + "@formatjs/fast-memoize" "2.2.0" + "@formatjs/icu-messageformat-parser" "2.7.3" + tslib "^2.4.0" invariant@^2.2.2: version "2.2.4" @@ -8365,6 +6908,14 @@ invariant@^2.2.2: dependencies: loose-envify "^1.0.0" +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -8377,7 +6928,7 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" @@ -8413,21 +6964,11 @@ is-builtin-module@^3.2.1: dependencies: builtin-modules "^3.3.0" -is-callable@^1.1.3, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-callable@^1.1.4: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== - -is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - is-ci@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" @@ -8435,29 +6976,24 @@ is-ci@^3.0.0: dependencies: ci-info "^3.2.0" -is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" -is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: - has "^1.0.3" - -is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + has-tostringtag "^1.0.0" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -8469,14 +7005,7 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -8496,7 +7025,7 @@ is-hotkey@0.2.0: is-in-browser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" - integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= + integrity sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g== is-installed-globally@~0.4.0: version "0.4.0" @@ -8506,10 +7035,10 @@ is-installed-globally@~0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" -is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== is-negative-zero@^2.0.2: version "2.0.2" @@ -8517,9 +7046,9 @@ is-negative-zero@^2.0.2: integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" @@ -8563,10 +7092,10 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== is-shared-array-buffer@^1.0.2: version "1.0.2" @@ -8576,9 +7105,9 @@ is-shared-array-buffer@^1.0.2: call-bind "^1.0.2" is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" @@ -8587,47 +7116,34 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" - -is-symbol@^1.0.3: +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.11" is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-weakref@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" - integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== - dependencies: - call-bind "^1.0.0" +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== is-weakref@^1.0.2: version "1.0.2" @@ -8636,15 +7152,23 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-window@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" - integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= + integrity sha512-uj00kdXyZb9t9RcAUAwMZAnkBUwdYGhYlt7djMXhfyhUCzwNba50tIiBKR7q0l7tdoBtFVw/3JmLY6fI3rmZmg== isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== isarray@^2.0.5: version "2.0.5" @@ -8654,37 +7178,32 @@ isarray@^2.0.5: isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isomorphic-base64@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/isomorphic-base64/-/isomorphic-base64-1.0.2.tgz#f426aae82569ba8a4ec5ca73ad21a44ab1ee7803" - integrity sha1-9Caq6CVpuopOxcpzrSGkSrHueAM= + integrity sha512-pQFyLwShVPA1Qr0dE1ZPguJkbOsFGDfSq6Wzz6XaO33v74X6/iQjgYPozwkeKGQxOI1/H3Fz7+ROtnV1veyKEg== isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== -istanbul-lib-coverage@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== - -istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: version "5.2.1" @@ -8698,9 +7217,9 @@ istanbul-lib-instrument@^5.0.4: semver "^6.3.0" istanbul-lib-instrument@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz#7a8af094cbfff1d5bb280f62ce043695ae8dd5b8" - integrity sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw== + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -8709,214 +7228,183 @@ istanbul-lib-instrument@^6.0.0: semver "^7.5.4" istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jackspeak@^2.0.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.3.tgz#95e4cbcc03b3eb357bf6bcce14a903fb3d1151e1" - integrity sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg== +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jest-changed-files@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.6.3.tgz#97cfdc93f74fb8af2a1acb0b78f836f1fb40c449" - integrity sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" - jest-util "^29.6.3" + jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.4.tgz#f074c8d795e0cc0f2ebf0705086b1be6a9a8722f" - integrity sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw== +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: - "@jest/environment" "^29.6.4" - "@jest/expect" "^29.6.4" - "@jest/test-result" "^29.6.4" + "@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.6.3" - jest-matcher-utils "^29.6.4" - jest-message-util "^29.6.3" - jest-runtime "^29.6.4" - jest-snapshot "^29.6.4" - jest-util "^29.6.3" + 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.6.3" + pretty-format "^29.7.0" pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.4.tgz#ad52f2dfa1b0291de7ec7f8d7c81ac435521ede0" - integrity sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^29.6.4" - "@jest/test-result" "^29.6.4" + "@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" - graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.6.4" - jest-util "^29.6.3" - jest-validate "^29.6.3" - prompts "^2.0.1" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" yargs "^17.3.1" -jest-config@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.4.tgz#eff958ee41d4e1ee7a6106d02b74ad9fc427d79e" - integrity sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A== +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.6.4" + "@jest/test-sequencer" "^29.7.0" "@jest/types" "^29.6.3" - babel-jest "^29.6.4" + 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.6.4" - jest-environment-node "^29.6.4" + 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.6.4" - jest-runner "^29.6.4" - jest-util "^29.6.3" - jest-validate "^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.6.3" + pretty-format "^29.7.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^26.0.0: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-diff@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" - integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" - -jest-diff@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.4.tgz#85aaa6c92a79ae8cd9a54ebae8d5b6d9a513314a" - integrity sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" diff-sequences "^29.6.3" jest-get-type "^29.6.3" - pretty-format "^29.6.3" + pretty-format "^29.7.0" -jest-docblock@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.6.3.tgz#293dca5188846c9f7c0c2b1bb33e5b11f21645f2" - integrity sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.3.tgz#1956f14f5f0cb8ae0b2e7cabc10bb03ec817c142" - integrity sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: "@jest/types" "^29.6.3" chalk "^4.0.0" jest-get-type "^29.6.3" - jest-util "^29.6.3" - pretty-format "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" jest-environment-jsdom@^29.5.0: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.6.4.tgz#0daf44454041f9e1ef7fa82eb1bd43426a82eb1c" - integrity sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA== + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" + integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== dependencies: - "@jest/environment" "^29.6.4" - "@jest/fake-timers" "^29.6.4" + "@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.6.3" - jest-util "^29.6.3" + jest-mock "^29.7.0" + jest-util "^29.7.0" jsdom "^20.0.0" -jest-environment-node@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.4.tgz#4ce311549afd815d3cafb49e60a1e4b25f06d29f" - integrity sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ== +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== dependencies: - "@jest/environment" "^29.6.4" - "@jest/fake-timers" "^29.6.4" + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^29.6.3" - jest-util "^29.6.3" - -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== - -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-mock "^29.7.0" + jest-util "^29.7.0" jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.4.tgz#97143ce833829157ea7025204b08f9ace609b96a" - integrity sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog== +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: "@jest/types" "^29.6.3" "@types/graceful-fs" "^4.1.3" @@ -8925,60 +7413,35 @@ jest-haste-map@^29.6.4: fb-watchman "^2.0.0" graceful-fs "^4.2.9" jest-regex-util "^29.6.3" - jest-util "^29.6.3" - jest-worker "^29.6.4" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz#b9661bc3aec8874e59aff361fa0c6d7cd507ea01" - integrity sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: jest-get-type "^29.6.3" - pretty-format "^29.6.3" - -jest-matcher-utils@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" - integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== - dependencies: - chalk "^4.0.0" - jest-diff "^29.5.0" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + pretty-format "^29.7.0" -jest-matcher-utils@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz#327db7ababea49455df3b23e5d6109fe0c709d24" - integrity sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^29.6.4" + jest-diff "^29.7.0" jest-get-type "^29.6.3" - pretty-format "^29.6.3" - -jest-message-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" - integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.5.0" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.5.0" - slash "^3.0.0" - stack-utils "^2.0.3" + pretty-format "^29.7.0" -jest-message-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.3.tgz#bce16050d86801b165f20cfde34dc01d3cf85fbf" - integrity sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" "@jest/types" "^29.6.3" @@ -8986,90 +7449,90 @@ jest-message-util@^29.6.3: chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.6.3" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.3.tgz#433f3fd528c8ec5a76860177484940628bdf5e0a" - integrity sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg== +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: "@jest/types" "^29.6.3" "@types/node" "*" - jest-util "^29.6.3" + jest-util "^29.7.0" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz#20156b33c7eacbb6bb77aeba4bed0eab4a3f8734" - integrity sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: jest-regex-util "^29.6.3" - jest-snapshot "^29.6.4" + jest-snapshot "^29.7.0" -jest-resolve@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.4.tgz#e34cb06f2178b429c38455d98d1a07572ac9faa3" - integrity sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.6.4" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^29.6.3" - jest-validate "^29.6.3" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.4.tgz#b3b8ccb85970fde0fae40c73ee11eb75adccfacf" - integrity sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^29.6.4" - "@jest/environment" "^29.6.4" - "@jest/test-result" "^29.6.4" - "@jest/transform" "^29.6.4" + "@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.6.3" - jest-environment-node "^29.6.4" - jest-haste-map "^29.6.4" - jest-leak-detector "^29.6.3" - jest-message-util "^29.6.3" - jest-resolve "^29.6.4" - jest-runtime "^29.6.4" - jest-util "^29.6.3" - jest-watcher "^29.6.4" - jest-worker "^29.6.4" + 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" -jest-runtime@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.4.tgz#b0bc495c9b6b12a0a7042ac34ca9bb85f8cd0ded" - integrity sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA== +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== dependencies: - "@jest/environment" "^29.6.4" - "@jest/fake-timers" "^29.6.4" - "@jest/globals" "^29.6.4" + "@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.6.4" - "@jest/transform" "^29.6.4" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" @@ -9077,58 +7540,46 @@ jest-runtime@^29.6.4: collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.6.4" - jest-message-util "^29.6.3" - jest-mock "^29.6.3" + 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.6.4" - jest-snapshot "^29.6.4" - jest-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" -jest-snapshot@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.4.tgz#9833eb6b66ff1541c7fd8ceaa42d541f407b4876" - integrity sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== 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.6.4" - "@jest/transform" "^29.6.4" + "@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.6.4" + expect "^29.7.0" graceful-fs "^4.2.9" - jest-diff "^29.6.4" + jest-diff "^29.7.0" jest-get-type "^29.6.3" - jest-matcher-utils "^29.6.4" - jest-message-util "^29.6.3" - jest-util "^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.6.3" + pretty-format "^29.7.0" semver "^7.5.3" -jest-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== - dependencies: - "@jest/types" "^29.5.0" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.3.tgz#e15c3eac8716440d1ed076f09bc63ace1aebca63" - integrity sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: "@jest/types" "^29.6.3" "@types/node" "*" @@ -9137,30 +7588,30 @@ jest-util@^29.6.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.3.tgz#a75fca774cfb1c5758c70d035d30a1f9c2784b4d" - integrity sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== 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.6.3" + pretty-format "^29.7.0" -jest-watcher@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.4.tgz#633eb515ae284aa67fd6831f1c9d1b534cf0e0ba" - integrity sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^29.6.4" + "@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.6.3" + jest-util "^29.7.0" string-length "^4.0.1" jest-worker@^27.4.5: @@ -9172,35 +7623,25 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" - integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== - dependencies: - "@types/node" "*" - jest-util "^29.5.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest-worker@^29.6.4: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.4.tgz#f34279f4afc33c872b470d4af21b281ac616abd3" - integrity sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q== +jest-worker@^29.5.0, jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" - jest-util "^29.6.3" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.5.0: - version "29.6.4" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.4.tgz#7c48e67a445ba264b778253b5d78d4ebc9d0a622" - integrity sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw== + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^29.6.4" + "@jest/core" "^29.7.0" "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^29.6.4" + jest-cli "^29.7.0" jquery@3.6.1: version "3.6.1" @@ -9245,7 +7686,7 @@ js-yaml@^3.13.1: jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== jsdoc-type-pratt-parser@~4.0.0: version "4.0.0" @@ -9292,7 +7733,7 @@ jsesc@^2.5.1: jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-buffer@3.0.1: version "3.0.1" @@ -9314,11 +7755,6 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" @@ -9327,7 +7763,7 @@ json-schema@0.4.0: json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stringify-pretty-compact@^2.0.0: version "2.0.0" @@ -9337,7 +7773,7 @@ json-stringify-pretty-compact@^2.0.0: json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== json5@^2.1.2, json5@^2.2.1, json5@^2.2.2, json5@^2.2.3: version "2.2.3" @@ -9359,13 +7795,13 @@ jsonfile@^6.0.1: graceful-fs "^4.1.6" jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" extsprintf "1.3.0" - json-schema "0.2.3" + json-schema "0.4.0" verror "1.10.0" jsprim@^2.0.2: @@ -9379,12 +7815,14 @@ jsprim@^2.0.2: verror "1.10.0" "jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" - integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== dependencies: - array-includes "^3.1.3" - object.assign "^4.1.2" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" keyv@^4.5.3: version "4.5.4" @@ -9431,18 +7869,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== listr2@^3.8.3: version "3.14.0" @@ -9501,14 +7931,14 @@ locate-path@^7.1.0: lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.memoize@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash.merge@4.6.2, lodash.merge@^4.6.2: +lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== @@ -9516,9 +7946,9 @@ lodash.merge@4.6.2, lodash.merge@^4.6.2: lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== -lodash@4.17.21, lodash@^4.1.1, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: +lodash@4.17.21, lodash@^4.1.1, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -9544,7 +7974,7 @@ log-update@^4.0.0: log4js@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/log4js/-/log4js-1.1.1.tgz#c21d29c7604089e4f255833e7f94b3461de1ff43" - integrity sha1-wh0px2BAieTyVYM+f5SzRh3h/0M= + integrity sha512-lYb14ZSs1M/CUFuvy7Zk3VZLDtqrqOaVql9CE0tv8g6/qE1Gfq97XKdltBsjSxxvcJ+t8fAXOnvFxSsms7gGVg== dependencies: debug "^2.2.0" semver "^5.3.0" @@ -9572,22 +8002,31 @@ lru-cache@^6.0.0: yallist "^4.0.0" "lru-cache@^9.1.1 || ^10.0.0": - version "10.0.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + version "10.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.2.tgz#34504678cc3266b09b8dfd6fab4e1515258271b7" + integrity sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg== + dependencies: + semver "^7.3.5" -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" - integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== -make-dir@^3.0.0, make-dir@^3.0.2: +make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -9601,9 +8040,9 @@ makeerror@1.0.12: tmpl "1.0.5" mapbox-to-css-font@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.1.tgz#41bf38faed36b7dab069828aa3654e4bd91a1eda" - integrity sha512-QQ/iKiM43DM9+aujTL45Iz5o7gDeSFmy4LPl3HZmNcwCE++NxGazf+yFpY+wCb+YS23sDa1ghpo3zrNFOcHlow== + version "2.4.2" + resolved "https://registry.yarnpkg.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.2.tgz#a9e31b363ad8ca881cd339ca99f2d2a6b02ea5dd" + integrity sha512-f+NBjJJY4T3dHtlEz1wCG7YFlkODEjFIYlxDdLIDMNpkSksqTt+l/d4rjuwItxuzkuMFvPyrjzV2lxRM4ePcIA== marked-mangle@1.1.0: version "1.1.0" @@ -9635,18 +8074,18 @@ mdn-data@2.0.14: integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== memfs@^3.4.1: - version "3.4.13" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.13.tgz#248a8bd239b3c240175cd5ec548de5227fc4f345" - integrity sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg== + version "3.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== dependencies: - fs-monkey "^1.0.3" + fs-monkey "^1.0.4" memoize-one@6.0.0, memoize-one@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== -"memoize-one@>=3.1.1 <6": +"memoize-one@>=3.1.1 <6", memoize-one@^5.1.1: version "5.2.1" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== @@ -9656,11 +8095,6 @@ memoize-one@^4.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== -memoize-one@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" - integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== - memory-fs@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" @@ -9670,9 +8104,9 @@ memory-fs@^0.5.0: readable-stream "^2.0.1" merge-class-names@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/merge-class-names/-/merge-class-names-1.4.0.tgz#02edcdd5ff677fbb03b47ecd4586df89d697b81b" - integrity sha512-xNdBM7s+6uD+vNZJEymqrFbMBCDGzoA8clZTcj2F1XIy1QQKF+wjFVv7iDZFfdCBnViTdt54A4Ye2lmBsXrBjQ== + version "1.4.2" + resolved "https://registry.yarnpkg.com/merge-class-names/-/merge-class-names-1.4.2.tgz#78d6d95ab259e7e647252a7988fd25a27d5a8835" + integrity sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw== merge-stream@^2.0.0: version "2.0.0" @@ -9684,15 +8118,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - -micromatch@^4.0.4, micromatch@^4.0.5: +micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -9700,24 +8126,12 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.45.0: - version "1.45.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" - integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.28" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" - integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== - dependencies: - mime-db "1.45.0" - -mime-types@^2.1.27: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -9749,7 +8163,7 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -9769,18 +8183,11 @@ minimist@^1.2.5, minimist@^1.2.6: integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" - integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== - -mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -mkdirp@^0.5.6: +mkdirp@^0.5.1, mkdirp@^0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -9839,14 +8246,14 @@ monaco-editor@0.34.0: integrity sha512-VF+S5zG8wxfinLKLrWcl4WUizMx+LeJrG4PM/M78OhcwocpV0jiyhX/pG6Q9jIOhrb/ckYi6nHnaR5OojlOZCQ== moo@^0.5.0, moo@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" - integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.2: version "2.1.2" @@ -9864,9 +8271,9 @@ murmurhash-js@^1.0.0: integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw== nano-css@^5.3.1: - version "5.3.4" - resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.4.tgz#40af6a83a76f84204f346e8ccaa9169cdae9167b" - integrity sha512-wfcviJB6NOxDIDfr7RFn/GlaN7I/Bhe4d39ZRCJ3xvZX60LVe2qZ+rDqM49nm4YT81gAjzS+ZklhKP/Gnfnubg== + version "5.3.5" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.5.tgz#3075ea29ffdeb0c7cb6d25edb21d8f7fa8e8fe8e" + integrity sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg== dependencies: css-tree "^1.1.2" csstype "^3.0.6" @@ -9883,9 +8290,9 @@ nanoid@3.3.3: integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== nanoid@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== natural-compare-lite@^1.4.0: version "1.4.0" @@ -9895,7 +8302,7 @@ natural-compare-lite@^1.4.0: natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== nearley@^2.19.5: version "2.20.1" @@ -9925,23 +8332,13 @@ node-abort-controller@^3.0.1: node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.13: version "2.0.13" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== - -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -9955,9 +8352,9 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: path-key "^3.0.0" nwsapi@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== oauth-sign@~0.9.0: version "0.9.0" @@ -9967,38 +8364,26 @@ oauth-sign@~0.9.0: object-assign@4.x, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-inspect@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== -object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" -object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" @@ -10097,7 +8482,7 @@ ol@^7.3.0: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" @@ -10108,29 +8493,17 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" ospath@^1.2.2: version "1.2.2" @@ -10194,12 +8567,12 @@ p-try@^2.0.0: pako@^0.2.6: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== pako@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" - integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== papaparse@5.3.2: version "5.3.2" @@ -10226,9 +8599,9 @@ parent-module@^2.0.0: callsites "^3.1.0" parse-headers@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.4.tgz#9eaf2d02bed2d1eff494331ce3df36d7924760bf" - integrity sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw== + version "2.0.5" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" + integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" @@ -10260,12 +8633,12 @@ path-exists@^5.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" @@ -10313,7 +8686,7 @@ pend@~1.2.0: performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== pgsql-ast-parser@^11.1.0: version "11.1.0" @@ -10328,12 +8701,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -10344,9 +8712,9 @@ pify@^2.2.0: integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" @@ -10358,7 +8726,7 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: pngjs-image@~0.11.5: version "0.11.7" resolved "https://registry.yarnpkg.com/pngjs-image/-/pngjs-image-0.11.7.tgz#631dd59924569fc82ffebae0d5d53f85f54dab62" - integrity sha1-Yx3VmSRWn8gv/rrg1dU/hfVNq2I= + integrity sha512-JRyrmT+HXa1/gvdHpebus8TGqKa8WRgcsHz/DDalxRsMhvu6AOA99/enBFjZIPvmXVAzwKR051s80TuE1IiCpg== dependencies: iconv-lite "^0.4.8" pako "^0.2.6" @@ -10370,7 +8738,7 @@ pngjs-image@~0.11.5: pngjs@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-2.3.1.tgz#11d1e12b9cb64d63e30c143a330f4c1f567da85f" - integrity sha1-EdHhK5y2TWPjDBQ6Mw9MH1Z9qF8= + integrity sha512-ITNPqvx+SSssNFOgHQzGG87HrqQ0g2nMSHc1jjU5Piq9xJEJ40fiFEPz0S5HSSXxBHrTnhaBHIayTO5aRfk2vw== portfinder@^1.0.17: version "1.0.32" @@ -10409,30 +8777,15 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-selector-parser@^6.0.2: - version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" - integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - util-deprecate "^1.0.2" - -postcss-selector-parser@^6.0.4: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== - -postcss-value-parser@^4.2.0: +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== @@ -10457,18 +8810,13 @@ preceptor-core@~0.10.0: prefix-style@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/prefix-style/-/prefix-style-2.0.1.tgz#66bba9a870cfda308a5dc20e85e9120932c95a06" - integrity sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY= + integrity sha512-gdr1MBNVT0drzTq95CbSNdsrBDoHGlb2aDJP/FoY+1e+jSDPOb1Cv554gH2MGiSr2WTcXi/zu+NaFzfcHQkfBQ== prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - prettier@^2.8.7: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" @@ -10479,39 +8827,19 @@ pretty-bytes@^5.6.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^26.0.0, pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - pretty-format@^27.0.2: - version "27.3.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.3.1.tgz#7e9486365ccdd4a502061fa761d3ab9ca1b78df5" - integrity sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA== + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: - "@jest/types" "^27.2.5" ansi-regex "^5.0.1" ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^29.0.0, pretty-format@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" - integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== - dependencies: - "@jest/schemas" "^29.4.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-format@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" - integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" @@ -10530,28 +8858,19 @@ process-nextick-args@~2.0.0: promise@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/promise/-/promise-6.0.0.tgz#456538dd4afdd25dc7d0f52a5201ed242b7c109d" - integrity sha1-RWU43Ur90l3H0PUqUgHtJCt8EJ0= + integrity sha512-PjIqIEWR8EWwP5ml3Wf5KWIP3sIdXAew9vQ6vLOLV+z4LMa/8ZQyLd7sTWe2r8OuA8A9jsIYptDfbEn/L36ogw== dependencies: asap "~1.0.0" prompts@^2.0.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" - integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - -prop-types@^15.8.1: +prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -10573,12 +8892,12 @@ proxy-from-env@1.0.0: prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== psl@^1.1.28, psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== pump@^3.0.0: version "3.0.0" @@ -10589,14 +8908,14 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" - integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + version "6.0.4" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" + integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== qs@^6.4.0: version "6.11.2" @@ -10622,6 +8941,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + quick-lru@^6.1.1: version "6.1.2" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-6.1.2.tgz#e9a90524108629be35287d0b864e7ad6ceb3659e" @@ -10633,9 +8957,9 @@ quickselect@^2.0.0: integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== raf-schd@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" - integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== + version "4.0.3" + resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" + integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== raf@^3.1.0, raf@^3.4.0, raf@^3.4.1: version "3.4.1" @@ -10690,14 +9014,14 @@ rc-align@^2.4.0: rc-util "^4.0.4" rc-align@^4.0.0: - version "4.0.9" - resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.9.tgz#46d8801c4a139ff6a65ad1674e8efceac98f85f2" - integrity sha512-myAM2R4qoB6LqBul0leaqY8gFaiECDJ3MtQDmzDo9xM9NRT/04TvWOYd2YHU9zvGzqk9QXF6S9/MifzSKDZeMw== + version "4.0.15" + resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.15.tgz#2bbd665cf85dfd0b0244c5a752b07565e9098577" + integrity sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" dom-align "^1.7.0" - rc-util "^5.3.0" + rc-util "^5.26.0" resize-observer-polyfill "^1.5.1" rc-animate@2.x: @@ -10759,61 +9083,33 @@ rc-drawer@6.3.0: rc-motion "^2.6.1" rc-util "^5.21.2" -rc-motion@^2.0.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.1.tgz#323f47c8635e6b2bc0cba2dfad25fc415b58e1dc" - integrity sha512-TWLvymfMu8SngPx5MDH8dQ0D2RYbluNTfam4hY/dNNx9RQ3WtGuZ/GXHi2ymLMzH+UNd6EEFYkOuR5JTTtm8Xg== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.2.1" - -rc-motion@^2.0.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.2.tgz#3d31f97e41fb8e4f91a4a4189b6a98ac63342869" - integrity sha512-4w1FaX3dtV749P8GwfS4fYnFG4Rb9pxvCYPc/b2fw1cmlHJWNNgOFIz7ysiD+eOrzJSvnLJWlNQQncpNMXwwpg== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.21.0" - -rc-motion@^2.6.1: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.3.tgz#e6d8ca06591c2c1bcd3391a8e7a822ebc4d94e9c" - integrity sha512-xFLkes3/7VL/J+ah9jJruEW/Akbx5F6jVa2wG5o/ApGKQKSOd5FR3rseHLL9+xtJg4PmCwo6/1tqhDO/T+jFHA== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.6.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.0.tgz#9e18a1b8d61e528a97369cf9a7601e9b29205710" + integrity sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.21.0" rc-overflow@^1.0.0: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.8.tgz#40f140fabc244118543e627cdd1ef750d9481a88" - integrity sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" + integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.19.2" + rc-util "^5.37.0" -rc-resize-observer@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.2.0.tgz#9f46052f81cdf03498be35144cb7c53fd282c4c7" - integrity sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.1" - rc-util "^5.15.0" - resize-observer-polyfill "^1.5.1" - -rc-resize-observer@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.3.1.tgz#b61b9f27048001243617b81f95e53d7d7d7a6a3d" - integrity sha512-iFUdt3NNhflbY3mwySv5CA1TC06zdJ+pfo0oc27xpf4PIOvfZwZGtD9Kz41wGYqC4SLio93RVAirSSpYlV/uYg== +rc-resize-observer@^1.0.0, rc-resize-observer@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" + integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== dependencies: "@babel/runtime" "^7.20.7" classnames "^2.2.1" - rc-util "^5.27.0" + rc-util "^5.38.0" resize-observer-polyfill "^1.5.1" rc-select@~14.2.0: @@ -10892,15 +9188,15 @@ rc-tooltip@6.0.1: classnames "^2.3.1" rc-tree@~5.7.0: - version "5.7.3" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.7.3.tgz#5da576ba87039486d59092eb4490831690b8b3b5" - integrity sha512-Oql2S9+ZmT+mfTp5SNo1XM0QvkENjc0mPRFsHWRFSPuKird0OYMZZKmLznUJ+0aGDeFFWN42wiUZJtMFhrLgLw== + version "5.7.12" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.7.12.tgz#6910e551390963708936c2cbf925f9deff4a6d76" + integrity sha512-LXA5nY2hG5koIAlHW5sgXgLpOMz+bFRbnZZ+cCg0tQs4Wv1AmY7EDi1SK7iFXhslYockbqUerQan82jljoaItg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.8" + rc-virtual-list "^3.5.1" rc-trigger@^2.2.0: version "2.6.5" @@ -10915,18 +9211,7 @@ rc-trigger@^2.2.0: rc-util "^4.4.0" react-lifecycles-compat "^3.0.4" -rc-trigger@^5.0.4: - version "5.2.10" - resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.2.10.tgz#8a0057a940b1b9027eaa33beec8a6ecd85cce2b1" - integrity sha512-FkUf4H9BOFDaIwu42fvRycXMAvkttph9AlbCZXssZDVzz2L+QZ0ERvfB/4nX3ZFPh1Zd+uVGr1DEDeXxq4J1TA== - dependencies: - "@babel/runtime" "^7.11.2" - classnames "^2.2.6" - rc-align "^4.0.0" - rc-motion "^2.0.0" - rc-util "^5.5.0" - -rc-trigger@^5.3.1: +rc-trigger@^5.0.4, rc-trigger@^5.3.1: version "5.3.4" resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.4.tgz#6b4b26e32825677c837d1eb4d7085035eecf9a61" integrity sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw== @@ -10943,59 +9228,23 @@ rc-util@^4.0.4, rc-util@^4.15.3, rc-util@^4.4.0: integrity sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg== dependencies: add-dom-event-listener "^1.1.0" - prop-types "^15.5.10" - react-is "^16.12.0" - react-lifecycles-compat "^3.0.4" - shallowequal "^1.1.0" - -rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.19.2, rc-util@^5.21.0, rc-util@^5.6.1: - version "5.23.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.23.0.tgz#a583b1ec3e1832a80eced7a700a494af0b590743" - integrity sha512-lgm6diJ/pLgyfoZY59Vz7sW4mSoQCgozqbBye9IJ7/mb5w5h4T7h+i2JpXAx/UBQxscBZe68q0sP7EW+qfkKUg== - dependencies: - "@babel/runtime" "^7.18.3" - react-is "^16.12.0" - shallowequal "^1.1.0" - -rc-util@^5.18.1, rc-util@^5.21.2, rc-util@^5.24.4: - version "5.29.2" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.29.2.tgz#a3741fca57b3dce78f315574ed099945f367b222" - integrity sha512-xHT9Dr3RD6tyvCibnH10l3mudC6TJjWNr9UDy3CrOGZqTY354OfdwP87ahKNe0b3A1dsysDldvx0SBuswhlOeA== - dependencies: - "@babel/runtime" "^7.18.3" - react-is "^16.12.0" - -rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.5.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.7.0.tgz#776b14cf5bbfc24f419fd40c42ffadddda0718fc" - integrity sha512-0hh5XkJ+vBDeMJsHElqT1ijMx+gC3gpClwQ10h/5hccrrgrMx8VUem183KLlH1YrWCfMMPmDXWWNnwsn+p6URw== - dependencies: - "@babel/runtime" "^7.12.5" + prop-types "^15.5.10" react-is "^16.12.0" + react-lifecycles-compat "^3.0.4" shallowequal "^1.1.0" -rc-util@^5.27.0, rc-util@^5.33.0, rc-util@^5.36.0: - version "5.37.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.37.0.tgz#6df9a55cb469b41b6995530a45b5f3dd3219a4ea" - integrity sha512-cPMV8DzaHI1KDaS7XPRXAf4J7mtBqjvjikLpQieaeOO7+cEbqY2j7Kso/T0R0OiEZTNcLS/8Zl9YrlXiO9UbjQ== +rc-util@^5.16.1, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.24.4, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.6.1: + version "5.38.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.38.1.tgz#4915503b89855f5c5cd9afd4c72a7a17568777bb" + integrity sha512-e4ZMs7q9XqwTuhIK7zBIVFltUtMSjphuPPQXHoHlzRzNdOwUxDejo0Zls5HYaJfRKNURcsS/ceKVULlhjBrxng== dependencies: "@babel/runtime" "^7.18.3" - react-is "^16.12.0" - -rc-virtual-list@^3.4.13, rc-virtual-list@^3.4.8: - version "3.4.13" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.13.tgz#20acc934b263abcf7b7c161f50ef82281b2f7e8d" - integrity sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w== - dependencies: - "@babel/runtime" "^7.20.0" - classnames "^2.2.6" - rc-resize-observer "^1.0.0" - rc-util "^5.15.0" + react-is "^18.2.0" -rc-virtual-list@^3.5.2: - version "3.10.5" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.10.5.tgz#a203ca60bf3334e16193f641db1e99a48ae76574" - integrity sha512-Vc89TL3JHfRlLVQXVj5Hmv0dIflgwmHDcbjt9lrZjOG3wNUDkTF5zci8kFDU/CzdmmqgKu+CUktEpT10VUKYSQ== +rc-virtual-list@^3.4.13, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: + version "3.11.3" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.3.tgz#77d4e12e20c1ba314b43c0e37e118296674c5401" + integrity sha512-tu5UtrMk/AXonHwHxUogdXAWynaXsrx1i6dsgg+lOo/KJSF8oBAcprh1z5J3xgnPJD5hXxTL58F8s8onokdt0Q== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" @@ -11050,14 +9299,13 @@ react-custom-scrollbars-2@4.5.0: prop-types "^15.5.10" raf "^3.1.0" -react-dom@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" react-dropzone@14.2.3: version "14.2.3" @@ -11069,9 +9317,9 @@ react-dropzone@14.2.3: prop-types "^15.8.1" react-fast-compare@^3.0.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" - integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== react-from-dom@^0.6.2: version "0.6.2" @@ -11093,9 +9341,9 @@ react-hook-form@7.5.3: integrity sha512-5T0mfJ4kCPKljd7t3Rgp7lML4Y2+kaZIeMdN6Zo/J7gBQ+WkrDBHOftdOtz4X+7/eqHGak5yL5evNpYdA9abVA== react-i18next@^12.0.0: - version "12.2.0" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.2.0.tgz#010e3f6070b8d700442947233352ebe4b252d7a1" - integrity sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ== + version "12.3.1" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.3.1.tgz#30134a41a2a71c61dc69c6383504929aed1c99e7" + integrity sha512-5v8E2XjZDFzK7K87eSwC7AJcAkcLt5xYZ4+yTPDAW1i7C93oOY1dnr4BaQM7un4Hm+GmghuiPvevWwlca5PwDA== dependencies: "@babel/runtime" "^7.20.6" html-parse-stringify "^3.0.1" @@ -11123,22 +9371,17 @@ react-inlinesvg@3.0.2: exenv "^1.2.2" react-from-dom "^0.6.2" -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: - version "17.0.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" - integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== - -react-is@^17.0.2: +react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: +react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -11171,9 +9414,9 @@ react-popper@2.3.0, react-popper@^2.3.0: warning "^4.0.2" react-redux@^7.2.0: - version "7.2.6" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa" - integrity sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ== + version "7.2.9" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d" + integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ== dependencies: "@babel/runtime" "^7.15.4" "@types/react-redux" "^7.1.20" @@ -11196,22 +9439,22 @@ react-router-dom@5.3.3: tiny-warning "^1.0.0" react-router-dom@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363" - integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ== + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.4.tgz#2ed62ffd88cae6db134445f4a0c0ae8b91d2e5e6" + integrity sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ== dependencies: "@babel/runtime" "^7.12.13" history "^4.9.0" loose-envify "^1.3.1" prop-types "^15.6.2" - react-router "5.2.1" + react-router "5.3.4" tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d" - integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ== +react-router@5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.3.tgz#8e3841f4089e728cf82a429d92cdcaa5e4a3a288" + integrity sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w== dependencies: "@babel/runtime" "^7.12.13" history "^4.9.0" @@ -11224,16 +9467,15 @@ react-router@5.2.1: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.3.tgz#8e3841f4089e728cf82a429d92cdcaa5e4a3a288" - integrity sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w== +react-router@5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.4.tgz#8ca252d70fcc37841e31473c7a151cf777887bb5" + integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA== dependencies: "@babel/runtime" "^7.12.13" history "^4.9.0" hoist-non-react-statics "^3.1.0" loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" path-to-regexp "^1.7.0" prop-types "^15.6.2" react-is "^16.6.0" @@ -11241,9 +9483,9 @@ react-router@5.3.3: tiny-warning "^1.0.0" react-select-event@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-select-event/-/react-select-event-5.3.0.tgz#4548fffd615a47176951cbb301ee21a0c60b582a" - integrity sha512-Novkl7X9JJKmDV5LyYaKwl0vffWtqPrBa1vuI0v43P/f87mSA7JfdYxU93SFb99RssphVzBSIAbcnbX1w21QIQ== + version "5.5.1" + resolved "https://registry.yarnpkg.com/react-select-event/-/react-select-event-5.5.1.tgz#d67e04a6a51428b1534b15ecb1b82afbe5edddcb" + integrity sha512-goAx28y0+iYrbqZA2FeRTreHHs/ZtSuKxtA+J5jpKT5RHPCbVZJ4MqACfPnWyFXsEec+3dP5bCrNTxIX8oYe9A== dependencies: "@testing-library/dom" ">=7" @@ -11282,7 +9524,7 @@ react-table@7.8.0: resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== -react-transition-group@4.4.5: +react-transition-group@4.4.5, react-transition-group@^4.3.0: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== @@ -11292,16 +9534,6 @@ react-transition-group@4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react-transition-group@^4.3.0: - version "4.4.1" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" - integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw== - dependencies: - "@babel/runtime" "^7.5.5" - dom-helpers "^5.0.1" - loose-envify "^1.4.0" - prop-types "^15.6.2" - react-universal-interface@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" @@ -11335,18 +9567,17 @@ react-window@1.8.8: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" readable-stream@^1.1.7: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -11354,9 +9585,9 @@ readable-stream@^1.1.7: string_decoder "~0.10.x" readable-stream@^2.0.1: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -11388,43 +9619,21 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redux@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" - integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw== +redux@^4.0.0, redux@^4.0.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== dependencies: "@babel/runtime" "^7.9.2" -redux@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" - integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== - dependencies: - loose-envify "^1.4.0" - symbol-observable "^1.2.0" - -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== dependencies: regenerate "^1.4.2" -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0, regenerate@^1.4.2: +regenerate@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== @@ -11434,7 +9643,7 @@ regenerator-runtime@0.13.10: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== -regenerator-runtime@0.13.11, regenerator-runtime@^0.13.11: +regenerator-runtime@0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== @@ -11444,64 +9653,26 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - regenerator-runtime@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexp.prototype.flags@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" - functions-have-names "^1.2.3" - -regexpu-core@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" - integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regexpu-core@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" - integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + set-function-name "^2.0.0" regexpu-core@^5.3.1: version "5.3.2" @@ -11515,30 +9686,6 @@ regexpu-core@^5.3.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" -regjsgen@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== - -regjsparser@^0.6.4: - version "0.6.7" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.7.tgz#c00164e1e6713c2e3ee641f1701c4b7aa0a7f86c" - integrity sha512-ib77G0uxsA2ovgiYbCVGx4Pv3PSttAx2vIwidqQzbL2U5S4Q+j00HdSAneSBuyVcMvEnTXMjiGgB+DlXozVhpQ== - dependencies: - jsesc "~0.5.0" - -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== - dependencies: - jsesc "~0.5.0" - regjsparser@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" @@ -11592,7 +9739,7 @@ request@^2.55.0: require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" @@ -11646,34 +9793,25 @@ resolve-protobuf-schema@^2.1.0: protocol-buffers-schema "^3.3.1" resolve.exports@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20" - integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw== - -resolve@^1.14.2: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== - dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.19.0, resolve@^1.20.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -11708,35 +9846,37 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: glob "^7.1.3" robust-predicates@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a" - integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g== + version "3.0.2" + resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" + integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== rtl-css-js@^1.14.0: - version "1.14.5" - resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.5.tgz#fd92685acd024e688dda899a28c5fb9270b79974" - integrity sha512-+ng7LWVvPjQUdgDVviR6vKi2X4JiBtlw5rdY0UM5/Cj39c2/KDUsY/VxEzGE25m4KR5g0dvuKfrDq7DaoDooIA== + version "1.16.1" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.1.tgz#4b48b4354b0ff917a30488d95100fbf7219a3e80" + integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg== dependencies: "@babel/runtime" "^7.1.2" run-parallel@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" - integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" rw@1, rw@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" - integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= + integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== rxjs@7.5.7, rxjs@7.8.0, rxjs@^7.5.1, rxjs@^7.5.6: - version "7.5.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" - integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" -safe-array-concat@^1.0.0: +safe-array-concat@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== @@ -11799,34 +9939,24 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@>1.0.0, schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" + ajv "^8.9.0" ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" - -schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" + ajv-keywords "^5.1.0" -schema-utils@^3.2.0: +schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -11843,7 +9973,7 @@ screenfull@^5.1.0: selection-is-backward@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/selection-is-backward/-/selection-is-backward-1.0.0.tgz#97a54633188a511aba6419fc5c1fa91b467e6be1" - integrity sha1-l6VGMxiKURq6ZBn8XB+pG0Z+a+E= + integrity sha512-C+6PCOO55NLCfS8uQjUKV/6E5XMuUcfOVsix5m0QqCCCKi495NgeQVNfWtAaD71NKHsdmFCJoXUGfir3qWdr9A== semver@^5.3.0, semver@^5.5.0: version "5.7.2" @@ -11876,6 +10006,25 @@ serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + set-harmonic-interval@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" @@ -11896,7 +10045,7 @@ shallowequal@^1.1.0: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== dependencies: shebang-regex "^1.0.0" @@ -11910,7 +10059,7 @@ shebang-command@^2.0.0: shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== shebang-regex@^3.0.0: version "3.0.0" @@ -11926,17 +10075,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -signal-exit@^3.0.3: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== - -signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -12055,17 +10194,17 @@ slice-ansi@^4.0.0: sort-asc@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/sort-asc/-/sort-asc-0.1.0.tgz#ab799df61fc73ea0956c79c4b531ed1e9e7727e9" - integrity sha1-q3md9h/HPqCVbHnEtTHtHp53J+k= + integrity sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw== sort-desc@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/sort-desc/-/sort-desc-0.1.1.tgz#198b8c0cdeb095c463341861e3925d4ee359a9ee" - integrity sha1-GYuMDN6wlcRjNBhh45JdTuNZqe4= + integrity sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw== sort-object@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/sort-object/-/sort-object-0.3.2.tgz#98e0d199ede40e07c61a84403c61d6c3b290f9e2" - integrity sha1-mODRme3kDgfGGoRAPGHWw7KQ+eI= + integrity sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA== dependencies: sort-asc "^0.1.0" sort-desc "^0.1.1" @@ -12094,12 +10233,12 @@ source-map-support@~0.5.20: source-map@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" @@ -12130,34 +10269,19 @@ spdx-expression-parse@^3.0.1: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + version "3.0.16" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" + integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -sshpk@^1.14.1: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== +sshpk@^1.14.1, sshpk@^1.7.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" + integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -12170,11 +10294,11 @@ sshpk@^1.7.0: tweetnacl "~0.14.0" stack-generator@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36" - integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q== + version "2.0.10" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" + integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== dependencies: - stackframe "^1.1.1" + stackframe "^1.3.4" stack-utils@^2.0.3: version "2.0.6" @@ -12183,18 +10307,18 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stackframe@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" - integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== stacktrace-gps@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a" - integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg== + version "3.1.2" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" + integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== dependencies: source-map "0.5.6" - stackframe "^1.1.1" + stackframe "^1.3.4" stacktrace-js@^2.0.2: version "2.0.2" @@ -12210,15 +10334,22 @@ state-local@^1.0.6: resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + stream-buffers@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-1.0.1.tgz#9a44a37555f96a5b78a5a765f0c48446cb160b8c" - integrity sha1-mkSjdVX5alt4padl8MSERssWC4w= + integrity sha512-t+8bSU8qPq7NnWHWAvikjcZf+biErLZzD15RroYft1IKQwYbkRyiwppT7kNqwdtYLS59YPxc4sTSvwbLSMaodw== streamroller@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.4.1.tgz#d435bd5974373abd9bd9068359513085106cc05f" - integrity sha1-1DW9WXQ3Or2b2QaDWVEwhRBswF8= + integrity sha512-w0GGkMlWOiIBIYTmOWHTWKy9Y5hKxGKpQ5WpiHqwhvoSoMHXNTITrk6ZsR3fdgz3Bi/c+CXVHwmfPUQFkEPL+A== dependencies: date-format "^0.0.0" debug "^0.7.2" @@ -12243,8 +10374,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12253,15 +10383,6 @@ string-template@~0.2.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -12272,9 +10393,9 @@ string-width@^5.0.1, string-width@^5.1.2: strip-ansi "^7.0.1" string.prototype.matchall@^4.0.8: - version "4.0.9" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.9.tgz#148779de0f75d36b13b15885fec5cadde994520d" - integrity sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA== + version "4.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" @@ -12283,55 +10404,40 @@ string.prototype.matchall@^4.0.8: has-symbols "^1.0.3" internal-slot "^1.0.5" regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" side-channel "^1.0.4" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" string_decoder@0.10, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== string_decoder@~1.1.1: version "1.1.1" @@ -12386,25 +10492,15 @@ style-loader@3.3.3: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== -stylis@4.0.13: - version "4.0.13" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" - integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== - -stylis@4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" - integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== - stylis@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== stylis@^4.0.6: - version "4.0.10" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" - integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg== + version "4.3.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.0.tgz#abe305a669fc3d8777e10eefcfc73ad861c5588c" + integrity sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ== supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" @@ -12437,11 +10533,6 @@ swc-loader@^0.2.3: resolved "https://registry.yarnpkg.com/swc-loader/-/swc-loader-0.2.3.tgz#6792f1c2e4c9ae9bf9b933b3e010210e270c186d" integrity sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A== -symbol-observable@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -12474,9 +10565,9 @@ terser-webpack-plugin@^5.3.7: terser "^5.16.8" terser@^5.16.8: - version "5.19.4" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.4.tgz#941426fa482bf9b40a0308ab2b3cd0cf7c775ebd" - integrity sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g== + version "5.24.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.24.0.tgz#4ae50302977bca4831ccc7b4fef63a3c04228364" + integrity sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -12495,7 +10586,7 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== throttle-debounce@^3.0.1: version "3.0.1" @@ -12512,15 +10603,10 @@ through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tiny-invariant@^1.0.1, tiny-invariant@^1.0.6: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" - integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== - -tiny-invariant@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== +tiny-invariant@^1.0.1, tiny-invariant@^1.0.2, tiny-invariant@^1.0.6: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== tiny-lr@^1.1.1: version "1.1.1" @@ -12569,19 +10655,19 @@ tmpl@1.0.5: to-camel-case@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/to-camel-case/-/to-camel-case-1.0.0.tgz#1a56054b2f9d696298ce66a60897322b6f423e46" - integrity sha1-GlYFSy+daWKYzmamCJcyK29CPkY= + integrity sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q== dependencies: to-space-case "^1.0.0" to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-no-case@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" - integrity sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo= + integrity sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg== to-regex-range@^5.0.1: version "5.0.1" @@ -12593,26 +10679,16 @@ to-regex-range@^5.0.1: to-space-case@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17" - integrity sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc= + integrity sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA== dependencies: to-no-case "^1.0.0" toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= - -tough-cookie@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== -tough-cookie@^4.1.3: +tough-cookie@^4.1.2, tough-cookie@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -12686,40 +10762,25 @@ tsconfig-paths@^4.2.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - tslib@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== -tslib@2.5.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== - tslib@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== -tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -tslib@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.3: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tsutils@^3.21.0: version "3.21.0" @@ -12731,14 +10792,14 @@ tsutils@^3.21.0: tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -12747,23 +10808,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -12782,7 +10831,7 @@ type-fest@^1.0.1: type-of@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/type-of/-/type-of-2.0.1.tgz#e72a1741896568e9f628378d816d6912f7f23972" - integrity sha1-5yoXQYllaOn2KDeNgW1pEvfyOXI= + integrity sha512-39wxbwHdQ2sTiBB8wAzKfQ9GN+om8w+sjNWzr+vZJR5AMD5J+J7Yc8AtXnU9r/r2c8XiDZ/smxutDmZehX/qpQ== typed-array-buffer@^1.0.0: version "1.0.0" @@ -12835,20 +10884,15 @@ typescript@4.8.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -ua-parser-js@^1.0.32: - version "1.0.34" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.34.tgz#b33f41c415325839f354005d25a2f588be296976" - integrity sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew== +typescript@^4.8.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== - dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" - which-boxed-primitive "^1.0.2" +ua-parser-js@^1.0.32: + version "1.0.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" + integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== unbox-primitive@^1.0.2: version "1.0.2" @@ -12863,26 +10907,18 @@ unbox-primitive@^1.0.2: underscore@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" - integrity sha1-a7rwh3UA02vjTsqlhODbn+8DUgk= + integrity sha512-cp0oQQyZhUM1kpJDLdGO1jPZHgS/MpzoWYfe9+CM2h/QGDZlqwT2T3YGukuBdaNJ/CAPoeyAZRRHz8JFo176vA== -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - unicode-match-property-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" @@ -12891,35 +10927,15 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - unicode-match-property-value-ecmascript@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== unique-string@^3.0.0: version "3.0.0" @@ -12934,35 +10950,19 @@ universalify@^0.2.0: integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -12993,14 +10993,14 @@ use-isomorphic-layout-effect@^1.1.2: integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== use-memo-one@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20" - integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ== + version "1.1.3" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99" + integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ== util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@9.0.0: version "9.0.0" @@ -13023,13 +11023,13 @@ v8-compile-cache-lib@^3.0.1: integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" - integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + version "9.1.3" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz#ea456604101cd18005ac2cae3cdd1aa058a6306b" + integrity sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" + convert-source-map "^2.0.0" value-equal@^1.0.1: version "1.0.1" @@ -13039,7 +11039,7 @@ value-equal@^1.0.1: verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -13089,15 +11089,10 @@ watchpack@^2.4.0: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -web-vitals@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.3.0.tgz#2e14bcbb3acf71d00c49519ab92cc517511476d5" - integrity sha512-GZsEmJBNclIpViS/7QVOTr7Kbt4BgLeR7kQ5zCCtJVuiWsA+K6xTXaoEXssvl8yYFICEyNmA2Nr+vgBYTnS4bA== - -web-vitals@^3.1.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.4.0.tgz#45ed33a3a2e029dc38d36547eb5d71d1c7e2552d" - integrity sha512-n9fZ5/bG1oeDkyxLWyep0eahrNcPDF6bFqoyispt7xkW0xhDzpUBTgyDKqWDi1twT0MgH4HvvqzpUyh0ZxZV4A== +web-vitals@^3.0.4, web-vitals@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.5.0.tgz#3a5571f00743ecd059394b61e0adceec7fac2634" + integrity sha512-f5YnCHVG9Y6uLCePD4tY8bO/Ge15NPEQWtvm3tPzDKygloiqtb4SVqRHBcrIAqo2ztqX5XueqDn97zHF0LdT6w== web-worker@^1.2.0: version "1.2.0" @@ -13139,11 +11134,12 @@ webpack-livereload-plugin@^3.0.2: tiny-lr "^1.1.1" webpack-merge@^5.7.3: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-sources@^3.2.3: @@ -13157,9 +11153,9 @@ webpack-virtual-modules@^0.4.4: integrity sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA== webpack@^5.86.0: - version "5.88.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" - integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" @@ -13231,28 +11227,26 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.10: - version "1.1.11" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" - integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.4" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^1.2.9: version "1.3.1" @@ -13269,14 +11263,9 @@ which@^2.0.1: isexe "^2.0.0" wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" - integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== workerpool@6.2.1: version "6.2.1" @@ -13313,7 +11302,7 @@ wrap-ansi@^8.1.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^3.0.3: version "3.0.3" @@ -13339,9 +11328,9 @@ ws@^7.2.0: integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.11.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== xdg-basedir@^5.0.1: version "5.1.0" @@ -13354,9 +11343,9 @@ xml-name-validator@^4.0.0: integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== xml-utils@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.0.2.tgz#8081bfefb87b72e03e4adbabdd217ccbbc395eeb" - integrity sha512-rEn0FvKi+YGjv9omf22oAf+0d6Ly/sgJ/CUufU/nOzS7SRLmgwSujrewc03KojXxt+aPaTRpm593TgehtUBMSQ== + version "1.7.0" + resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.7.0.tgz#333ce391d8918f872aaf98d2cf90f9ef9b82bd0f" + integrity sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw== xmlchars@^2.2.0: version "2.2.0" @@ -13392,9 +11381,9 @@ yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.0.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" - integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== yargs-parser@20.2.4: version "20.2.4" @@ -13435,9 +11424,9 @@ yargs@16.2.0: yargs-parser "^20.2.2" yargs@^17.3.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" @@ -13469,3 +11458,8 @@ yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +zstddec@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec" + integrity sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg== From 65c4d6b98b53cecb65b58b947d779c1ba08b757d Mon Sep 17 00:00:00 2001 From: Spencer Torres Date: Wed, 6 Dec 2023 15:00:23 -0500 Subject: [PATCH 2/3] Allow LimitEditor to go to 0 to exclude LIMIT (#619) --- src/components/queryBuilder/LimitEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/queryBuilder/LimitEditor.tsx b/src/components/queryBuilder/LimitEditor.tsx index fbe059e7..7d4da2c1 100644 --- a/src/components/queryBuilder/LimitEditor.tsx +++ b/src/components/queryBuilder/LimitEditor.tsx @@ -22,7 +22,7 @@ export const LimitEditor = (props: LimitEditorProps) => { width={10} value={limit} type="number" - min={1} + min={0} onChange={e => setLimit(e.currentTarget.valueAsNumber)} onBlur={() => props.onLimitChange(limit)} /> From 33c95b4d036a2649ecb1fb8d314a4055a6b3c8f5 Mon Sep 17 00:00:00 2001 From: Spencer Torres Date: Thu, 1 Feb 2024 11:44:15 -0500 Subject: [PATCH 3/3] v4.0.0-beta fixes (#666) Signed-off-by: dependabot[bot] Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adam Yeats Co-authored-by: ohdearaugustin Co-authored-by: Archimedes Fagundes Junior Co-authored-by: Adam Simpson Co-authored-by: Christoph Wurm --- .github/workflows/detect-breaking-changes.yml | 2 +- .github/workflows/pull-request-image.yml | 6 +- .github/workflows/run-frontend-tests.yml | 2 +- CHANGELOG.md | 63 ++- DEV_GUIDE.md | 44 ++ README.md | 89 ++-- cspell.config.json | 2 + e2e/e2e_test.go | 4 +- go.mod | 49 +- go.sum | 103 ++-- package.json | 2 +- pkg/macros/macros.go | 8 +- pkg/macros/macros_test.go | 12 +- pkg/plugin/driver.go | 1 + pkg/plugin/driver_test.go | 24 +- pkg/plugin/settings.go | 42 +- pkg/plugin/settings_test.go | 26 +- src/__mocks__/ConfigEditor.ts | 2 +- src/__mocks__/datasource.ts | 79 +-- .../configEditor/HttpHeadersConfig.test.tsx | 123 +++++ .../configEditor/HttpHeadersConfig.tsx | 175 +++++++ src/components/configEditor/LogsConfig.tsx | 1 + src/components/configEditor/TracesConfig.tsx | 1 + .../queryBuilder/AggregateEditor.tsx | 44 +- src/components/queryBuilder/ColumnSelect.tsx | 9 +- .../queryBuilder/FilterEditor.test.tsx | 77 ++- src/components/queryBuilder/FilterEditor.tsx | 173 ++++--- src/components/queryBuilder/OrderByEditor.tsx | 30 +- .../queryBuilder/OtelVersionSelect.test.tsx | 2 +- .../queryBuilder/OtelVersionSelect.tsx | 5 +- src/components/queryBuilder/QueryBuilder.tsx | 91 +++- .../queryBuilder/TraceIdInput.test.tsx | 26 + src/components/queryBuilder/TraceIdInput.tsx | 39 ++ src/components/queryBuilder/utils.test.ts | 346 +------------ src/components/queryBuilder/utils.ts | 216 -------- .../queryBuilder/views/LogsQueryBuilder.tsx | 65 ++- .../queryBuilder/views/TableQueryBuilder.tsx | 26 +- .../views/TimeSeriesQueryBuilder.tsx | 13 +- .../queryBuilder/views/TraceQueryBuilder.tsx | 103 ++-- .../views/logsQueryBuilderHooks.test.ts | 51 +- .../views/logsQueryBuilderHooks.ts | 59 ++- .../views/timeSeriesQueryBuilderHooks.test.ts | 46 +- .../views/timeSeriesQueryBuilderHooks.ts | 46 +- .../views/traceQueryBuilderHooks.test.ts | 63 ++- .../views/traceQueryBuilderHooks.ts | 67 ++- src/data/CHDatasource.test.ts | 22 +- src/data/CHDatasource.ts | 211 ++++---- src/data/adHocFilter.test.ts | 18 +- src/data/adHocFilter.ts | 19 +- src/data/logs.ts | 10 + src/data/sqlGenerator.test.ts | 487 ++++++++++++++---- src/data/sqlGenerator.ts | 449 ++++++++++++++-- src/data/utils.test.ts | 104 +++- src/data/utils.ts | 198 ++++++- src/hooks/useBuilderOptionsState.test.ts | 9 +- src/hooks/useBuilderOptionsState.ts | 11 +- src/hooks/useDatabases.ts | 2 +- src/hooks/useTables.ts | 2 +- src/hooks/useUniqueMapKeys.test.ts | 65 +++ src/hooks/useUniqueMapKeys.ts | 41 ++ src/labels.ts | 115 ++++- src/otel.ts | 67 ++- src/selectors.ts | 82 +-- src/tracking.test.ts | 450 ++++++++++++---- src/tracking.ts | 129 +++-- src/types/config.ts | 30 +- src/types/queryBuilder.ts | 66 ++- src/views/CHConfigEditor.test.tsx | 40 +- src/views/CHConfigEditor.tsx | 132 +++-- src/views/CHConfigEditorHooks.test.ts | 162 +++++- src/views/CHConfigEditorHooks.ts | 138 ++++- src/views/CHQueryEditor.tsx | 6 +- 72 files changed, 3899 insertions(+), 1623 deletions(-) create mode 100644 src/components/configEditor/HttpHeadersConfig.test.tsx create mode 100644 src/components/configEditor/HttpHeadersConfig.tsx create mode 100644 src/components/queryBuilder/TraceIdInput.test.tsx create mode 100644 src/components/queryBuilder/TraceIdInput.tsx create mode 100644 src/hooks/useUniqueMapKeys.test.ts create mode 100644 src/hooks/useUniqueMapKeys.ts diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index d495f59e..35b9a18e 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4.0.0 + - uses: actions/setup-node@v4.0.1 with: node-version-file: '.nvmrc' - name: Install dependencies diff --git a/.github/workflows/pull-request-image.yml b/.github/workflows/pull-request-image.yml index 36da27a9..a22e6714 100644 --- a/.github/workflows/pull-request-image.yml +++ b/.github/workflows/pull-request-image.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Node.js environment - uses: actions/setup-node@v4.0.0 + uses: actions/setup-node@v4.0.1 with: node-version-file: '.nvmrc' @@ -44,7 +44,7 @@ jobs: NODE_OPTIONS: '--max_old_space_size=4096' - name: Archive plugin build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: plugin-dist path: | @@ -56,7 +56,7 @@ jobs: needs: build steps: - name: Download plugin build artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 id: download with: name: plugin-dist diff --git a/.github/workflows/run-frontend-tests.yml b/.github/workflows/run-frontend-tests.yml index f91fdda9..cb0dc718 100644 --- a/.github/workflows/run-frontend-tests.yml +++ b/.github/workflows/run-frontend-tests.yml @@ -28,7 +28,7 @@ jobs: sudo apt-get install -y zip zstd jq git - name: Setup Node.js environment - uses: actions/setup-node@v4.0.0 + uses: actions/setup-node@v4.0.1 with: node-version-file: '.nvmrc' diff --git a/CHANGELOG.md b/CHANGELOG.md index 94558435..31f17875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,69 @@ # Changelog -## Unreleased +## 4.0.0 ### Features -- Add a new optional `path` setting to specify an additional URL path. [#512](https://github.com/grafana/clickhouse-datasource/pull/512) +Version 4.0.0 contains major revisions to the query builder and datasource configuration settings. + +#### Query Builder + +- Completely rebuilt query builder to have specialized editors for Table, Logs, Time Series, and Traces. +- Completely rebuilt SQL generator to support more complicated and dynamic queries. +- Updated query builder options structure to be clearer and support more complex queries. +- Updated database/table selector to be in a more convenient location. Database and table options are automatically selected on initial load. +- Upgraded query builder state management so queries stay consistent when saving/editing/sharing queries. +- Separated Table and Time Series query builders. Table view operates as a catch-all for queries that don't fit the other query types. +- Combined "format" into the query type switcher for simplicity. The query tab now changes the builder view and the display format when on the Explore page. This includes the raw SQL editor. +- Added an OTEL switch for logs and trace views. This will allow for quicker query building for those using the OTEL exporter for ClickHouse. +- Updated Time Series query builder with dedicated Time column. Default filters are added on-load. +- Added an `IS ANYTHING` filter that acts as a placeholder for easily editing later (useful for query templates/bookmarks on the Explore page.) +- Added better support for Map types on the Filter editor. +- LIMIT editor can now be set to 0 to be excluded from the query. +- Table and Time Series views now have a simple / aggregate mode, depending on the query complexity. +- Updated the logs histogram query to use the new query builder options and column hints. +- Added Logs query builder with dedicated Time, Level, and Message columns. Includes OTEL switch for automatically loading OTEL schema columns. Default filters are added on-load. +- Added Trace query builder with dedicated trace columns. Includes OTEL switch for automatically loading OTEL schema columns. Default filters are added on-load. +- Updated data panel filtering to append filters with column hints. Visible in logs view when filtering by a specific level. Instead of referencing a column by name, it will use its hint. +- Order By now lists aggregates by their full name + alias. +- Order By column allows for custom value to be typed in. +- Aggregate column name allows for custom value to be typed in. +- Filter editor allows for custom column names to be typed in. +- Increased width of filter value text input. +- Columns with the `Map*` type now show a `[]` at the end to indicate they are complex types. For example, `SpanAttributes[]`. +- Filter editor now has a dedicated field for map key. You can now select a map column and its key separately. For example, `SpanAttributes['key']`. +- Map types now load a sample of options when editing the `key` for the map. This doesn't include all unique values, but for most datasets it should be a convenience. +- Added column hints, which offers better linking across query components when working with columns and filters. For example, a filter can be added for the `Time` column, even without knowing what the time column name is yet. This enables better SQL generation that is "aware" of a column's intended use. + +### Plugin Backend +- Added migration logic for `v3` configs going to `v4+`. This is applied when the config is loaded when building a database connection. +- `$__timeFilter`, `$__fromTime`, and `$__toTime` macros now convert to `DateTime64(3)` for better server-side type conversion. Also enables millisecond precision time range filtering. + +#### Datasource Configuration +- Added migration script for `v3.x` configurations to `v4+`. This runs automatically when opening/saving the datasource configuration. +- Renamed config value `server` to `host`. +- Renamed config value `timeout` to the more specific `dial_timeout`. +- Updated labeling for port selection. The default port will now change depending on native/http and secure/unsecure setting. +- Rearranged fields and sections to flow better for initial setup of a new datasource. +- Added plugin version to config data for easier config version migrations in the future. +- Added fields for setting default values for database/table. +- Added section for setting default log database/table/columns. Includes OTEL. These are used when using the log query builder. +- Added section for setting default trace database/table/columns. Includes OTEL. These are used when using the trace query builder. +- Added OTEL switches for logs/traces for quicker query building. OTEL defaults to the latest version, and will auto update if kept on this setting. +- Increased width of inputs for typically long values (server URL, path, etc.) +- Allow adding custom HTTP headers with either plain text or secure credentials. [#633](https://github.com/grafana/clickhouse-datasource/pull/633) +- Add `path` setting to specify an additional URL path when using the HTTP protocol. [#512](https://github.com/grafana/clickhouse-datasource/pull/512) + +### Fixes + +- Queries will now remain consistent when reloading/editing a previously saved query. +- Fixed default Ad-Hoc filters. [#650](https://github.com/grafana/clickhouse-datasource/pull/650) +- Fixed Ad-Hoc filters parsing numeric fields. [#629](https://github.com/grafana/clickhouse-datasource/pull/629) +- Fixed majority of usability quirks with redesigned query builder. + +### Upgrades + +- Updated all dependencies to latest compatible versions (Includes Dependabot PRs) ## 3.3.0 diff --git a/DEV_GUIDE.md b/DEV_GUIDE.md index 018a62e1..744ed790 100644 --- a/DEV_GUIDE.md +++ b/DEV_GUIDE.md @@ -70,3 +70,47 @@ docker run -d -p 8443:8443 -p 9440:9440 -p 9000:9000 -p 8123:8123 --name secure- docker exec -it secure-clickhouse-server bash cp /etc/clickhouse-server/my-own-ca.crt /usr/local/share/ca-certificates/root.ca.crt update-ca-certificates + +# Code Structure / Notes + +## Column Hints + +Column hints are used within the query builder and SQL generator to enable flexible and dynamic queries. + +Here's an example of some column hints: +```js +ColumnHint.Time +ColumnHint.LogMessage +ColumnHint.LogLevel +ColumnHint.TraceId +``` + +The easiest example is the time hint (`ColumnHint.Time`). When building a Logs query, we need to know what the primary log time column is: + +```ts +const logTimeColumn: SelectedColumn = { name: 'my_time_column_on_my_table', hint: ColumnHint.Time, alias: 'logTime' }; +``` + +Using the column hint, we can add an `ORDER BY` statement to the query without having to know the actual column name: + +```ts +const logsOrderBy: OrderBy = { name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }; +``` + +Notice how `name` can be left empty, this is because the SQL generator knows to find the final column/alias by the time hint: + +```ts +// Input options +const queryBuilderOptions: QueryBuilderOptions = { + table: 'logs', + columns: [logTimeColumn], + orderBy: [logsOrderBy], + . . . +}; +``` +```sql +-- Final output from SQL generator +SELECT my_time_column_on_my_table as logTime FROM logs ORDER BY logTime ASC +``` + +By adding a simple hint, we can apply filters, orderBys, and other behaviors to the SQL generator without having to reference specific columns. This simplifies the UI logic and user experience by reducing the number of places where a column name needs to be updated. diff --git a/README.md b/README.md index 25168127..a7d183d5 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,63 @@ -# ClickHouse data source for Grafana +# Official ClickHouse data source for Grafana +The ClickHouse data source plugin allows you to query and visualize ClickHouse data in Grafana. Grafana Dashboard Screenshot - Query Analysis Grafana Dashboard Screenshot - Data Analysis - ## Version compatibility -Users on `v8.x` of Grafana are encouraged to continue to use `v2.2.0` of the plugin. -Users on `v9.x` and higher of Grafana can use `v3` however it is `beta` and may contain bugs. +Users on Grafana `v9.x` and higher of Grafana can use `v4`. +Users on Grafana `v8.x` are encouraged to continue using `v2.2.0` of the plugin. -The ClickHouse data source plugin allows you to query and visualize ClickHouse -data from within Grafana. -**As of 2.0 this plugin will only support ad hoc filters when using ClickHouse 22.7+** +\* *As of 2.0 this plugin will only support ad hoc filters when using ClickHouse 22.7+* ## Installation -For detailed instructions on how to install the plugin on Grafana Cloud or -locally, please checkout the [Plugin installation docs](https://grafana.com/docs/grafana/latest/plugins/installation/). +For detailed instructions on how to install the plugin on Grafana Cloud or locally, +please checkout the [Plugin installation docs](https://grafana.com/docs/grafana/latest/plugins/installation/). ## Configuration ### ClickHouse user for the data source Set up an ClickHouse user account with [readonly](https://clickhouse.com/docs/en/operations/settings/permissions-for-queries#settings_readonly) permission and access to -databases and tables you want to query. Please note that Grafana does not -validate that queries are safe. Queries can contain any SQL statement. For -example, statements like `ALTER TABLE system.users DELETE WHERE name='sadUser'` +databases and tables you want to query. +Please note that Grafana does not validate that queries are safe. Queries can contain any SQL statement. +For example, statements like `ALTER TABLE system.users DELETE WHERE name='sadUser'` and `DROP TABLE sadTable;` would be executed. To configure a readonly user, follow these steps: 1. Create a `readonly` user profile following the [Creating Users and Roles in ClickHouse](https://clickhouse.com/docs/en/operations/access-rights) guide. 2. Ensure the `readonly` user has enough permission to modify the `max_execution_time` setting required by the underlying [clickhouse-go client](https://github.com/ClickHouse/clickhouse-go/). 3. If you're using a public Clickhouse instance, it's not recommended to set `readonly=2` in the `readonly` profile. Instead, leave `readonly=1` and set the constraint type of `max_execution_time` to [changeable_in_readonly](https://clickhouse.com/docs/en/operations/settings/constraints-on-settings) to allow modification of this setting. + ### ClickHouse protocol support -The plugin supports both `HTTP` and `Native` (default) transport protocols. This can be enabled in the configuration via the `protocol` configuration parameter. Both protocols exchange data with ClickHouse using optimized native format. +The plugin supports both `Native` (default) and `HTTP` transport protocols. +This can be enabled in the configuration via the `protocol` configuration parameter. +Both protocols exchange data with ClickHouse using optimized native format. -Note that the default ports for `HTTP/s` and `Native` differ: +Note that the default ports for `HTTP/S` and `Native` differ: - HTTP - 8123 - HTTPS - 8443 - Native - 9000 - Native with TLS - 9440 -### Manual configuration +### Manual configuration via UI -Once the plugin is installed on your Grafana instance, follow [these -instructions](https://grafana.com/docs/grafana/latest/datasources/add-a-data-source/) +Once the plugin is installed on your Grafana instance, follow +[these instructions](https://grafana.com/docs/grafana/latest/datasources/add-a-data-source/) to add a new ClickHouse data source, and enter configuration options. ### With a configuration file -It is possible to configure data sources using configuration files with -Grafana’s provisioning system. To read about how it works, including all the -settings that you can set for this data source, refer to [Provisioning Grafana -data sources](https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources). +It is possible to configure data sources using configuration files with Grafana’s provisioning system. +To read about how it works, refer to +[Provisioning Grafana data sources](https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources). Here are some provisioning examples for this data source using basic authentication: @@ -69,18 +69,50 @@ datasources: jsonData: defaultDatabase: database port: 9000 - server: localhost + host: localhost username: username tlsSkipVerify: false + # tlsAuth: + # tlsAuthWithCACert: + # secure: + # dialTimeout: + # queryTimeout: + # protocol: + # defaultTable: + # httpHeaders: + # - name: X-Example-Header + # secure: false + # value: + # - name: Authorization + # secure: true + # logs: + # defaultDatabase: + # defaultTable: + # otelEnabled: + # otelVersion: + # timeColumn: + # ...Column: + # traces: + # defaultDatabase: + # defaultTable: + # otelEnabled: + # otelVersion: + # durationUnit: + # traceIdColumn: + # ...Column: secureJsonData: password: password + # tlsCACert: + # tlsClientCert: + # tlsClientKey: + # secureHttpHeaders.Authorization: ``` ## Building queries -The query editor allows you to query ClickHouse to return time series or -tabular data. Queries can contain macros which simplify syntax and allow for -dynamic parts. +Queries can be built using the raw SQL editor or the query builder. +Queries can contain macros which simplify syntax and allow for +dynamic SQL generation. ### Time series @@ -161,11 +193,10 @@ WHERE $__timeFilter(date_time) | Macro | Description | Output example | |----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------| -| *$__timeFilter(columnName)* | Replaced by a conditional that filters the data (using the provided column) based on the time range of the panel in seconds | `time >= '1480001790' AND time <= '1482576232' )` | +| *$__timeFilter(columnName)* | Replaced by a conditional that filters the data (using the provided column) based on the time range of the panel in milliseconds | `time >= toDateTime64(1480001790/1000, 3) AND time <= toDateTime64(1482576232/1000, 3) )` | | *$__dateFilter(columnName)* | Replaced by a conditional that filters the data (using the provided column) based on the date range of the panel | `date >= '2022-10-21' AND date <= '2022-10-23' )` | -| *$__timeFilter_ms(columnName)* | Replaced by a conditional that filters the data (using the provided column) based on the time range of the panel in milliseconds | `time >= '1480001790671' AND time <= '1482576232479' )` | -| *$__fromTime* | Replaced by the starting time of the range of the panel casted to DateTime | `toDateTime(intDiv(1415792726371,1000))` | -| *$__toTime* | Replaced by the ending time of the range of the panel casted to DateTime | `toDateTime(intDiv(1415792726371,1000))` | +| *$__fromTime* | Replaced by the starting time of the range of the panel casted to `DateTime64(3)` | `toDateTime64(1415792726371/1000, 3)` | +| *$__toTime* | Replaced by the ending time of the range of the panel casted to `DateTime64(3)` | `toDateTime64(1415792726371/1000, 3)` | | *$__interval_s* | Replaced by the interval in seconds | `20` | | *$__timeInterval(columnName)* | Replaced by a function calculating the interval based on window size in seconds, useful when grouping | `toStartOfInterval(toDateTime(column), INTERVAL 20 second)` | | *$__timeInterval_ms(columnName)* | Replaced by a function calculating the interval based on window size in milliseconds, useful when grouping | `toStartOfInterval(toDateTime64(column, 3), INTERVAL 20 millisecond)` | diff --git a/cspell.config.json b/cspell.config.json index c501bc7c..81d20c48 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -10,6 +10,8 @@ "mage_output_file.go" ], "words": [ + "concats", + "traceid", "aggregatable", "aheads", "apikey", diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 2497f1cf..a27de4d9 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -160,7 +160,7 @@ func startGrafana(client *dagger.Client, clickHouseDist *dagger.Directory, click From("grafana/grafana:main"). WithFile("/etc/grafana/grafana.ini", client.Host().File("e2e/custom.ini")). WithMountedDirectory("/var/lib/grafana/plugins/clickhouse-datasource", clickHouseDist). - WithServiceBinding("clickhouse", clickhouseContainer). + WithServiceBinding("clickhouse", clickhouseContainer.AsService()). WithExposedPort(3000) fmt.Println("Grafana built") @@ -183,7 +183,7 @@ func runK6(client *dagger.Client, ctx context.Context, grafanaContainer *dagger. value, err := client. Container(). From("grafana/k6:master-with-browser"). - WithServiceBinding("grafana", grafanaContainer). + WithServiceBinding("grafana", grafanaContainer.AsService()). WithEnvVariable("K6_BROWSER_ARGS", "no-sandbox"). WithFile("k6.test.js", testFile). WithExec([]string{"run", "k6.test.js"}, dagger.ContainerWithExecOpts{InsecureRootCapabilities: true}).Stderr(ctx) diff --git a/go.mod b/go.mod index cea0943f..acec1266 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,18 @@ go 1.21 toolchain go1.21.4 require ( - dagger.io/dagger v0.8.4 - github.com/ClickHouse/clickhouse-go/v2 v2.16.0 + dagger.io/dagger v0.9.6 + github.com/ClickHouse/clickhouse-go/v2 v2.17.1 github.com/docker/docker v24.0.7+incompatible github.com/docker/go-units v0.5.0 - github.com/grafana/grafana-plugin-sdk-go v0.195.0 + github.com/grafana/grafana-plugin-sdk-go v0.199.0 github.com/grafana/sqlds/v2 v2.7.2 - github.com/paulmach/orb v0.10.0 + github.com/paulmach/orb v0.11.0 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.8.4 - github.com/testcontainers/testcontainers-go v0.26.0 - golang.org/x/net v0.19.0 + github.com/testcontainers/testcontainers-go v0.27.0 + golang.org/x/net v0.20.0 ) require ( @@ -26,14 +26,13 @@ require ( github.com/apache/arrow/go/v13 v13.0.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/goccy/go-json v0.10.0 // indirect - github.com/iancoleman/strcase v0.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/vektah/gqlparser/v2 v2.5.6 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect ) @@ -43,14 +42,14 @@ require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/ClickHouse/ch-go v0.58.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.1 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/andybalholm/brotli v1.0.6 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20230816033919-17ee49f3eb4f // indirect - github.com/containerd/containerd v1.7.7 // indirect + github.com/containerd/containerd v1.7.11 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -58,7 +57,7 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a // indirect github.com/fatih/color v1.15.0 // indirect - github.com/getkin/kin-openapi v0.122.0 // indirect + github.com/getkin/kin-openapi v0.120.0 // indirect github.com/go-faster/city v1.0.1 // indirect github.com/go-faster/errors v0.6.1 // indirect github.com/go-logr/logr v1.3.0 // indirect @@ -70,12 +69,12 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/flatbuffers v23.1.21+incompatible // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.2 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/invopop/yaml v0.2.0 // indirect @@ -113,13 +112,13 @@ require ( github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shirou/gopsutil/v3 v3.23.11 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect @@ -142,18 +141,18 @@ require ( go.opentelemetry.io/otel/sdk v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + golang.org/x/tools v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/grpc v1.60.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4c33d278..dbf415ea 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopT cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -dagger.io/dagger v0.8.4 h1:2zNu40cBvPZc/CSgMnLRfayfQj5VtbJlDtWJyWGwGSQ= -dagger.io/dagger v0.8.4/go.mod h1:Nwl7WI8YETaZhGjPJvkiOZnKLJXBaJOkSarp5m4+FxA= +dagger.io/dagger v0.9.6 h1:izajlnhz4VuFusdmq4qDBdWQ7w62tEqhREzkx9emFSk= +dagger.io/dagger v0.9.6/go.mod h1:ic2UD6gS5iBp2e6VWPxyb7h6VpAyhFN6U7/TDlriox8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/99designs/gqlgen v0.17.31 h1:VncSQ82VxieHkea8tz11p7h/zSbvHSxSDZfywqWt158= @@ -19,17 +19,16 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= -github.com/ClickHouse/clickhouse-go/v2 v2.16.0 h1:rhMfnPewXPnY4Q4lQRGdYuTLRBRKJEIEYHtbUMrzmvI= -github.com/ClickHouse/clickhouse-go/v2 v2.16.0/go.mod h1:J7SPfIxwR+x4mQ+o8MLSe0oY50NNntEqCIjFe/T1VPM= +github.com/ClickHouse/clickhouse-go/v2 v2.17.1 h1:ZCmAYWpu75IyEi7+Yrs/uaAjiCGY5wfW5kXo64exkX4= +github.com/ClickHouse/clickhouse-go/v2 v2.17.1/go.mod h1:rkGTvFDTLqLIm0ma+13xmcCfr/08Gvs7KmFt1tgiWHQ= github.com/Khan/genqlient v0.6.0 h1:Bwb1170ekuNIVIwTJEqvO8y7RxBxXu639VJOkKSrwAk= github.com/Khan/genqlient v0.6.0/go.mod h1:rvChwWVTqXhiapdhLDV4bp9tz/Xvtewwkon4DpWWCRM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= -github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= -github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= @@ -56,8 +55,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/containerd v1.7.7 h1:QOC2K4A42RQpcrZyptP6z9EJZnlHfHJUfZrAAHe15q4= -github.com/containerd/containerd v1.7.7/go.mod h1:3c4XZv6VeT9qgf9GMTxNTMFxGJrGpI2vz1yk4ye+YY8= +github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= +github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= @@ -99,8 +98,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10= -github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= @@ -148,15 +147,15 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grafana/grafana-plugin-sdk-go v0.195.0 h1:8aL7FNY+J4QJzyg/qJmLsiEtMMQMbXXJjsOOvmNbLWM= -github.com/grafana/grafana-plugin-sdk-go v0.195.0/go.mod h1:USZmBY13skEB0Ia2vyegQW0KTDQW7iQVJCsyBWlBjN4= +github.com/grafana/grafana-plugin-sdk-go v0.199.0 h1:IkVvdxP8Glz2gsZEBikzqB/ypksvovFvKTIv/Wax1cs= +github.com/grafana/grafana-plugin-sdk-go v0.199.0/go.mod h1:XDTbWaz2ajuAubya8kaogWt6nfeWk2mLDOKUNZH/wJA= github.com/grafana/sqlds/v2 v2.7.2 h1:5bkY9QO5Nc4uAWm3aHF2fHWcOSWhqJmfdJfPRHJVJoo= github.com/grafana/sqlds/v2 v2.7.2/go.mod h1:u5FkkJfuL6EwqesXWjV6cNw7aS5F51sCo/0Op57C8rs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= @@ -165,14 +164,12 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 h1:dygLcbEBA+t/P7ck6a8AkXv6juQ4cK0RHBoh32jxhHM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2/go.mod h1:Ap9RLCIJVtgQg1/BBgVEfypOAySvvlcpcVQkSzJCH4Y= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= +github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= -github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= @@ -267,8 +264,8 @@ github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVn github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s= -github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/orb v0.11.0 h1:JfVXJUBeH9ifc/OrhBY0lL16QsmPgpCHMlqSSYhcgAA= +github.com/paulmach/orb v0.11.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= @@ -281,15 +278,15 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -300,8 +297,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= +github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -336,8 +333,8 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/testcontainers/testcontainers-go v0.26.0 h1:uqcYdoOHBy1ca7gKODfBd9uTHVK3a7UL848z09MVZ0c= -github.com/testcontainers/testcontainers-go v0.26.0/go.mod h1:ICriE9bLX5CLxL9OFQ2N+2N+f+803LNJ1utJb1+Inx0= +github.com/testcontainers/testcontainers-go v0.27.0 h1:IeIrJN4twonTDuMuBNQdKZ+K97yd7VrmNGu+lDpYcDk= +github.com/testcontainers/testcontainers-go v0.27.0/go.mod h1:+HgYZcd17GshBUZv9b+jKFJ198heWPQq3KQIp2+N+7U= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -404,19 +401,19 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -426,8 +423,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= @@ -437,8 +434,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -463,12 +460,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -487,8 +484,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -499,8 +496,8 @@ gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -515,13 +512,13 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/package.json b/package.json index 5248f67c..644e0ff6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clickhouse-datasource", - "version": "4.0.0-beta", + "version": "4.0.0", "description": "Clickhouse Datasource", "engines": { "node": ">=16" diff --git a/pkg/macros/macros.go b/pkg/macros/macros.go index aa17bd18..48de1ea5 100644 --- a/pkg/macros/macros.go +++ b/pkg/macros/macros.go @@ -26,7 +26,7 @@ func newTimeFilter(queryType timeQueryType, query *sqlds.Query) (string, error) if queryType == timeQueryTypeTo { date = query.TimeRange.To } - return fmt.Sprintf("toDateTime(intDiv(%d,1000))", date.UnixMilli()), nil + return fmt.Sprintf("toDateTime64(%d/1000, 3)", date.UnixMilli()), nil } // FromTimeFilter return time filter query based on grafana's timepicker's from time @@ -46,11 +46,11 @@ func TimeFilter(query *sqlds.Query, args []string) (string, error) { var ( column = args[0] - from = query.TimeRange.From.UTC().Unix() - to = query.TimeRange.To.UTC().Unix() + from = query.TimeRange.From.UTC().UnixMilli() + to = query.TimeRange.To.UTC().UnixMilli() ) - return fmt.Sprintf("%s >= '%d' AND %s <= '%d'", column, from, column, to), nil + return fmt.Sprintf("%s >= toDateTime64(%d/1000, 3) AND %s <= toDateTime64(%d/1000, 3)", column, from, column, to), nil } func DateFilter(query *sqlds.Query, args []string) (string, error) { diff --git a/pkg/macros/macros_test.go b/pkg/macros/macros_test.go index 0b596cb0..b960813b 100644 --- a/pkg/macros/macros_test.go +++ b/pkg/macros/macros_test.go @@ -44,7 +44,7 @@ func TestMacroFromTimeFilter(t *testing.T) { }{ { name: "should return timefilter", - want: "toDateTime(intDiv(1415792726371,1000))", + want: "toDateTime64(1415792726371/1000, 3)", }, } for _, tt := range tests { @@ -76,7 +76,7 @@ func TestMacroToTimeFilter(t *testing.T) { }{ { name: "should return timefilter", - want: "toDateTime(intDiv(1447328726371,1000))", + want: "toDateTime64(1447328726371/1000, 3)", }, } for _, tt := range tests { @@ -150,10 +150,10 @@ func TestInterpolate(t *testing.T) { } tests := []test{ - {input: "select * from foo where $__timeFilter(cast(sth as timestamp))", output: "select * from foo where cast(sth as timestamp) >= '1415792726' AND cast(sth as timestamp) <= '1447328726'", name: "clickhouse timeFilter"}, - {input: "select * from foo where $__timeFilter(cast(sth as timestamp) )", output: "select * from foo where cast(sth as timestamp) >= '1415792726' AND cast(sth as timestamp) <= '1447328726'", name: "clickhouse timeFilter with empty spaces"}, - {input: "select * from foo where ( date >= $__fromTime and date <= $__toTime ) limit 100", output: "select * from foo where ( date >= toDateTime(intDiv(1415792726371,1000)) and date <= toDateTime(intDiv(1447328726371,1000)) ) limit 100", name: "clickhouse fromTime and toTime"}, - {input: "select * from foo where ( date >= $__fromTime ) and ( date <= $__toTime ) limit 100", output: "select * from foo where ( date >= toDateTime(intDiv(1415792726371,1000)) ) and ( date <= toDateTime(intDiv(1447328726371,1000)) ) limit 100", name: "clickhouse fromTime and toTime inside a complex clauses"}, + {input: "select * from foo where $__timeFilter(cast(sth as timestamp))", output: "select * from foo where cast(sth as timestamp) >= toDateTime64(1415792726371/1000, 3) AND cast(sth as timestamp) <= toDateTime64(1447328726371/1000, 3)", name: "clickhouse timeFilter"}, + {input: "select * from foo where $__timeFilter(cast(sth as timestamp) )", output: "select * from foo where cast(sth as timestamp) >= toDateTime64(1415792726371/1000, 3) AND cast(sth as timestamp) <= toDateTime64(1447328726371/1000, 3)", name: "clickhouse timeFilter with empty spaces"}, + {input: "select * from foo where ( date >= $__fromTime and date <= $__toTime ) limit 100", output: "select * from foo where ( date >= toDateTime64(1415792726371/1000, 3) and date <= toDateTime64(1447328726371/1000, 3) ) limit 100", name: "clickhouse fromTime and toTime"}, + {input: "select * from foo where ( date >= $__fromTime ) and ( date <= $__toTime ) limit 100", output: "select * from foo where ( date >= toDateTime64(1415792726371/1000, 3) ) and ( date <= toDateTime64(1447328726371/1000, 3) ) limit 100", name: "clickhouse fromTime and toTime inside a complex clauses"}, } for i, tc := range tests { diff --git a/pkg/plugin/driver.go b/pkg/plugin/driver.go index efd80791..4eb9db83 100644 --- a/pkg/plugin/driver.go +++ b/pkg/plugin/driver.go @@ -160,6 +160,7 @@ func (h *Clickhouse) Connect(config backend.DataSourceInstanceSettings, message Compression: &clickhouse.Compression{ Method: compression, }, + HttpHeaders: settings.HttpHeaders, DialTimeout: time.Duration(t) * time.Second, ReadTimeout: time.Duration(qt) * time.Second, Protocol: protocol, diff --git a/pkg/plugin/driver_test.go b/pkg/plugin/driver_test.go index 014cedb2..c285bed8 100644 --- a/pkg/plugin/driver_test.go +++ b/pkg/plugin/driver_test.go @@ -79,13 +79,23 @@ func TestMain(m *testing.M) { adminHostPath = "../../config/admin.21.8.xml" } req := testcontainers.ContainerRequest{ - Image: fmt.Sprintf("clickhouse/clickhouse-server:%s", chVersion), + Env: map[string]string{ + "TZ": time.Local.String(), + }, ExposedPorts: []string{"9000/tcp", "8123/tcp"}, - WaitingFor: wait.ForLog("Ready for connections"), - Mounts: []testcontainers.ContainerMount{ - testcontainers.BindMount(path.Join(cwd, customHostPath), "/etc/clickhouse-server/config.d/custom.xml"), - testcontainers.BindMount(path.Join(cwd, adminHostPath), "/etc/clickhouse-server/users.d/admin.xml"), + Files: []testcontainers.ContainerFile{ + { + ContainerFilePath: "/etc/clickhouse-server/config.d/custom.xml", + FileMode: 0644, + HostFilePath: path.Join(cwd, customHostPath), + }, + { + ContainerFilePath: "/etc/clickhouse-server/users.d/admin.xml", + FileMode: 0644, + HostFilePath: path.Join(cwd, adminHostPath), + }, }, + Image: fmt.Sprintf("clickhouse/clickhouse-server:%s", chVersion), Resources: container.Resources{ Ulimits: []*units.Ulimit{ { @@ -95,9 +105,7 @@ func TestMain(m *testing.M) { }, }, }, - Env: map[string]string{ - "TZ": time.Local.String(), - }, + WaitingFor: wait.ForLog("Ready for connections"), } clickhouseContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, diff --git a/pkg/plugin/settings.go b/pkg/plugin/settings.go index e6c19854..90f3c312 100644 --- a/pkg/plugin/settings.go +++ b/pkg/plugin/settings.go @@ -3,6 +3,7 @@ package plugin import ( "encoding/json" "fmt" + "github.com/ClickHouse/clickhouse-go/v2" "strconv" "strings" "time" @@ -34,7 +35,8 @@ type Settings struct { DialTimeout string `json:"dialTimeout,omitempty"` QueryTimeout string `json:"queryTimeout,omitempty"` - CustomSettings []CustomSetting `json:"customSettings"` + HttpHeaders map[string]string `json:"-"` + CustomSettings []CustomSetting `json:"customSettings"` ProxyOptions *proxy.Options } @@ -43,6 +45,8 @@ type CustomSetting struct { Value string `json:"value"` } +const secureHeaderKeyPrefix = "secureHttpHeaders." + func (settings *Settings) isValid() (err error) { if settings.Host == "" { return ErrorMessageInvalidHost @@ -187,8 +191,12 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, settings.TlsClientKey = tlsClientKey } - // proxy options are only able to be loaded via environment variables - // currently, so we pass `nil` here so they are loaded with defaults + if settings.Protocol == clickhouse.HTTP.String() { + settings.HttpHeaders = loadHttpHeaders(jsonData, config.DecryptedSecureJSONData) + } + + // proxy options are currently only able to load via environment variables, + // so we pass `nil` here so that they are loaded with defaults proxyOpts, err := config.ProxyOptions(nil) if err == nil && proxyOpts != nil { @@ -203,3 +211,31 @@ func LoadSettings(config backend.DataSourceInstanceSettings) (settings Settings, return settings, settings.isValid() } + +// loadHttpHeaders loads secure and plain text headers from the config +func loadHttpHeaders(jsonData map[string]interface{}, secureJsonData map[string]string) map[string]string { + httpHeaders := make(map[string]string) + + if jsonData["httpHeaders"] != nil { + httpHeadersRaw := jsonData["httpHeaders"].([]interface{}) + + for _, rawHeader := range httpHeadersRaw { + header, _ := rawHeader.(map[string]interface{}) + headerName, _ := header["name"].(string) + headerName = strings.TrimSpace(headerName) + headerValue, _ := header["value"].(string) + if headerName != "" && headerValue != "" { + httpHeaders[headerName] = headerValue + } + } + } + + for k, v := range secureJsonData { + if v != "" && strings.HasPrefix(k, secureHeaderKeyPrefix) { + headerName := strings.TrimSpace(k[len(secureHeaderKeyPrefix):]) + httpHeaders[headerName] = v + } + } + + return httpHeaders +} diff --git a/pkg/plugin/settings_test.go b/pkg/plugin/settings_test.go index 6805f3b9..d4f62626 100644 --- a/pkg/plugin/settings_test.go +++ b/pkg/plugin/settings_test.go @@ -3,6 +3,7 @@ package plugin import ( "errors" "fmt" + "github.com/ClickHouse/clickhouse-go/v2" "reflect" "testing" "time" @@ -27,15 +28,29 @@ func TestLoadSettings(t *testing.T) { name: "should parse and set all json fields correctly", args: args{ config: backend.DataSourceInstanceSettings{ - UID: "ds-uid", - JSONData: []byte(`{ "host": "foo", "port": 443, "path": "custom-path", "username": "baz", "defaultDatabase":"example", "tlsSkipVerify": true, "tlsAuth" : true, "tlsAuthWithCACert": true, "dialTimeout": "10", "enableSecureSocksProxy": true}`), - DecryptedSecureJSONData: map[string]string{"password": "bar", "tlsCACert": "caCert", "tlsClientCert": "clientCert", "tlsClientKey": "clientKey", "secureSocksProxyPassword": "test"}, + UID: "ds-uid", + JSONData: []byte(`{ + "host": "foo", "port": 443, + "path": "custom-path", "protocol": "http", + "username": "baz", + "defaultDatabase":"example", "tlsSkipVerify": true, "tlsAuth" : true, + "tlsAuthWithCACert": true, "dialTimeout": "10", "enableSecureSocksProxy": true, + "httpHeaders": [{ "name": " test-plain-1 ", "value": "value-1", "secure": false }] + }`), + DecryptedSecureJSONData: map[string]string{ + "password": "bar", + "tlsCACert": "caCert", "tlsClientCert": "clientCert", "tlsClientKey": "clientKey", + "secureSocksProxyPassword": "test", + "secureHttpHeaders. test-secure-2 ": "value-2", + "secureHttpHeaders.test-secure-3": "value-3", + }, }, }, wantSettings: Settings{ Host: "foo", Port: 443, Path: "custom-path", + Protocol: clickhouse.HTTP.String(), Username: "baz", DefaultDatabase: "example", InsecureSkipVerify: true, @@ -47,6 +62,11 @@ func TestLoadSettings(t *testing.T) { TlsClientKey: "clientKey", DialTimeout: "10", QueryTimeout: "60", + HttpHeaders: map[string]string{ + "test-plain-1": "value-1", + "test-secure-2": "value-2", + "test-secure-3": "value-3", + }, ProxyOptions: &proxy.Options{ Enabled: true, Auth: &proxy.AuthOptions{ diff --git a/src/__mocks__/ConfigEditor.ts b/src/__mocks__/ConfigEditor.ts index 9f43d6f2..6abe9a2c 100644 --- a/src/__mocks__/ConfigEditor.ts +++ b/src/__mocks__/ConfigEditor.ts @@ -12,7 +12,7 @@ export const mockConfigEditorProps = (overrides?: Partial): ConfigEdit port: 443, path: '', username: 'user', - protocol: 'native', + protocol: 'http', ...overrides, }, }, diff --git a/src/__mocks__/datasource.ts b/src/__mocks__/datasource.ts index f0d48cb7..08840acb 100644 --- a/src/__mocks__/datasource.ts +++ b/src/__mocks__/datasource.ts @@ -3,47 +3,54 @@ import { Protocol } from 'types/config'; import { CHQuery, EditorType } from 'types/sql'; import { QueryType } from 'types/queryBuilder'; import { Datasource } from '../data/CHDatasource'; +import { pluginVersion } from 'utils/version'; -export const mockDatasource = new Datasource({ - id: 1, - uid: 'clickhouse_ds', - type: 'grafana-clickhouse-datasource', - name: 'ClickHouse', - jsonData: { - host: 'foo.com', - port: 443, - path: '', - username: 'user', - defaultDatabase: 'foo', - defaultTable: 'bar', - protocol: Protocol.Native, - }, - readOnly: true, - access: 'direct', - meta: { - id: 'grafana-clickhouse-datasource', +export const newMockDatasource = (): Datasource => { + const mockDatasource = new Datasource({ + id: 1, + uid: 'clickhouse_ds', + type: 'grafana-clickhouse-datasource', name: 'ClickHouse', - type: PluginType.datasource, - module: '', - baseUrl: '', - info: { - description: '', - screenshots: [], - updated: '', - version: '', - logos: { - small: '', - large: '', - }, - author: { - name: '', + jsonData: { + version: pluginVersion, + host: 'foo.com', + port: 443, + path: '', + username: 'user', + defaultDatabase: 'foo', + defaultTable: 'bar', + protocol: Protocol.Native, + }, + readOnly: true, + access: 'direct', + meta: { + id: 'grafana-clickhouse-datasource', + name: 'ClickHouse', + type: PluginType.datasource, + module: '', + baseUrl: '', + info: { + description: '', + screenshots: [], + updated: '', + version: '', + logos: { + small: '', + large: '', + }, + author: { + name: '', + }, + links: [], }, - links: [], }, - }, -}); + }); + + mockDatasource.adHocFiltersStatus = 1; // most tests should skip checking the CH version. We will set ad hoc filters to enabled to avoid running the CH version check + return mockDatasource; +}; -mockDatasource.adHocFiltersStatus = 1; // most tests should skip checking the CH version. We will set ad hoc filters to enabled to avoid running the CH version check +export const mockDatasource = newMockDatasource(); export const mockQuery: CHQuery = { pluginVersion: '', diff --git a/src/components/configEditor/HttpHeadersConfig.test.tsx b/src/components/configEditor/HttpHeadersConfig.test.tsx new file mode 100644 index 00000000..206f7ac6 --- /dev/null +++ b/src/components/configEditor/HttpHeadersConfig.test.tsx @@ -0,0 +1,123 @@ +import React from 'react'; +import { render, fireEvent, renderHook } from '@testing-library/react'; +import { HttpHeadersConfig, useConfiguredSecureHttpHeaders } from './HttpHeadersConfig'; +import { selectors as allSelectors } from 'selectors'; +import { CHHttpHeader } from 'types/config'; +import { KeyValue } from '@grafana/data'; + +describe('HttpHeadersConfig', () => { + const selectors = allSelectors.components.Config.HttpHeaderConfig; + + it('should render', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should not call onHttpHeadersChange when header is added', () => { + const onHttpHeadersChange = jest.fn(); + const result = render( + + ); + expect(result.container.firstChild).not.toBeNull(); + + const addHeaderButton = result.getByTestId(selectors.addHeaderButton); + expect(addHeaderButton).toBeInTheDocument(); + fireEvent.click(addHeaderButton); + + expect(onHttpHeadersChange).toHaveBeenCalledTimes(0); + }); + + it('should call onHttpHeadersChange when header is updated', () => { + const onHttpHeadersChange = jest.fn(); + const result = render( + + ); + expect(result.container.firstChild).not.toBeNull(); + + const addHeaderButton = result.getByTestId(selectors.addHeaderButton); + expect(addHeaderButton).toBeInTheDocument(); + fireEvent.click(addHeaderButton); + + const headerEditor = result.getByTestId(selectors.headerEditor); + expect(headerEditor).toBeInTheDocument(); + + const headerNameInput = result.getByTestId(selectors.headerNameInput); + expect(headerNameInput).toBeInTheDocument(); + fireEvent.change(headerNameInput, { target: { value: 'x-test ' } }); // with space in name + fireEvent.blur(headerNameInput); + expect(headerNameInput).toHaveValue('x-test '); + expect(onHttpHeadersChange).toHaveBeenCalledTimes(1); + + const headerValueInput = result.getByTestId(selectors.headerValueInput); + expect(headerValueInput).toBeInTheDocument(); + fireEvent.change(headerValueInput, { target: { value: 'test value' } }); + fireEvent.blur(headerValueInput); + expect(headerValueInput).toHaveValue('test value'); + expect(onHttpHeadersChange).toHaveBeenCalledTimes(2); + + const headerSecureSwitch = result.getByTestId(selectors.headerSecureSwitch); + expect(headerSecureSwitch).toBeInTheDocument(); + fireEvent.click(headerSecureSwitch); + fireEvent.blur(headerSecureSwitch); + expect(onHttpHeadersChange).toHaveBeenCalledTimes(3); + + const expected: CHHttpHeader[] = [ + { name: 'x-test', value: 'test value', secure: true } // without space in name + ]; + expect(onHttpHeadersChange).toHaveBeenCalledWith(expect.objectContaining(expected)); + }); + + it('should call onHttpHeadersChange when header is removed', () => { + const onHttpHeadersChange = jest.fn(); + const result = render( + + ); + expect(result.container.firstChild).not.toBeNull(); + + const removeHeaderButton = result.getAllByTestId(selectors.removeHeaderButton)[0]; // Get 1st + expect(removeHeaderButton).toBeInTheDocument(); + fireEvent.click(removeHeaderButton); + + const expected: CHHttpHeader[] = [ + { name: 'x-test-2', value: 'test value 2', secure: false } + ]; + expect(onHttpHeadersChange).toHaveBeenCalledTimes(1); + expect(onHttpHeadersChange).toHaveBeenCalledWith(expect.objectContaining(expected)); + }); +}); + + +describe('useConfiguredSecureHttpHeaders', () => { + it('returns unique set of secure header keys', async () => { + const fields: KeyValue = { + 'otherKey': true, + 'otherOtherKey': false, + 'secureHttpHeaders.a': true, + 'secureHttpHeaders.b': true, + 'secureHttpHeaders.c': false, + }; + + const hook = renderHook(() => useConfiguredSecureHttpHeaders(fields)); + const result = hook.result.current; + + expect(result.size).toBe(2); + expect(result.has('a')).toBe(true); + expect(result.has('b')).toBe(true); + expect(result.has('c')).toBe(false); + }); +}); diff --git a/src/components/configEditor/HttpHeadersConfig.tsx b/src/components/configEditor/HttpHeadersConfig.tsx new file mode 100644 index 00000000..e53ac7b8 --- /dev/null +++ b/src/components/configEditor/HttpHeadersConfig.tsx @@ -0,0 +1,175 @@ +import React, { ChangeEvent, useMemo, useState } from 'react'; +import { ConfigSection } from '@grafana/experimental'; +import { Input, Field, HorizontalGroup, Switch, SecretInput, Button } from '@grafana/ui'; +import { CHHttpHeader } from 'types/config'; +import allLabels from 'labels'; +import { styles } from 'styles'; +import { selectors as allSelectors } from 'selectors'; +import { KeyValue } from '@grafana/data'; + +interface HttpHeadersConfigProps { + headers?: CHHttpHeader[]; + secureFields: KeyValue; + onHttpHeadersChange: (v: CHHttpHeader[]) => void; +} + +export const HttpHeadersConfig = (props: HttpHeadersConfigProps) => { + const { secureFields, onHttpHeadersChange } = props; + const configuredSecureHeaders = useConfiguredSecureHttpHeaders(secureFields); + const [headers, setHeaders] = useState(props.headers || []); + const labels = allLabels.components.Config.HttpHeadersConfig; + const selectors = allSelectors.components.Config.HttpHeaderConfig; + + const addHeader = () => setHeaders([...headers, { name: '', value: '', secure: false }]); + const removeHeader = (index: number) => { + const nextHeaders: CHHttpHeader[] = headers.slice(); + nextHeaders.splice(index, 1); + setHeaders(nextHeaders); + onHttpHeadersChange(nextHeaders); + }; + const updateHeader = (index: number, header: CHHttpHeader) => { + const nextHeaders: CHHttpHeader[] = headers.slice(); + header.name = header.name.trim(); + nextHeaders[index] = header; + setHeaders(nextHeaders); + onHttpHeadersChange(nextHeaders); + }; + + return ( + + {headers.map((header, index) => ( + updateHeader(index, header)} + onRemove={() => removeHeader(index)} + /> + ))} + + + ); +} + +interface HttpHeaderEditorProps { + name: string; + value: string; + secure: boolean; + isSecureConfigured: boolean; + onHeaderChange: (v: CHHttpHeader) => void; + onRemove?: () => void; +} + +const HttpHeaderEditor = (props: HttpHeaderEditorProps) => { + const { onHeaderChange, onRemove } = props; + const [name, setName] = useState(props.name); + const [value, setValue] = useState(props.value); + const [secure, setSecure] = useState(props.secure); + const [isSecureConfigured, setSecureConfigured] = useState(props.isSecureConfigured); + const labels = allLabels.components.Config.HttpHeadersConfig; + const selectors = allSelectors.components.Config.HttpHeaderConfig; + + const onUpdate = () => { + onHeaderChange({ + name, + value, + secure + }); + } + + let valueInput; + if (secure) { + valueInput = setSecureConfigured(false)} + onChange={(e: ChangeEvent) => setValue(e.target.value)} + onBlur={() => onUpdate()} + />; + } else { + valueInput = ) => setValue(e.target.value)} + onBlur={() => onUpdate()} + />; + } + + const headerValueLabel = secure ? labels.secureHeaderValueLabel : labels.insecureHeaderValueLabel; + return ( +
+ + + ) => setName(e.target.value)} + onBlur={() => onUpdate()} + /> + + + {valueInput} + + { !isSecureConfigured && + + setSecure(e.currentTarget.checked)} + onBlur={() => onUpdate()} + /> + + } + { onRemove && +
+ ); +} + +/** + * Returns a Set of all secured headers that are configured + */ +export const useConfiguredSecureHttpHeaders = (secureJsonFields: KeyValue): Set => { + return useMemo(() => { + const secureHeaders = new Set(); + for (let key in secureJsonFields) { + if (key.startsWith('secureHttpHeaders.') && secureJsonFields[key]) { + secureHeaders.add(key.substring(key.indexOf('.') + 1)); + } + } + return secureHeaders; + }, [secureJsonFields]); +}; diff --git a/src/components/configEditor/LogsConfig.tsx b/src/components/configEditor/LogsConfig.tsx index 0cb7484e..bd017e4f 100644 --- a/src/components/configEditor/LogsConfig.tsx +++ b/src/components/configEditor/LogsConfig.tsx @@ -45,6 +45,7 @@ export const LogsConfig = (props: LogsConfigProps) => { title={labels.title} description={labels.description} > +
{ title={labels.title} description={labels.description} > +
void; + removeAggregate: (index: number) => void; } -const aggregateOptions: Array> = [ +const allAggregateOptions: Array> = [ { label: 'Count', value: AggregateType.Count }, { label: 'Sum', value: AggregateType.Sum }, { label: 'Min', value: AggregateType.Min }, @@ -24,13 +25,25 @@ const aggregateOptions: Array> = [ ]; const Aggregate = (props: AggregateProps) => { - const { columnOptions, index, aggregate, updateAggregate } = props; + const { index, aggregate, updateAggregate, removeAggregate } = props; const [isOpen, setIsOpen] = useState(false); const [alias, setAlias] = useState(aggregate.alias || ''); const { aliasLabel } = labels.components.AggregatesEditor; + // Add current value to aggregate functions + const aggregateOptions = allAggregateOptions.slice(); + if (!aggregateOptions.find(a => a.value === aggregate.aggregateType)) { + aggregateOptions.push({ label: aggregate.aggregateType, value: aggregate.aggregateType }); + } + + // Add current value to column options + const columnOptions = props.columnOptions.slice(); + if (!columnOptions.find(c => c.value === aggregate.column)) { + columnOptions.push({ label: aggregate.column, value: aggregate.column }); + } + return ( - <> + props.onChange(e.currentTarget.value)} />
@@ -102,6 +109,8 @@ export const FilterValueEditor = (props: { }; if (utils.isNullFilter(filter)) { return <>; + } else if ([FilterOperator.IsAnything, FilterOperator.IsEmpty, FilterOperator.IsNotEmpty].includes(filter.operator)) { + return <>; } else if (utils.isBooleanFilter(filter)) { const onBoolFilterValueChange = (value: boolean) => { onFilterChange({ ...filter, value }); @@ -114,15 +123,26 @@ export const FilterValueEditor = (props: { } else if (utils.isNumberFilter(filter)) { return onFilterChange({ ...filter, value })} />; } else if (utils.isDateFilter(filter)) { + if (utils.isDateFilterWithOutValue(filter)) { + return null; + } + const onDateFilterValueChange = (value: string) => { onFilterChange({ ...filter, value }); }; - return utils.isDateFilterWithOutValue(filter) ? null : ( + const dateOptions = [...standardTimeOptions]; + if (filter.value && !standardTimeOptions.find(o => o.value === filter.value)) { + dateOptions.push({ label: filter.value, value: filter.value }); + } + + return (
setIsOpen(true)} onCloseMenu={() => setIsOpen(false)} onChange={(e) => onFilterNameChange(e.value!)} - allowCustomValue={true} + allowCustomValue menuPlacement={'bottom'} /> + { isMapType && + - +
); diff --git a/src/components/queryBuilder/OrderByEditor.tsx b/src/components/queryBuilder/OrderByEditor.tsx index 8fa56227..499e436f 100644 --- a/src/components/queryBuilder/OrderByEditor.tsx +++ b/src/components/queryBuilder/OrderByEditor.tsx @@ -16,6 +16,7 @@ interface OrderByItemProps { index: number, orderByItem: OrderBy; updateOrderByItem: (index: number, orderByItem: OrderBy) => void; + removeOrderByItem: (index: number) => void; } const sortOptions = [ @@ -24,19 +25,21 @@ const sortOptions = [ ]; const OrderByItem = (props: OrderByItemProps) => { - const { columnOptions, index, orderByItem, updateOrderByItem } = props; + const { columnOptions, index, orderByItem, updateOrderByItem, removeOrderByItem } = props; return ( <> + /> value={orderByItem.dir} className={styles.Common.inlineSelect} @@ -45,6 +48,14 @@ const OrderByItem = (props: OrderByItemProps) => { onChange={e => updateOrderByItem(index, { ...orderByItem, dir: e.value! })} menuPlacement={'bottom'} /> +
); diff --git a/src/components/queryBuilder/OtelVersionSelect.test.tsx b/src/components/queryBuilder/OtelVersionSelect.test.tsx index b891ebbc..996fdd9b 100644 --- a/src/components/queryBuilder/OtelVersionSelect.test.tsx +++ b/src/components/queryBuilder/OtelVersionSelect.test.tsx @@ -5,7 +5,7 @@ import { versions as allVersions } from 'otel'; describe('OtelVersionSelect', () => { const testVersion = allVersions[0]; - const testVersionName = allVersions[0].version + ' (latest)'; + const testVersionName = testVersion.name; it('should render with empty properties', () => { const result = render( diff --git a/src/components/queryBuilder/OtelVersionSelect.tsx b/src/components/queryBuilder/OtelVersionSelect.tsx index 511b6402..7cc4870b 100644 --- a/src/components/queryBuilder/OtelVersionSelect.tsx +++ b/src/components/queryBuilder/OtelVersionSelect.tsx @@ -16,12 +16,13 @@ export const OtelVersionSelect = (props: OtelVersionSelectProps) => { const { enabled, onEnabledChange, selectedVersion, onVersionChange, wide } = props; const { label, tooltip } = selectors.components.OtelVersionSelect; const options: SelectableValue[] = allVersions.map(v => ({ - label: `${v.version}${v.name ? (` (${v.name})`) : ''}`, + label: v.name, value: v.version })); useEffect(() => { - if (selectedVersion === '') { + // Use latest version if not set or doesn't exist (which may happen if config is broken) + if (selectedVersion === '' || !allVersions.find(v => selectedVersion === v.version)) { onVersionChange(allVersions[0].version); } }, [selectedVersion, onVersionChange]); diff --git a/src/components/queryBuilder/QueryBuilder.tsx b/src/components/queryBuilder/QueryBuilder.tsx index 791548bd..06362214 100644 --- a/src/components/queryBuilder/QueryBuilder.tsx +++ b/src/components/queryBuilder/QueryBuilder.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Datasource } from 'data/CHDatasource'; -import { QueryType, QueryBuilderOptions } from 'types/queryBuilder'; +import { QueryType, QueryBuilderOptions, ColumnHint, StringFilter } from 'types/queryBuilder'; import { CoreApp } from '@grafana/data'; import { LogsQueryBuilder } from './views/LogsQueryBuilder'; import { TimeSeriesQueryBuilder } from './views/TimeSeriesQueryBuilder'; @@ -10,12 +10,16 @@ import { DatabaseTableSelect } from 'components/queryBuilder/DatabaseTableSelect import { QueryTypeSwitcher } from 'components/queryBuilder/QueryTypeSwitcher'; import { styles } from 'styles'; import { TraceQueryBuilder } from './views/TraceQueryBuilder'; -import { BuilderOptionsReducerAction, setDatabase, setQueryType, setTable } from 'hooks/useBuilderOptionsState'; +import { BuilderOptionsReducerAction, setBuilderMinimized, setDatabase, setQueryType, setTable } from 'hooks/useBuilderOptionsState'; +import TraceIdInput from './TraceIdInput'; +import { Alert, Button, VerticalGroup } from '@grafana/ui'; +import { Components as allSelectors } from 'selectors'; +import allLabels from 'labels'; interface QueryBuilderProps { app: CoreApp | undefined; builderOptions: QueryBuilderOptions; - builderOptionsDispatch: React.Dispatch, + builderOptionsDispatch: React.Dispatch; datasource: Datasource; generatedSql: string; } @@ -27,6 +31,16 @@ export const QueryBuilder = (props: QueryBuilderProps) => { const onTableChange = (table: string) => builderOptionsDispatch(setTable(table)); const onQueryTypeChange = (queryType: QueryType) => builderOptionsDispatch(setQueryType(queryType)); + if (builderOptions.meta?.minimized) { + return ( + + ); + } + return (
@@ -49,3 +63,72 @@ export const QueryBuilder = (props: QueryBuilderProps) => {
); } + +interface MinimizedQueryBuilder { + builderOptions: QueryBuilderOptions; + builderOptionsDispatch: React.Dispatch; + datasource: Datasource; +} + +const MinimizedQueryViewer = (props: MinimizedQueryBuilder) => { + const { builderOptions, builderOptionsDispatch, datasource } = props; + const defaultColumns = useMemo | undefined>(() => { + if (builderOptions.queryType === QueryType.Logs) { + return datasource.getDefaultLogsColumns(); + } else if (builderOptions.queryType === QueryType.Traces) { + return datasource.getDefaultTraceColumns(); + } + + return undefined; + }, [builderOptions.queryType, datasource]); + const showConfigWarning = defaultColumns?.size === 0 && builderOptions.columns?.length === 0; + const configQueryType = ( + builderOptions.queryType === QueryType.Logs ? 'logs' : + builderOptions.queryType === QueryType.Traces ? 'trace' : + builderOptions.queryType + ); + + let traceId; + if (builderOptions.queryType === QueryType.Traces && builderOptions.meta?.isTraceIdMode && builderOptions.meta.traceId) { + traceId = builderOptions.meta.traceId!; + } else if (builderOptions.queryType === QueryType.Logs && builderOptions.filters?.find(f => f.hint === ColumnHint.TraceId && 'value' in f)) { + const traceIdFilter = builderOptions.filters?.find(f => f.hint === ColumnHint.TraceId && 'value' in f) as StringFilter; + traceId = traceIdFilter.value; + } + + return ( +
+ { showConfigWarning && ( + + +
+ {`To enable data linking, enter your default ${configQueryType} configuration in your `} + ClickHouse Data Source settings +
+
+
+ )} + { !traceId && ( + + +
Trace ID is empty
+
+
+ )} + + {traceId && {}} disabled /> } + + +
+ ); +} diff --git a/src/components/queryBuilder/TraceIdInput.test.tsx b/src/components/queryBuilder/TraceIdInput.test.tsx new file mode 100644 index 00000000..42248b4d --- /dev/null +++ b/src/components/queryBuilder/TraceIdInput.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { selectors } from 'selectors'; +import TraceIdInput from './TraceIdInput'; +import userEvent from '@testing-library/user-event'; + +describe('TraceIdInput', () => { + it('should render', () => { + const result = render( {}} />); + expect(result.container.firstChild).not.toBeNull(); + }); + + it('should call onChange when ID is changed', async () => { + const onChange = jest.fn(); + const result = render(); + expect(result.container.firstChild).not.toBeNull(); + + const idInput = result.getByTestId(selectors.components.QueryBuilder.TraceIdInput.input); + expect(idInput).toBeInTheDocument(); + await userEvent.type(idInput, 'test'); + idInput.blur(); + expect(idInput).toHaveValue('test'); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith('test'); + }); +}); diff --git a/src/components/queryBuilder/TraceIdInput.tsx b/src/components/queryBuilder/TraceIdInput.tsx new file mode 100644 index 00000000..8fb0f9fb --- /dev/null +++ b/src/components/queryBuilder/TraceIdInput.tsx @@ -0,0 +1,39 @@ +import React, { useEffect, useState } from 'react'; +import allLabels from 'labels'; +import { InlineFormLabel, Input } from '@grafana/ui'; +import { selectors } from 'selectors'; + +interface TraceIdInputProps { + traceId: string; + onChange: (traceId: string) => void; + disabled?: boolean; +}; + +const TraceIdInput = (props: TraceIdInputProps) => { + const [inputId, setInputId] = useState(''); + const { traceId, onChange, disabled } = props; + const { label, tooltip } = allLabels.components.TraceQueryBuilder.columns.traceIdFilter; + + useEffect(() => { + setInputId(traceId); + }, [traceId]); + + return ( +
+ + {label} + + setInputId(e.currentTarget.value)} + onBlur={() => onChange(inputId)} + /> +
+ ) +} + +export default TraceIdInput; diff --git a/src/components/queryBuilder/utils.test.ts b/src/components/queryBuilder/utils.test.ts index 412bf5df..1afe2462 100644 --- a/src/components/queryBuilder/utils.test.ts +++ b/src/components/queryBuilder/utils.test.ts @@ -1,5 +1,4 @@ -import { AggregateType, BuilderMode, FilterOperator, OrderByDirection, ColumnHint, QueryType } from 'types/queryBuilder'; -import { getQueryOptionsFromSql, getSqlFromQueryBuilderOptions, isDateTimeType, isDateType, isNumberType } from './utils'; +import { isDateTimeType, isDateType, isNumberType } from './utils'; describe('isDateType', () => { it('returns true for Date type', () => { @@ -115,346 +114,3 @@ describe('isNumberType', () => { expect(isNumberType('Nullable')).toBe(false); }); }); - -describe('Utils: getSqlFromQueryBuilderOptions and getQueryOptionsFromSql', () => { - testCondition('handles a table without a database', 'SELECT "name" FROM "foo"', { - queryType: QueryType.Table, - mode: BuilderMode.List, - table: 'foo', - columns: [{ name: 'name' }], - }); - - testCondition('handles a database with a special character', 'SELECT "name" FROM "foo-bar"."buzz"', { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'foo-bar', - table: 'buzz', - columns: [{ name: 'name' }], - }); - - testCondition('handles a database and a table', 'SELECT "name" FROM "db"."foo"', { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'db', - table: 'foo', - columns: [{ name: 'name' }], - }); - - testCondition('handles a database and a table with a dot', 'SELECT "name" FROM "db"."foo.bar"', { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'db', - table: 'foo.bar', - columns: [{ name: 'name' }], - }); - - testCondition('handles 2 columns', 'SELECT "field1", "field2" FROM "db"."foo"', { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'db', - table: 'foo', - columns: [{ name: 'field1'}, { name: 'field2' }], - }); - - testCondition('handles a limit', 'SELECT "field1", "field2" FROM "db"."foo" LIMIT 20', { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'db', - table: 'foo', - columns: [{ name: 'field1'}, { name: 'field2' }], - limit: 20, - }); - - testCondition( - 'handles empty orderBy array', - 'SELECT "field1", "field2" FROM "db"."foo" LIMIT 20', - { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'db', - table: 'foo', - columns: [{ name: 'field1'}, { name: 'field2' }], - orderBy: [], - limit: 20, - }, - false - ); - - testCondition('handles order by', 'SELECT "field1", "field2" FROM "db"."foo" ORDER BY field1 ASC LIMIT 20', { - queryType: QueryType.Table, - mode: BuilderMode.List, - database: 'db', - table: 'foo', - columns: [{ name: 'field1'}, { name: 'field2' }], - orderBy: [{ name: 'field1', dir: OrderByDirection.ASC }], - limit: 20, - }); - - testCondition( - 'handles no select', - 'SELECT FROM "db"', - { - mode: BuilderMode.Aggregate, - database: 'db', - table: '', - columns: [], - aggregates: [], - }, - false - ); - - testCondition( - 'does not escape * field', - 'SELECT * FROM "db"', - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: '', - columns: [{ name: '*' }], - aggregates: [], - limit: undefined - }, - false - ); - - testCondition('handles aggregation function', 'SELECT sum(field1) FROM "db"."foo"', { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [{ column: 'field1', aggregateType: AggregateType.Sum, alias: undefined }], - limit: undefined - }); - - testCondition('handles aggregation with alias', 'SELECT sum(field1) total_records FROM "db"."foo"', { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [{ column: 'field1', aggregateType: AggregateType.Sum, alias: 'total_records' }], - limit: undefined - }); - - testCondition( - 'handles 2 aggregations', - 'SELECT sum(field1) total_records, count(field2) total_records2 FROM "db"."foo"', - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - table: 'foo', - database: 'db', - columns: [], - aggregates: [ - { column: 'field1', aggregateType: AggregateType.Sum, alias: 'total_records' }, - { column: 'field2', aggregateType: AggregateType.Count, alias: 'total_records2' }, - ], - limit: undefined - } - ); - - testCondition( - 'handles aggregation with groupBy', - 'SELECT field3, sum(field1) total_records, count(field2) total_records2 FROM "db"."foo" GROUP BY field3', - { - mode: BuilderMode.Aggregate, - table: 'foo', - database: 'db', - columns: [], - aggregates: [ - { column: 'field1', aggregateType: AggregateType.Sum, alias: 'total_records' }, - { column: 'field2', aggregateType: AggregateType.Count, alias: 'total_records2' }, - ], - groupBy: ['field3'], - }, - false - ); - - testCondition( - 'handles aggregation with groupBy with columns having group by value', - 'SELECT field3, sum(field1) total_records, count(field2) total_records2 FROM "db"."foo" GROUP BY field3', - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - table: 'foo', - database: 'db', - columns: [{ name: 'field3' }], - aggregates: [ - { column: 'field1', aggregateType: AggregateType.Sum, alias: 'total_records' }, - { column: 'field2', aggregateType: AggregateType.Count, alias: 'total_records2' }, - ], - groupBy: ['field3'], - limit: undefined - } - ); - - testCondition( - 'handles aggregation with group by and order by', - 'SELECT StageName, Type, count(Id) count_of, sum(Amount) FROM "db"."foo" GROUP BY StageName, Type ORDER BY count(Id) DESC, StageName ASC', - { - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [ - { column: 'Id', aggregateType: AggregateType.Count, alias: 'count_of' }, - { column: 'Amount', aggregateType: AggregateType.Sum, alias: undefined }, - ], - groupBy: ['StageName', 'Type'], - orderBy: [ - { name: 'count(Id)', dir: OrderByDirection.DESC }, - { name: 'StageName', dir: OrderByDirection.ASC }, - ], - }, - false - ); - - testCondition( - 'handles aggregation with a IN filter', - `SELECT count(id) FROM "db"."foo" WHERE ( stagename IN ('Deal Won', 'Deal Lost' ) )`, - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [{ column: 'id', aggregateType: AggregateType.Count, alias: undefined }], - filters: [ - { - key: 'stagename', - operator: FilterOperator.In, - value: ['Deal Won', 'Deal Lost'], - type: 'string', - }, - ], - limit: undefined - } - ); - - testCondition( - 'handles aggregation with a NOT IN filter', - `SELECT count(id) FROM "db"."foo" WHERE ( stagename NOT IN ('Deal Won', 'Deal Lost' ) )`, - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [{ column: 'id', aggregateType: AggregateType.Count, alias: undefined }], - filters: [ - { - key: 'stagename', - operator: FilterOperator.NotIn, - value: ['Deal Won', 'Deal Lost'], - type: 'string', - }, - ], - limit: undefined - } - ); - - testCondition( - 'handles aggregation with datetime filter', - `SELECT count(id) FROM "db"."foo" WHERE ( createddate >= $__fromTime AND createddate <= $__toTime )`, - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [{ column: 'id', aggregateType: AggregateType.Count, alias: undefined }], - filters: [ - { - key: 'createddate', - operator: FilterOperator.WithInGrafanaTimeRange, - type: 'datetime', - }, - ], - limit: undefined - } - ); - - testCondition( - 'handles aggregation with date filter', - `SELECT count(id) FROM "db"."foo" WHERE ( NOT ( closedate >= $__fromTime AND closedate <= $__toTime ) )`, - { - queryType: QueryType.Table, - mode: BuilderMode.Aggregate, - database: 'db', - table: 'foo', - columns: [], - aggregates: [{ column: 'id', aggregateType: AggregateType.Count, alias: undefined }], - filters: [ - { - key: 'closedate', - operator: FilterOperator.OutsideGrafanaTimeRange, - type: 'datetime', - }, - ], - limit: undefined - } - ); - - testCondition( - 'handles timeseries function with "timeFieldType: DateType"', - 'SELECT $__timeInterval(time) as time FROM "db"."foo" WHERE $__timeFilter(time) GROUP BY time ORDER BY time ASC', - { - queryType: QueryType.TimeSeries, - mode: BuilderMode.Trend, - database: 'db', - table: 'foo', - columns: [{ name: 'time', type: 'datetime', hint: ColumnHint.Time }], - aggregates: [], - filters: [], - }, - false - ); - - testCondition( - 'handles timeseries function with "timeFieldType: DateType" with a filter', - 'SELECT $__timeInterval(time) as time FROM "db"."foo" WHERE $__timeFilter(time) AND ( base IS NOT NULL ) GROUP BY time ORDER BY time ASC', - { - queryType: QueryType.TimeSeries, - mode: BuilderMode.Trend, - database: 'db', - table: 'foo', - columns: [{ name: 'time', type: 'datetime', hint: ColumnHint.Time }], - aggregates: [], - filters: [ - { - condition: 'AND', - filterType: 'custom', - key: 'base', - operator: 'IS NOT NULL', - type: 'LowCardinality(String)', - value: 'GBP', - }, - ], - }, - false - ); - - it('timeseries function returns empty $__timeInterval macro if time column missing', () => { - const sql = getSqlFromQueryBuilderOptions({ - database: 'db', - table: 'foo', - queryType: QueryType.TimeSeries, - mode: BuilderMode.Trend, - aggregates: [], - filters: [], - }); - - expect(sql).toContain('$__timeInterval()'); - }); -}); - -function testCondition(name: string, sql: string, builder: any, testQueryOptionsFromSql = true) { - it(name, () => { - expect(getSqlFromQueryBuilderOptions(builder)).toBe(sql); - if (testQueryOptionsFromSql) { - expect(getQueryOptionsFromSql(sql)).toEqual(builder); - } - }); -} diff --git a/src/components/queryBuilder/utils.ts b/src/components/queryBuilder/utils.ts index 79a11911..da0d42d4 100644 --- a/src/components/queryBuilder/utils.ts +++ b/src/components/queryBuilder/utils.ts @@ -32,7 +32,6 @@ import { QueryType, } from 'types/queryBuilder'; import { sqlToStatement } from 'data/ast'; -import { getColumnByHint } from 'data/sqlGenerator'; export const isBooleanType = (type: string): boolean => { @@ -79,206 +78,6 @@ export const isMultiFilter = (filter: Filter): filter is MultiFilter => { return isStringType(filter.type) && [FilterOperator.In, FilterOperator.NotIn].includes(filter.operator); }; -const getListQuery = (database = '', table = '', fields: string[] = []): string => { - const sep = database === '' || table === '' ? '' : '.'; - fields = fields && fields.length > 0 ? fields : ['']; - return `SELECT ${escapedFields(fields).join(', ')} FROM ${escaped(database)}${sep}${escaped(table)}`; -}; - -const getAggregationQuery = ( - database = '', - table = '', - fields: string[] = [], - metrics: AggregateColumn[] = [], - groupBy: string[] = [] -): string => { - let selected = fields.length > 0 ? fields.join(', ') : ''; - let metricsQuery = metrics - .map((m) => { - const alias = m.alias ? ` ${m.alias.replace(/ /g, '_')}` : ''; - return `${m.aggregateType}(${m.column})${alias}`; - }) - .join(', '); - const groupByQuery = groupBy - .filter((x) => !fields.some((y) => y === x)) // not adding field if its already is selected - .join(', '); - const sep = database === '' || table === '' ? '' : '.'; - return `SELECT ${selected}${selected && (groupByQuery || metricsQuery) ? ', ' : ''}${groupByQuery}${ - metricsQuery && groupByQuery ? ', ' : '' - }${metricsQuery} FROM ${escaped(database)}${sep}${escaped(table)}`; -}; - -const getTrendByQuery = ( - database = '', - table = '', - metrics: AggregateColumn[] = [], - groupBy: string[] = [], - timeField = '' -): string => { - metrics = metrics && metrics.length > 0 ? metrics : []; - - let metricsQuery = metrics - .map((m) => { - const alias = m.alias ? ` ` + m.alias.replace(/ /g, '_') : ''; - return `${m.aggregateType}(${m.column})${alias}`; - }) - .join(', '); - const time = `$__timeInterval(${timeField}) as time`; - if (metricsQuery !== '') { - const group = groupBy.length > 0 ? `${groupBy.join(', ')},` : ''; - metricsQuery = `${time}, ${group} ${metricsQuery}`; - } else if (groupBy.length > 0) { - metricsQuery = `${time}, ${groupBy.join(', ')}`; - } else { - metricsQuery = `${time}`; - } - - const sep = database === '' || table === '' ? '' : '.'; - return `SELECT ${metricsQuery} FROM ${escaped(database)}${sep}${escaped(table)}`; -}; - -export const getFilters = (filters: Filter[]): string => { - return filters.reduce((previousValue, currentFilter, currentIndex) => { - const prefixCondition = currentIndex === 0 ? '' : currentFilter.condition; - let filter = ''; - let field = currentFilter.key; - let operator = ''; - let notOperator = false; - if (currentFilter.operator === FilterOperator.NotLike) { - operator = 'LIKE'; - notOperator = true; - } else if (currentFilter.operator === FilterOperator.OutsideGrafanaTimeRange) { - operator = ''; - notOperator = true; - } else { - if ([FilterOperator.WithInGrafanaTimeRange].includes(currentFilter.operator)) { - operator = ''; - } else { - operator = currentFilter.operator; - } - } - filter = `${field} ${operator}`; - if (isNullFilter(currentFilter)) { - } else if (isBooleanFilter(currentFilter)) { - filter += ` ${currentFilter.value}`; - } else if (isNumberFilter(currentFilter)) { - filter += ` ${currentFilter.value || '0'}`; - } else if (isDateFilter(currentFilter)) { - if (isDateFilterWithOutValue(currentFilter)) { - if (isDateType(currentFilter.type)) { - filter += ` >= \$__fromTime AND ${currentFilter.key} <= \$__toTime`; - } - } else { - switch (currentFilter.value) { - case 'GRAFANA_START_TIME': - if (isDateType(currentFilter.type)) { - filter += ` \$__fromTime`; - } - break; - case 'GRAFANA_END_TIME': - if (isDateType(currentFilter.type)) { - filter += ` \$__toTime`; - } - break; - default: - filter += ` ${currentFilter.value || 'TODAY'}`; - } - } - } else if (isStringFilter(currentFilter)) { - if (currentFilter.operator === FilterOperator.Like || currentFilter.operator === FilterOperator.NotLike) { - filter += ` '%${currentFilter.value || ''}%'`; - } else { - filter += formatStringValue(currentFilter.value || ''); - } - } else if (isMultiFilter(currentFilter)) { - let values = currentFilter.value; - filter += ` (${values?.map((v) => formatStringValue(v).trim()).join(', ')} )`; - } - if (notOperator) { - filter = ` NOT ( ${filter} )`; - } - return filter ? `${previousValue} ${prefixCondition} ( ${filter} )` : previousValue; - }, ''); -}; - -const getGroupBy = (groupBy: string[] = [], timeField?: string): string => { - const clause = groupBy.length > 0 ? ` GROUP BY ${groupBy.join(', ')}` : ''; - if (timeField === undefined) { - return clause; - } - if (groupBy.length === 0) { - return ` GROUP BY time`; - } - return `${clause}, time`; -}; - -export const getOrderBy = (orderBy?: OrderBy[], prefix = true): string => { - const pfx = prefix ? ' ORDER BY ' : ''; - return orderBy && orderBy.filter((o) => o.name).length > 0 - ? pfx + - orderBy - .filter((o) => o.name) - .map((o) => { - return `${o.name} ${o.dir}`; - }) - .join(', ') - : ''; -}; - -const getLimit = (limit: number): string => { - return ` LIMIT ` + limit; -}; - -export const getSqlFromQueryBuilderOptions = (options: QueryBuilderOptions): string => { - const { database, table } = options; - const limit = options.limit ? getLimit(options.limit) : ''; - let query = ``; - switch (options.mode) { - case BuilderMode.Aggregate: - query += getAggregationQuery(database, table, options.columns?.map(c => c.name), options.aggregates, options.groupBy); - let aggregateFilters = getFilters(options.filters || []); - if (aggregateFilters) { - query += ` WHERE ${aggregateFilters}`; - } - query += getGroupBy(options.groupBy); - break; - case BuilderMode.Trend: - const timeColumn = getColumnByHint(options, ColumnHint.Time); - query += getTrendByQuery( - database, - table, - options.aggregates, - options.groupBy, - timeColumn?.name || '' - ); - const trendFilters = getFilters(options.filters || []); - - query += ` WHERE $__timeFilter(${timeColumn?.name})`; - query += trendFilters ? ` AND ${trendFilters}` : ''; - query += getGroupBy(options.groupBy, timeColumn?.name); - break; - case BuilderMode.List: - default: - query += getListQuery(options.database, options.table, options.columns?.map(c => c.name)); - const filters = getFilters(options.filters || []); - if (filters) { - query += ` WHERE ${filters}`; - } - } - if (options.mode === BuilderMode.Trend) { - query += ` ORDER BY time ASC`; - const orderBy = getOrderBy(options.orderBy, false); - if (orderBy.trim() !== '') { - query += `, ${orderBy}`; - } - query += limit; - } else { - query += getOrderBy(options.orderBy); - query += limit; - } - return query; -}; - export function getQueryOptionsFromSql(sql: string): QueryBuilderOptions | string { const ast = sqlToStatement(sql); if (!ast) { @@ -570,21 +369,6 @@ function getAggregatesFromAst(selectClauses: SelectedColumn[] | null): { return { timeField, aggregates, columns }; } -function formatStringValue(currentFilter: string): string { - if (currentFilter.startsWith('$')) { - return ` ${currentFilter || ''}`; - } - return ` '${currentFilter || ''}'`; -} - -function escaped(object: string) { - return object === '' ? '' : `"${object}"`; -} - -function escapedFields(fields: string[]) { - return fields.map((field) => (field === '*' ? field : escaped(field))); -} - export const operMap = new Map([ ['equals', FilterOperator.Equals], ['contains', FilterOperator.Like], diff --git a/src/components/queryBuilder/views/LogsQueryBuilder.tsx b/src/components/queryBuilder/views/LogsQueryBuilder.tsx index a890507d..8ff4acc4 100644 --- a/src/components/queryBuilder/views/LogsQueryBuilder.tsx +++ b/src/components/queryBuilder/views/LogsQueryBuilder.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { ColumnsEditor } from '../ColumnsEditor'; import { Filter, OrderBy, QueryBuilderOptions, SelectedColumn, ColumnHint } from 'types/queryBuilder'; import { ColumnSelect } from '../ColumnSelect'; @@ -11,11 +11,13 @@ import { getColumnByHint } from 'data/sqlGenerator'; import { columnFilterDateTime, columnFilterString } from 'data/columnFilters'; import { Datasource } from 'data/CHDatasource'; import { useBuilderOptionChanges } from 'hooks/useBuilderOptionChanges'; -import { Alert, VerticalGroup } from '@grafana/ui'; +import { Alert, Button, InlineFormLabel, Input, VerticalGroup } from '@grafana/ui'; import useColumns from 'hooks/useColumns'; import { BuilderOptionsReducerAction, setOptions, setOtelEnabled, setOtelVersion } from 'hooks/useBuilderOptionsState'; import useIsNewQuery from 'hooks/useIsNewQuery'; import { useDefaultFilters, useDefaultTimeColumn, useLogDefaultsOnMount, useOtelColumns } from './logsQueryBuilderHooks'; +import { styles } from 'styles'; +import { Components as allSelectors } from 'selectors'; interface LogsQueryBuilderProps { datasource: Datasource; @@ -34,6 +36,7 @@ interface LogsQueryBuilderState { orderBy: OrderBy[]; limit: number; filters: Filter[]; + logMessageLike: string; } export const LogsQueryBuilder = (props: LogsQueryBuilderProps) => { @@ -57,6 +60,7 @@ export const LogsQueryBuilder = (props: LogsQueryBuilderProps) => { filters: builderOptions.filters || [], orderBy: builderOptions.orderBy || [], limit: builderOptions.limit || 0, + logMessageLike: builderOptions.meta?.logMessageLike || '', }), [builderOptions]); const [showConfigWarning, setConfigWarningOpen] = useState(datasource.getDefaultLogsColumns().size === 0 && builderOptions.columns?.length === 0); @@ -76,21 +80,24 @@ export const LogsQueryBuilder = (props: LogsQueryBuilderProps) => { columns: nextColumns, filters: next.filters, orderBy: next.orderBy, - limit: next.limit + limit: next.limit, + meta: { + logMessageLike: next.logMessageLike, + } })); }, builderState); useLogDefaultsOnMount(datasource, isNewQuery, builderOptions, builderOptionsDispatch); useOtelColumns(builderState.otelEnabled, builderState.otelVersion, builderOptionsDispatch); useDefaultTimeColumn(datasource, allColumns, builderOptions.table, builderState.timeColumn, builderState.otelEnabled, builderOptionsDispatch); - useDefaultFilters(builderOptions.table, builderState.timeColumn, builderState.filters, builderState.orderBy, builderOptionsDispatch); + useDefaultFilters(builderOptions.table, isNewQuery, builderOptionsDispatch); const configWarning = showConfigWarning && ( setConfigWarningOpen(false)}>
{'To speed up your query building, enter your default logs configuration in your '} - ClickHouse Data Source settings + ClickHouse Data Source settings
@@ -163,10 +170,56 @@ export const LogsQueryBuilder = (props: LogsQueryBuilderProps) => { /> +
); } + +interface LogMessageLikeInputProps { + logMessageLike: string; + onChange: (logMessageLike: string) => void; +}; + +const LogMessageLikeInput = (props: LogMessageLikeInputProps) => { + const [input, setInput] = useState(''); + const { logMessageLike, onChange } = props; + const { label, tooltip, clearButton } = allLabels.components.LogsQueryBuilder.logMessageFilter; + + useEffect(() => { + setInput(logMessageLike); + }, [logMessageLike]); + + return ( +
+ + {label} + + setInput(e.currentTarget.value)} + onBlur={() => onChange(input)} + /> + { logMessageLike && + + } +
+ ) +} diff --git a/src/components/queryBuilder/views/TableQueryBuilder.tsx b/src/components/queryBuilder/views/TableQueryBuilder.tsx index c9d705f4..cdb7ce6d 100644 --- a/src/components/queryBuilder/views/TableQueryBuilder.tsx +++ b/src/components/queryBuilder/views/TableQueryBuilder.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { ColumnsEditor } from '../ColumnsEditor'; import { AggregateColumn, BuilderMode, Filter, OrderBy, QueryBuilderOptions, SelectedColumn } from 'types/queryBuilder'; import { OrderByEditor, getOrderByOptions } from '../OrderByEditor'; @@ -20,6 +20,7 @@ interface TableQueryBuilderProps { } interface TableQueryBuilderState { + isAggregateMode: boolean; selectedColumns: SelectedColumn[]; aggregates: AggregateColumn[]; groupBy: string[]; @@ -32,8 +33,8 @@ export const TableQueryBuilder = (props: TableQueryBuilderProps) => { const { datasource, builderOptions, builderOptionsDispatch } = props; const allColumns = useColumns(datasource, builderOptions.database, builderOptions.table); const labels = allLabels.components.TableQueryBuilder; - const [isAggregateMode, setAggregateMode] = useState((builderOptions.aggregates?.length || 0) > 0); // Toggle Simple vs Aggregate mode const builderState: TableQueryBuilderState = useMemo(() => ({ + isAggregateMode: builderOptions.mode === BuilderMode.Aggregate, selectedColumns: builderOptions.columns || [], aggregates: builderOptions.aggregates || [], groupBy: builderOptions.groupBy || [], @@ -44,10 +45,10 @@ export const TableQueryBuilder = (props: TableQueryBuilderProps) => { const onOptionChange = useBuilderOptionChanges(next => { builderOptionsDispatch(setOptions({ - mode: isAggregateMode ? BuilderMode.Aggregate : BuilderMode.List, + mode: next.isAggregateMode ? BuilderMode.Aggregate : BuilderMode.List, columns: next.selectedColumns, - aggregates: isAggregateMode ? next.aggregates : [], - groupBy: isAggregateMode ? next.groupBy : [], + aggregates: next.aggregates, + groupBy: next.groupBy, filters: next.filters, orderBy: next.orderBy, limit: next.limit @@ -59,8 +60,8 @@ export const TableQueryBuilder = (props: TableQueryBuilderProps) => { @@ -72,7 +73,7 @@ export const TableQueryBuilder = (props: TableQueryBuilderProps) => { showAllOption /> - {isAggregateMode && ( + {builderState.isAggregateMode && ( <> @@ -85,7 +86,14 @@ export const TableQueryBuilder = (props: TableQueryBuilderProps) => { onOrderByChange={onOptionChange('orderBy')} /> - + ); } diff --git a/src/components/queryBuilder/views/TimeSeriesQueryBuilder.tsx b/src/components/queryBuilder/views/TimeSeriesQueryBuilder.tsx index 5dc69e18..2419538d 100644 --- a/src/components/queryBuilder/views/TimeSeriesQueryBuilder.tsx +++ b/src/components/queryBuilder/views/TimeSeriesQueryBuilder.tsx @@ -16,6 +16,7 @@ import { useBuilderOptionChanges } from 'hooks/useBuilderOptionChanges'; import useColumns from 'hooks/useColumns'; import { BuilderOptionsReducerAction, setOptions } from 'hooks/useBuilderOptionsState'; import { useDefaultFilters, useDefaultTimeColumn } from './timeSeriesQueryBuilderHooks'; +import useIsNewQuery from 'hooks/useIsNewQuery'; interface TimeSeriesQueryBuilderProps { datasource: Datasource; @@ -36,6 +37,7 @@ interface TimeSeriesQueryBuilderState { export const TimeSeriesQueryBuilder = (props: TimeSeriesQueryBuilderProps) => { const { datasource, builderOptions, builderOptionsDispatch } = props; + const isNewQuery = useIsNewQuery(builderOptions); const allColumns = useColumns(datasource, builderOptions.database, builderOptions.table); const labels = allLabels.components.TimeSeriesQueryBuilder; const builderState: TimeSeriesQueryBuilderState = useMemo(() => ({ @@ -72,7 +74,7 @@ export const TimeSeriesQueryBuilder = (props: TimeSeriesQueryBuilderProps) => { }, builderState); useDefaultTimeColumn(allColumns, builderOptions.table, builderState.timeColumn, builderOptionsDispatch); - useDefaultFilters(builderOptions.table, builderState.timeColumn, builderState.filters, builderOptionsDispatch); + useDefaultFilters(builderOptions.table, isNewQuery, builderOptionsDispatch); return (
@@ -116,7 +118,14 @@ export const TimeSeriesQueryBuilder = (props: TimeSeriesQueryBuilderProps) => { onOrderByChange={onOptionChange('orderBy')} /> - +
); } diff --git a/src/components/queryBuilder/views/TraceQueryBuilder.tsx b/src/components/queryBuilder/views/TraceQueryBuilder.tsx index 9372af07..d0824aa4 100644 --- a/src/components/queryBuilder/views/TraceQueryBuilder.tsx +++ b/src/components/queryBuilder/views/TraceQueryBuilder.tsx @@ -1,11 +1,11 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { Filter, QueryBuilderOptions, SelectedColumn, ColumnHint, TimeUnit } from 'types/queryBuilder'; +import React, { useMemo, useState } from 'react'; +import { Filter, QueryBuilderOptions, SelectedColumn, ColumnHint, TimeUnit, OrderBy } from 'types/queryBuilder'; import { ColumnSelect } from '../ColumnSelect'; import { FiltersEditor } from '../FilterEditor'; import allLabels from 'labels'; import { ModeSwitch } from '../ModeSwitch'; import { getColumnByHint } from 'data/sqlGenerator'; -import { Alert, Collapse, InlineFormLabel, Input, VerticalGroup } from '@grafana/ui'; +import { Alert, Collapse, VerticalGroup } from '@grafana/ui'; import { DurationUnitSelect } from 'components/queryBuilder/DurationUnitSelect'; import { Datasource } from 'data/CHDatasource'; import { useBuilderOptionChanges } from 'hooks/useBuilderOptionChanges'; @@ -13,7 +13,10 @@ import useColumns from 'hooks/useColumns'; import { BuilderOptionsReducerAction, setOptions, setOtelEnabled, setOtelVersion } from 'hooks/useBuilderOptionsState'; import useIsNewQuery from 'hooks/useIsNewQuery'; import { OtelVersionSelect } from '../OtelVersionSelect'; -import { useOtelColumns, useTraceDefaultsOnMount } from './traceQueryBuilderHooks'; +import { useDefaultFilters, useOtelColumns, useTraceDefaultsOnMount } from './traceQueryBuilderHooks'; +import TraceIdInput from '../TraceIdInput'; +import { OrderByEditor, getOrderByOptions } from '../OrderByEditor'; +import { LimitEditor } from '../LimitEditor'; interface TraceQueryBuilderProps { datasource: Datasource; @@ -22,7 +25,7 @@ interface TraceQueryBuilderProps { } interface TraceQueryBuilderState { - isSearchMode: boolean; + isTraceIdMode: boolean; otelEnabled: boolean; otelVersion: string; traceIdColumn?: SelectedColumn; @@ -36,6 +39,8 @@ interface TraceQueryBuilderState { tagsColumn?: SelectedColumn; serviceTagsColumn?: SelectedColumn; traceId: string; + orderBy: OrderBy[]; + limit: number; filters: Filter[]; } @@ -45,10 +50,10 @@ export const TraceQueryBuilder = (props: TraceQueryBuilderProps) => { const isNewQuery = useIsNewQuery(builderOptions); const [showConfigWarning, setConfigWarningOpen] = useState(datasource.getDefaultTraceColumns().size === 0 && builderOptions.columns?.length === 0); const [isColumnsOpen, setColumnsOpen] = useState(showConfigWarning); // Toggle Columns collapse section - const [isFiltersOpen, setFiltersOpen] = useState(true); // Toggle Filters collapse section + const [isFiltersOpen, setFiltersOpen] = useState(!(builderOptions.meta?.isTraceIdMode && builderOptions.meta.traceId)); // Toggle Filters collapse section const labels = allLabels.components.TraceQueryBuilder; const builderState: TraceQueryBuilderState = useMemo(() => ({ - isSearchMode: builderOptions.meta?.isTraceSearchMode || false, + isTraceIdMode: builderOptions.meta?.isTraceIdMode || false, otelEnabled: builderOptions.meta?.otelEnabled || false, otelVersion: builderOptions.meta?.otelVersion || '', traceIdColumn: getColumnByHint(builderOptions, ColumnHint.TraceId), @@ -62,6 +67,8 @@ export const TraceQueryBuilder = (props: TraceQueryBuilderProps) => { tagsColumn: getColumnByHint(builderOptions, ColumnHint.TraceTags), serviceTagsColumn: getColumnByHint(builderOptions, ColumnHint.TraceServiceTags), traceId: builderOptions.meta?.traceId || '', + orderBy: builderOptions.orderBy || [], + limit: builderOptions.limit || 0, filters: builderOptions.filters || [], }), [builderOptions]); @@ -80,9 +87,11 @@ export const TraceQueryBuilder = (props: TraceQueryBuilderProps) => { builderOptionsDispatch(setOptions({ columns: nextColumns, + orderBy: next.orderBy, + limit: next.limit, filters: next.filters, meta: { - isTraceSearchMode: next.isSearchMode, + isTraceIdMode: next.isTraceIdMode, traceDurationUnit: next.durationUnit, traceId: next.traceId, } @@ -91,13 +100,14 @@ export const TraceQueryBuilder = (props: TraceQueryBuilderProps) => { useTraceDefaultsOnMount(datasource, isNewQuery, builderOptions, builderOptionsDispatch); useOtelColumns(builderState.otelEnabled, builderState.otelVersion, builderOptionsDispatch); + useDefaultFilters(builderOptions.table, builderState.isTraceIdMode, isNewQuery, builderOptionsDispatch); const configWarning = showConfigWarning && ( setConfigWarningOpen(false)}>
{'To speed up your query building, enter your default trace configuration in your '} - ClickHouse Data Source settings + ClickHouse Data Source settings
@@ -106,10 +116,10 @@ export const TraceQueryBuilder = (props: TraceQueryBuilderProps) => { return (
@@ -247,52 +257,27 @@ export const TraceQueryBuilder = (props: TraceQueryBuilderProps) => { />
- { builderState.isSearchMode ? ( - - - - ) : - - } + + + + + + { builderState.isTraceIdMode && } ); } - -interface TraceIdInputProps { - traceId: string; - onChange: (traceId: string) => void; -}; - -const TraceIdInput = (props: TraceIdInputProps) => { - const [inputId, setInputId] = useState(''); - const { traceId, onChange } = props; - const { label, tooltip } = allLabels.components.TraceQueryBuilder.columns.traceIdFilter; - - useEffect(() => { - setInputId(traceId); - }, [traceId]) - - return ( -
- - {label} - - setInputId(e.currentTarget.value)} - onBlur={() => onChange(inputId)} - /> -
- ) -} diff --git a/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts b/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts index f92b1d67..b3fe05c5 100644 --- a/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts +++ b/src/components/queryBuilder/views/logsQueryBuilderHooks.test.ts @@ -1,7 +1,7 @@ import { renderHook } from '@testing-library/react'; import { useDefaultFilters, useDefaultTimeColumn, useLogDefaultsOnMount, useOtelColumns } from './logsQueryBuilderHooks'; import { mockDatasource } from '__mocks__/datasource'; -import { ColumnHint, Filter, OrderBy, QueryBuilderOptions, SelectedColumn, TableColumn } from 'types/queryBuilder'; +import { ColumnHint, QueryBuilderOptions, SelectedColumn, TableColumn } from 'types/queryBuilder'; import { setColumnByHint, setOptions } from 'hooks/useBuilderOptionsState'; import { versions as otelVersions } from 'otel'; @@ -154,53 +154,44 @@ describe('useDefaultTimeColumn', () => { }); describe('useDefaultFilters', () => { - it('should not call builderOptionsDispatch when column/table are present on initial load', async () => { + it('should call builderOptionsDispatch when query is new', async () => { const builderOptionsDispatch = jest.fn(); const tableName = 'logs'; - const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; - const filters: Filter[] = []; - const orderBy: OrderBy[] = []; + const isNewQuery = true; - renderHook(() => useDefaultFilters(tableName, timeColumn, filters, orderBy, builderOptionsDispatch)); + renderHook(() => useDefaultFilters(tableName, isNewQuery, builderOptionsDispatch)); - expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + const expectedOptions = { + filters: [expect.anything(), expect.anything()], + orderBy: [expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); }); - it('should call builderOptionsDispatch when table changes', async () => { + it('should not call builderOptionsDispatch when query is not new', async () => { const builderOptionsDispatch = jest.fn(); const tableName = 'logs'; - const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; - const filters: Filter[] = []; - const orderBy: OrderBy[] = []; + const isNewQuery = false; - const hook = renderHook(table => - useDefaultFilters(table, timeColumn, filters, orderBy, builderOptionsDispatch), - { initialProps: tableName } - ); - hook.rerender('other_logs'); + renderHook(() => useDefaultFilters(tableName, isNewQuery, builderOptionsDispatch)); - const expectedOptions = { - filters: [expect.anything()], - orderBy: [expect.anything()], - }; - expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); - expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); }); - it('should call builderOptionsDispatch when time column changes', async () => { + it('should call builderOptionsDispatch when table changes', async () => { const builderOptionsDispatch = jest.fn(); const tableName = 'logs'; - const filters: Filter[] = []; - const orderBy: OrderBy[] = []; + const isNewQuery = false; - const hook = renderHook(timeColumn => - useDefaultFilters(tableName, timeColumn, filters, orderBy, builderOptionsDispatch), - { initialProps: { name: 'timestamp', hint: ColumnHint.Time } } + const hook = renderHook(table => + useDefaultFilters(table, isNewQuery, builderOptionsDispatch), + { initialProps: tableName } ); - hook.rerender({ name: 'other_timestamp', hint: ColumnHint.Time }); + hook.rerender('other_logs'); const expectedOptions = { - filters: [expect.anything()], + filters: [expect.anything(), expect.anything()], orderBy: [expect.anything()], }; expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); diff --git a/src/components/queryBuilder/views/logsQueryBuilderHooks.ts b/src/components/queryBuilder/views/logsQueryBuilderHooks.ts index f16f0816..ef2ca6c5 100644 --- a/src/components/queryBuilder/views/logsQueryBuilderHooks.ts +++ b/src/components/queryBuilder/views/logsQueryBuilderHooks.ts @@ -2,7 +2,7 @@ import { Datasource } from "data/CHDatasource"; import { columnFilterDateTime } from "data/columnFilters"; import { BuilderOptionsReducerAction, setColumnByHint, setOptions } from "hooks/useBuilderOptionsState"; import { useEffect, useMemo, useRef } from "react"; -import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, OrderBy, OrderByDirection, QueryBuilderOptions, SelectedColumn, TableColumn } from "types/queryBuilder"; +import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, OrderBy, OrderByDirection, QueryBuilderOptions, SelectedColumn, StringFilter, TableColumn } from "types/queryBuilder"; import { versions as otelVersions } from 'otel'; /** @@ -105,40 +105,47 @@ export const useDefaultTimeColumn = (datasource: Datasource, allColumns: readonl }, [datasource, allColumns, table, builderOptionsDispatch]); }; -// Apply default filters/orderBy on timeColumn change -const timeRangeFilterId = 'timeRange'; -export const useDefaultFilters = (table: string, timeColumn: SelectedColumn | undefined, filters: Filter[], orderBy: OrderBy[], builderOptionsDispatch: React.Dispatch) => { - const lastTimeColumn = useRef(timeColumn?.name || ''); +// Apply default filters/orderBy on table change +export const useDefaultFilters = (table: string, isNewQuery: boolean, builderOptionsDispatch: React.Dispatch) => { + const appliedDefaultFilters = useRef(!isNewQuery); const lastTable = useRef(table || ''); - if (!timeColumn || table !== lastTable.current) { - lastTimeColumn.current = ''; + if (table !== lastTable.current) { + appliedDefaultFilters.current = false; } useEffect(() => { - if (!timeColumn || (timeColumn.name === lastTimeColumn.current) || !table) { + if (!table || appliedDefaultFilters.current) { return; } - const nextFilters: Filter[] = filters.filter(f => f.id !== timeRangeFilterId); - const timeRangeFilter: DateFilterWithoutValue = { - type: 'datetime', - operator: FilterOperator.WithInGrafanaTimeRange, - filterType: 'custom', - key: timeColumn.name, - id: timeRangeFilterId, - condition: 'AND' - }; - nextFilters.unshift(timeRangeFilter); - - const nextOrderBy: OrderBy[] = orderBy.filter(o => !o.default); - const defaultOrderBy: OrderBy = { name: timeColumn?.name, dir: OrderByDirection.DESC, default: true }; - nextOrderBy.unshift(defaultOrderBy); + const defaultFilters: Filter[] = [ + { + type: 'datetime', + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: '', + hint: ColumnHint.Time, + condition: 'AND' + } as DateFilterWithoutValue, + { + type: 'string', + operator: FilterOperator.IsAnything, + filterType: 'custom', + key: '', + hint: ColumnHint.LogLevel, + condition: 'AND' + } as StringFilter, + ]; + + const defaultOrderBy: OrderBy[] = [ + { name: '', hint: ColumnHint.Time, dir: OrderByDirection.DESC, default: true } + ]; lastTable.current = table; - lastTimeColumn.current = timeColumn.name; + appliedDefaultFilters.current = true; builderOptionsDispatch(setOptions({ - filters: nextFilters, - orderBy: nextOrderBy + filters: defaultFilters, + orderBy: defaultOrderBy })); - }, [table, timeColumn, filters, orderBy, builderOptionsDispatch]); + }, [table, builderOptionsDispatch]); }; diff --git a/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts index 57d2a6be..4cdca6e7 100644 --- a/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts +++ b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.test.ts @@ -1,6 +1,6 @@ import { renderHook } from '@testing-library/react'; import { useDefaultFilters, useDefaultTimeColumn } from './timeSeriesQueryBuilderHooks'; -import { ColumnHint, Filter, SelectedColumn, TableColumn } from 'types/queryBuilder'; +import { ColumnHint, SelectedColumn, TableColumn } from 'types/queryBuilder'; import { setColumnByHint, setOptions } from 'hooks/useBuilderOptionsState'; describe('useDefaultTimeColumn', () => { @@ -54,49 +54,45 @@ describe('useDefaultTimeColumn', () => { }); describe('useDefaultFilters', () => { - it('should not call builderOptionsDispatch when column/table are present on initial load', async () => { + it('should call builderOptionsDispatch when query is new', async () => { const builderOptionsDispatch = jest.fn(); const tableName = 'timeseries'; - const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; - const filters: Filter[] = []; + const isNewQuery = true; - renderHook(() => useDefaultFilters(tableName, timeColumn, filters, builderOptionsDispatch)); + renderHook(() => useDefaultFilters(tableName, isNewQuery, builderOptionsDispatch)); - expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + const expectedOptions = { + filters: [expect.anything()], + orderBy: [expect.anything()] + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); }); - it('should call builderOptionsDispatch when table changes', async () => { + it('should not call builderOptionsDispatch when query is not new', async () => { const builderOptionsDispatch = jest.fn(); const tableName = 'timeseries'; - const timeColumn: SelectedColumn = { name: 'timestamp', hint: ColumnHint.Time }; - const filters: Filter[] = []; + const isNewQuery = false; - const hook = renderHook(table => - useDefaultFilters(table, timeColumn, filters, builderOptionsDispatch), - { initialProps: tableName } - ); - hook.rerender('other_timeseries'); + renderHook(() => useDefaultFilters(tableName, isNewQuery, builderOptionsDispatch)); - const expectedOptions = { - filters: [expect.anything()], - }; - expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); - expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); }); - it('should call builderOptionsDispatch when time column changes', async () => { + it('should call builderOptionsDispatch when table changes', async () => { const builderOptionsDispatch = jest.fn(); const tableName = 'timeseries'; - const filters: Filter[] = []; + const isNewQuery = false; - const hook = renderHook(timeColumn => - useDefaultFilters(tableName, timeColumn, filters, builderOptionsDispatch), - { initialProps: { name: 'timestamp', hint: ColumnHint.Time } } + const hook = renderHook(table => + useDefaultFilters(table, isNewQuery, builderOptionsDispatch), + { initialProps: tableName } ); - hook.rerender({ name: 'other_timestamp', hint: ColumnHint.Time }); + hook.rerender('other_timeseries'); const expectedOptions = { filters: [expect.anything()], + orderBy: [expect.anything()] }; expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); diff --git a/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts index 4302d3b0..0c82ada9 100644 --- a/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts +++ b/src/components/queryBuilder/views/timeSeriesQueryBuilderHooks.ts @@ -1,7 +1,7 @@ import { columnFilterDateTime } from 'data/columnFilters'; import { BuilderOptionsReducerAction, setColumnByHint, setOptions } from 'hooks/useBuilderOptionsState'; import React, { useEffect, useRef } from 'react'; -import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, SelectedColumn, TableColumn } from 'types/queryBuilder'; +import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, OrderBy, OrderByDirection, SelectedColumn, TableColumn } from 'types/queryBuilder'; // Finds and selects a default log time column, updates when table changes export const useDefaultTimeColumn = (allColumns: readonly TableColumn[], table: string, timeColumn: SelectedColumn | undefined, builderOptionsDispatch: React.Dispatch) => { @@ -33,35 +33,39 @@ export const useDefaultTimeColumn = (allColumns: readonly TableColumn[], table: }, [allColumns, table, builderOptionsDispatch]); }; -// Apply default filters/orderBy on timeColumn change -const timeRangeFilterId = 'timeRange'; -export const useDefaultFilters = (table: string, timeColumn: SelectedColumn | undefined, filters: Filter[], builderOptionsDispatch: React.Dispatch) => { - const lastTimeColumn = useRef(timeColumn?.name || ''); +// Apply default filters on table change +export const useDefaultFilters = (table: string, isNewQuery: boolean, builderOptionsDispatch: React.Dispatch) => { + const appliedDefaultFilters = useRef(!isNewQuery); const lastTable = useRef(table || ''); - if (!timeColumn || table !== lastTable.current) { - lastTimeColumn.current = ''; + if (table !== lastTable.current) { + appliedDefaultFilters.current = false; } useEffect(() => { - if (!timeColumn || (timeColumn.name === lastTimeColumn.current) || !table) { + if (!table || appliedDefaultFilters.current) { return; } - const nextFilters: Filter[] = filters.filter(f => f.id !== timeRangeFilterId); - const timeRangeFilter: DateFilterWithoutValue = { - type: 'datetime', - operator: FilterOperator.WithInGrafanaTimeRange, - filterType: 'custom', - key: timeColumn.name, - id: timeRangeFilterId, - condition: 'AND' - }; - nextFilters.unshift(timeRangeFilter); + const defaultFilters: Filter[] = [ + { + type: 'datetime', + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: '', + hint: ColumnHint.Time, + condition: 'AND' + } as DateFilterWithoutValue + ]; + + const defaultOrderBy: OrderBy[] = [ + { name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC, default: true } + ]; lastTable.current = table; - lastTimeColumn.current = timeColumn.name; + appliedDefaultFilters.current = true; builderOptionsDispatch(setOptions({ - filters: nextFilters + filters: defaultFilters, + orderBy: defaultOrderBy, })); - }, [table, timeColumn, filters, builderOptionsDispatch]); + }, [table, builderOptionsDispatch]); }; diff --git a/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts b/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts index 7bbb7038..9224f520 100644 --- a/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts +++ b/src/components/queryBuilder/views/traceQueryBuilderHooks.test.ts @@ -1,5 +1,5 @@ import { renderHook } from '@testing-library/react'; -import { useTraceDefaultsOnMount, useOtelColumns } from './traceQueryBuilderHooks'; +import { useTraceDefaultsOnMount, useOtelColumns, useDefaultFilters } from './traceQueryBuilderHooks'; import { mockDatasource } from '__mocks__/datasource'; import { ColumnHint, QueryBuilderOptions, SelectedColumn } from 'types/queryBuilder'; import { setOptions } from 'hooks/useBuilderOptionsState'; @@ -97,3 +97,64 @@ describe('useOtelColumns', () => { expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); }); }); + + +describe('useDefaultFilters', () => { + it('should call builderOptionsDispatch when query is new', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const isTraceIdMode = false; + const isNewQuery = true; + + renderHook(() => useDefaultFilters(tableName, isTraceIdMode, isNewQuery, builderOptionsDispatch)); + + const expectedOptions = { + filters: [expect.anything(), expect.anything(), expect.anything(), expect.anything()], + orderBy: [expect.anything(), expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); + + it('should not call builderOptionsDispatch when query is not new', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const isTraceIdMode = false; + const isNewQuery = false; + + renderHook(() => useDefaultFilters(tableName, isTraceIdMode, isNewQuery, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should not call builderOptionsDispatch when query is trace ID mode', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const isTraceIdMode = true; + const isNewQuery = true; + + renderHook(() => useDefaultFilters(tableName, isTraceIdMode, isNewQuery, builderOptionsDispatch)); + + expect(builderOptionsDispatch).toHaveBeenCalledTimes(0); + }); + + it('should call builderOptionsDispatch when table changes', async () => { + const builderOptionsDispatch = jest.fn(); + const tableName = 'timeseries'; + const isTraceIdMode = false; + const isNewQuery = false; + + const hook = renderHook(table => + useDefaultFilters(table, isTraceIdMode, isNewQuery, builderOptionsDispatch), + { initialProps: tableName } + ); + hook.rerender('other_timeseries'); + + const expectedOptions = { + filters: [expect.anything(), expect.anything(), expect.anything(), expect.anything()], + orderBy: [expect.anything(), expect.anything()], + }; + expect(builderOptionsDispatch).toHaveBeenCalledTimes(1); + expect(builderOptionsDispatch).toHaveBeenCalledWith(expect.objectContaining(setOptions(expectedOptions))); + }); +}); diff --git a/src/components/queryBuilder/views/traceQueryBuilderHooks.ts b/src/components/queryBuilder/views/traceQueryBuilderHooks.ts index fdca6788..4787c565 100644 --- a/src/components/queryBuilder/views/traceQueryBuilderHooks.ts +++ b/src/components/queryBuilder/views/traceQueryBuilderHooks.ts @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from 'react'; import { Datasource } from 'data/CHDatasource'; import { versions as otelVersions } from 'otel'; -import { QueryBuilderOptions, SelectedColumn } from 'types/queryBuilder'; +import { ColumnHint, DateFilterWithoutValue, Filter, FilterOperator, NumberFilter, OrderBy, OrderByDirection, QueryBuilderOptions, SelectedColumn, StringFilter } from 'types/queryBuilder'; import { BuilderOptionsReducerAction, setOptions } from 'hooks/useBuilderOptionsState'; /** @@ -74,3 +74,68 @@ export const useOtelColumns = (otelEnabled: boolean, otelVersion: string, builde didSetColumns.current = true; }, [otelEnabled, otelVersion, builderOptionsDispatch]); }; + +// Apply default filters on table change +export const useDefaultFilters = (table: string, isTraceIdMode: boolean, isNewQuery: boolean, builderOptionsDispatch: React.Dispatch) => { + const appliedDefaultFilters = useRef(!isNewQuery); + const lastTable = useRef(table || ''); + if (table !== lastTable.current) { + appliedDefaultFilters.current = false; + } + + useEffect(() => { + if (isTraceIdMode || !table || appliedDefaultFilters.current) { + return; + } + + const defaultFilters: Filter[] = [ + { + type: 'datetime', + operator: FilterOperator.WithInGrafanaTimeRange, + filterType: 'custom', + key: '', + hint: ColumnHint.Time, + condition: 'AND' + } as DateFilterWithoutValue, // Filter to dashboard time range + { + type: 'string', + operator: FilterOperator.IsEmpty, + filterType: 'custom', + key: '', + hint: ColumnHint.TraceParentSpanId, + condition: 'AND', + value: '' + } as StringFilter, // Only show top level spans + { + type: 'UInt64', + operator: FilterOperator.GreaterThan, + filterType: 'custom', + key: '', + hint: ColumnHint.TraceDurationTime, + condition: 'AND', + value: 0 + } as NumberFilter, // Only show spans where duration > 0 + { + type: 'string', + operator: FilterOperator.IsAnything, + filterType: 'custom', + key: '', + hint: ColumnHint.TraceServiceName, + condition: 'AND', + value: '' + } as StringFilter, // Placeholder service name filter for convenience + ]; + + const defaultOrderBy: OrderBy[] = [ + { name: '', hint: ColumnHint.Time, dir: OrderByDirection.DESC, default: true }, + { name: '', hint: ColumnHint.TraceDurationTime, dir: OrderByDirection.DESC, default: true }, + ]; + + lastTable.current = table; + appliedDefaultFilters.current = true; + builderOptionsDispatch(setOptions({ + filters: defaultFilters, + orderBy: defaultOrderBy, + })); + }, [table, isTraceIdMode, builderOptionsDispatch]); +}; diff --git a/src/data/CHDatasource.test.ts b/src/data/CHDatasource.test.ts index fb00db4d..6e4be16f 100644 --- a/src/data/CHDatasource.test.ts +++ b/src/data/CHDatasource.test.ts @@ -421,9 +421,9 @@ describe('ClickHouseDatasource', () => { } as QueryBuilderOptions, }); expect(result?.rawSql).toEqual( - 'SELECT toStartOfInterval("created_at", INTERVAL 1 DAY) AS time, count(*) logs ' + + 'SELECT toStartOfInterval("created_at", INTERVAL 1 DAY) as time, count(*) as logs ' + 'FROM "default"."logs" ' + - 'GROUP BY toStartOfInterval("created_at", INTERVAL 1 DAY) AS time ' + + 'GROUP BY time ' + 'ORDER BY time ASC' ); }); @@ -434,16 +434,16 @@ describe('ClickHouseDatasource', () => { .mockReturnValue('toStartOfInterval("created_at", INTERVAL 1 DAY)'); const result = datasource.getSupplementaryLogsVolumeQuery(request, query); expect(result?.rawSql).toEqual( - `SELECT sum(toString("level") IN ('critical','fatal','crit','alert','emerg','CRITICAL','FATAL','CRIT','ALERT','EMERG','Critical','Fatal','Crit','Alert','Emerg')) AS critical, ` + - `sum(toString("level") IN ('error','err','eror','ERROR','ERR','EROR','Error','Err','Eror')) AS error, ` + - `sum(toString("level") IN ('warn','warning','WARN','WARNING','Warn','Warning')) AS warn, ` + - `sum(toString("level") IN ('info','information','informational','INFO','INFORMATION','INFORMATIONAL','Info','Information','Informational')) AS info, ` + - `sum(toString("level") IN ('debug','dbug','DEBUG','DBUG','Debug','Dbug')) AS debug, ` + - `sum(toString("level") IN ('trace','TRACE','Trace')) AS trace, ` + - `sum(toString("level") IN ('unknown','UNKNOWN','Unknown')) AS unknown, ` + - `toStartOfInterval("created_at", INTERVAL 1 DAY) AS time ` + + `SELECT toStartOfInterval("created_at", INTERVAL 1 DAY) as time, ` + + `sum(toString("level") IN ('critical','fatal','crit','alert','emerg','CRITICAL','FATAL','CRIT','ALERT','EMERG','Critical','Fatal','Crit','Alert','Emerg')) as critical, ` + + `sum(toString("level") IN ('error','err','eror','ERROR','ERR','EROR','Error','Err','Eror')) as error, ` + + `sum(toString("level") IN ('warn','warning','WARN','WARNING','Warn','Warning')) as warn, ` + + `sum(toString("level") IN ('info','information','informational','INFO','INFORMATION','INFORMATIONAL','Info','Information','Informational')) as info, ` + + `sum(toString("level") IN ('debug','dbug','DEBUG','DBUG','Debug','Dbug')) as debug, ` + + `sum(toString("level") IN ('trace','TRACE','Trace')) as trace, ` + + `sum(toString("level") IN ('unknown','UNKNOWN','Unknown')) as unknown ` + `FROM "default"."logs" ` + - `GROUP BY toStartOfInterval("created_at", INTERVAL 1 DAY) AS time ` + + `GROUP BY time ` + `ORDER BY time ASC` ); }); diff --git a/src/data/CHDatasource.ts b/src/data/CHDatasource.ts index 3eef6e42..c375e707 100644 --- a/src/data/CHDatasource.ts +++ b/src/data/CHDatasource.ts @@ -17,7 +17,7 @@ import { vectorator, } from '@grafana/data'; import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; -import { Observable } from 'rxjs'; +import { Observable, map } from 'rxjs'; import { CHConfig } from 'types/config'; import { EditorType, CHQuery } from 'types/sql'; import { @@ -32,6 +32,7 @@ import { QueryBuilderOptions, ColumnHint, TimeUnit, + SelectedColumn, } from 'types/queryBuilder'; import { AdHocFilter } from './adHocFilter'; import { cloneDeep, isEmpty, isString } from 'lodash'; @@ -43,10 +44,11 @@ import { queryLogsVolume, TIME_FIELD_ALIAS, } from './logs'; -import { getSqlFromQueryBuilderOptions } from '../components/queryBuilder/utils'; -import { generateSql, getColumnByHint } from './sqlGenerator'; +import { generateSql, getColumnByHint, logAliasToColumnHints } from './sqlGenerator'; import { versions as otelVersions } from 'otel'; import { ReactNode } from 'react'; +import { transformQueryResponseWithTraceAndLogLinks } from './utils'; +import { pluginVersion } from 'utils/version'; export class Datasource extends DataSourceWithBackend @@ -130,28 +132,32 @@ export class Datasource return undefined; } + + const timeColumn = getColumnByHint(query.builderOptions, ColumnHint.Time); if (timeColumn === undefined) { return undefined; } - const timeFieldRoundingClause = getTimeFieldRoundingClause( - logsVolumeRequest.scopedVars, - timeColumn.name - ); - const fields: string[] = []; + const columns: SelectedColumn[] = []; const aggregates: AggregateColumn[] = []; - // could be undefined or an empty string (if user deselects the field) + columns.push({ + name: getTimeFieldRoundingClause(logsVolumeRequest.scopedVars, timeColumn.name), + alias: TIME_FIELD_ALIAS, + hint: ColumnHint.Time + }); + const logLevelColumn = getColumnByHint(query.builderOptions, ColumnHint.LogLevel); if (logLevelColumn) { - // Generate "fields" like + // Generates aggregates like // sum(toString("log_level") IN ('dbug', 'debug', 'DBUG', 'DEBUG', 'Dbug', 'Debug')) AS debug const llf = `toString("${logLevelColumn.name}")`; let level: keyof typeof LOG_LEVEL_TO_IN_CLAUSE; for (level in LOG_LEVEL_TO_IN_CLAUSE) { - fields.push(`sum(${llf} ${LOG_LEVEL_TO_IN_CLAUSE[level]}) AS ${level}`); + aggregates.push({ aggregateType: AggregateType.Sum, column: `${llf} ${LOG_LEVEL_TO_IN_CLAUSE[level]}`, alias: level }); } } else { + // Count all logs if level column isn't selected aggregates.push({ aggregateType: AggregateType.Count, column: '*', @@ -159,29 +165,32 @@ export class Datasource }); } + const filters = (query.builderOptions.filters?.slice() || []).map(f => { + // In order for a hinted filter to work, the hinted column must be SELECTed OR provide "key" + // For this histogram query the "level" column isn't selected, so we must find the original column name + if (f.hint && !f.key) { + const originalColumn = getColumnByHint(query.builderOptions, f.hint); + f.key = originalColumn?.alias || originalColumn?.name || ''; + } + + return f; + }); + const logVolumeSqlBuilderOptions: QueryBuilderOptions = { database: query.builderOptions.database, table: query.builderOptions.table, queryType: QueryType.TimeSeries, - mode: BuilderMode.Aggregate, - filters: query.builderOptions.filters, - columns: fields.map(f => ({ name: f })), + filters, + columns, aggregates, - groupBy: [`${timeFieldRoundingClause} AS ${TIME_FIELD_ALIAS}`], - orderBy: [ - { - name: TIME_FIELD_ALIAS, - dir: OrderByDirection.ASC, - }, - ], + orderBy: [{ name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }], }; - const logVolumeSupplementaryQuery = getSqlFromQueryBuilderOptions(logVolumeSqlBuilderOptions); + const logVolumeSupplementaryQuery = generateSql(logVolumeSqlBuilderOptions); return { - // format: Format.AUTO, - // selectedFormat: Format.AUTO, - pluginVersion: '', - editorType: EditorType.SQL, + pluginVersion, + editorType: EditorType.Builder, + builderOptions: logVolumeSqlBuilderOptions, rawSql: logVolumeSupplementaryQuery, refId: '', }; @@ -266,67 +275,77 @@ export class Datasource return rawQuery; } + // Support filtering by field value in Explore modifyQuery(query: CHQuery, action: QueryFixAction): CHQuery { - // support filtering by field value in Explore - if ( - query.editorType === EditorType.Builder && - action.options !== undefined && - 'key' in action.options && - 'value' in action.options - ) { - let filters: Filter[] = query.builderOptions.filters || []; - if (action.type === 'ADD_FILTER') { - // we need to remove *any other EQ or NE* for the same field, - // because we don't want to end up with two filters like `level=info` AND `level=error` - filters = (query.builderOptions.filters ?? []).filter( - (f) => - !( - f.type === 'string' && - f.key === action.options?.key && - (f.operator === FilterOperator.Equals || f.operator === FilterOperator.NotEquals) - ) - ); - filters.push({ - condition: 'AND', - key: action.options.key, - type: 'string', - filterType: 'custom', - operator: FilterOperator.Equals, - value: action.options.value, - }); - } else if (action.type === 'ADD_FILTER_OUT') { - // with this we might want to add multiple values as NE filters - // for example, `level != info` AND `level != debug` - // thus, here we remove only exactly matching NE filters or an existing EQ filter for this field - filters = (query.builderOptions.filters ?? []).filter( - (f) => - !( - (f.type === 'string' && - f.key === action.options?.key && - 'value' in f && - f.value === action.options?.value && - f.operator === FilterOperator.NotEquals) || - (f.type === 'string' && f.key === action.options?.key && f.operator === FilterOperator.Equals) - ) - ); - filters.push({ - condition: 'AND', - key: action.options.key, - type: 'string', - filterType: 'custom', - operator: FilterOperator.NotEquals, - value: action.options.value, - }); - } - const updatedBuilder = { ...query.builderOptions, filters }; - return { - ...query, - // the query is updated to trigger the URL update and propagation to the panels - rawSql: generateSql(updatedBuilder), - builderOptions: updatedBuilder, - }; + if (query.editorType !== EditorType.Builder || !action.options || !action.options.key || !action.options.value) { + return query; + } + + const columnName = action.options.key; + const actionValue = action.options.value; + + // Find selected column by alias/name + const lookupByAlias = query.builderOptions.columns?.find(c => c.alias === columnName); // Check all aliases first, + const lookupByName = query.builderOptions.columns?.find(c => c.name === columnName); // then try matching column name + const lookupByLogsAlias = logAliasToColumnHints.has(columnName) ? getColumnByHint(query.builderOptions, logAliasToColumnHints.get(columnName)!) : undefined; + const column = lookupByAlias || lookupByName || lookupByLogsAlias; + + let nextFilters: Filter[] = (query.builderOptions.filters?.slice() || []); + if (action.type === 'ADD_FILTER') { + // we need to remove *any other EQ or NE* for the same field, + // because we don't want to end up with two filters like `level=info` AND `level=error` + nextFilters = nextFilters.filter(f => + !( + f.type === 'string' && + ((column && column.hint && f.hint) ? f.hint === column.hint : f.key === columnName) && + (f.operator === FilterOperator.IsAnything || f.operator === FilterOperator.Equals || f.operator === FilterOperator.NotEquals) + ) + ); + nextFilters.push({ + condition: 'AND', + key: (column && column.hint) ? '' : columnName, + hint: (column && column.hint) ? column.hint : undefined, + type: 'string', + filterType: 'custom', + operator: FilterOperator.Equals, + value: actionValue, + }); + } else if (action.type === 'ADD_FILTER_OUT') { + // with this we might want to add multiple values as NE filters + // for example, `level != info` AND `level != debug` + // thus, here we remove only exactly matching NE filters or an existing EQ filter for this field + nextFilters = nextFilters.filter(f => + !( + (f.type === 'string' && + ((column && column.hint && f.hint) ? f.hint === column.hint : f.key === columnName) && + 'value' in f && f.value === actionValue && + (f.operator === FilterOperator.IsAnything || f.operator === FilterOperator.NotEquals) + ) || + ( + f.type === 'string' && + ((column && column.hint && f.hint) ? f.hint === column.hint : f.key === columnName) && + (f.operator === FilterOperator.IsAnything || f.operator === FilterOperator.Equals) + ) + ) + ); + nextFilters.push({ + condition: 'AND', + key: (column && column.hint) ? '' : columnName, + hint: (column && column.hint) ? column.hint : undefined, + type: 'string', + filterType: 'custom', + operator: FilterOperator.NotEquals, + value: actionValue, + }); } - return query; + + // the query is updated to trigger the URL update and propagation to the panels + const nextOptions = { ...query.builderOptions, filters: nextFilters }; + return { + ...query, + rawSql: generateSql(nextOptions), + builderOptions: nextOptions, + }; } private getMacroArgs(query: string, argsIndex: number): string[] { @@ -472,6 +491,17 @@ export class Datasource return this.fetchData(rawSql); } + /** + * Used to populate suggestions in the filter editor for Map columns. + * + * Samples rows to get a unique set of keys for the map. + * May not include ALL keys for a given dataset. + */ + async fetchUniqueMapKeys(mapColumn: string, db: string, table: string): Promise { + const rawSql = `SELECT DISTINCT arrayJoin(${mapColumn}.keys) as keys FROM "${db}"."${table}" LIMIT 1000`; + return this.fetchData(rawSql); + } + async fetchEntities() { return this.fetchTables(); } @@ -520,7 +550,7 @@ export class Datasource return { ...t, meta: { - ...t.meta, + ...t?.meta, timezone: this.getTimezone(request), }, }; @@ -529,7 +559,7 @@ export class Datasource return super.query({ ...request, targets, - }); + }).pipe(map((res: DataQueryResponse) => transformQueryResponseWithTraceAndLogLinks(this, request, res))); } private runQuery(request: Partial, options?: any): Promise { @@ -614,7 +644,6 @@ export class Datasource this.skipAdHocFilter = true; if (tagSource.source === undefined) { - this.adHocFilter.setTargetTable('default'); const rawSql = 'SELECT name, type, table FROM system.columns'; const results = await this.runQuery({ rawSql }); return { type: TagType.schema, frame: results }; @@ -622,12 +651,6 @@ export class Datasource if (tagSource.type === TagType.query) { this.adHocFilter.setTargetTableFromQuery(tagSource.source); - } else { - let table = tagSource.from; - if (table?.includes('.')) { - table = table.split('.')[1]; - } - this.adHocFilter.setTargetTable(table || ''); } const results = await this.runQuery({ rawSql: tagSource.source }); diff --git a/src/data/adHocFilter.test.ts b/src/data/adHocFilter.test.ts index 12f4aa7d..75e48984 100644 --- a/src/data/adHocFilter.test.ts +++ b/src/data/adHocFilter.test.ts @@ -9,7 +9,7 @@ describe('AdHocManager', () => { { key: 'keyNum', operator: '=', value: '123' }, ] as AdHocVariableFilter[]); expect(val).toEqual( - `SELECT stuff FROM foo WHERE col = test settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = 123 '}` + `SELECT stuff FROM foo WHERE col = test settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = \\'123\\' '}` ); }); it('apply ad hoc filter with no inner query and no existing WHERE', () => { @@ -20,7 +20,7 @@ describe('AdHocManager', () => { { key: 'keyNum', operator: '=', value: '123' }, ] as AdHocVariableFilter[]); expect(val).toEqual( - `SELECT stuff FROM foo settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = 123 '}` + `SELECT stuff FROM foo settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = \\'123\\' '}` ); }); it('apply ad hoc filter with an inner query without existing WHERE', () => { @@ -31,7 +31,7 @@ describe('AdHocManager', () => { { key: 'keyNum', operator: '=', value: '123' }, ] as AdHocVariableFilter[]); expect(val).toEqual( - `SELECT stuff FROM (SELECT * FROM foo) as r , bar GROUP BY s ORDER BY s settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = 123 '}` + `SELECT stuff FROM (SELECT * FROM foo) as r , bar GROUP BY s ORDER BY s settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = \\'123\\' '}` ); }); it('apply ad hoc filter with an inner from query with existing WHERE', () => { @@ -42,7 +42,7 @@ describe('AdHocManager', () => { { key: 'keyNum', operator: '=', value: '123' }, ] as AdHocVariableFilter[]); expect(val).toEqual( - `SELECT stuff FROM (SELECT * FROM foo WHERE col = test) as r GROUP BY s ORDER BY s settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = 123 '}` + `SELECT stuff FROM (SELECT * FROM foo WHERE col = test) as r GROUP BY s ORDER BY s settings additional_table_filters={'foo' : ' key = \\'val\\' AND keyNum = \\'123\\' '}` ); }); it('apply ad hoc filter with an inner where query with existing WHERE', () => { @@ -194,4 +194,14 @@ describe('AdHocManager', () => { expect(warn).toHaveBeenCalledTimes(1); expect(warn).toHaveBeenCalledWith('Invalid adhoc filter will be ignored:', value); }); + + it('apply ad hoc filter with no set table', () => { + const ahm = new AdHocFilter(); + const val = ahm.apply('SELECT stuff FROM foo', [ + { key: 'key', operator: '=', value: 'val' } + ] as AdHocVariableFilter[]); + expect(val).toEqual( + `SELECT stuff FROM foo settings additional_table_filters={'foo' : ' key = \\'val\\' '}` + ); + }); }); diff --git a/src/data/adHocFilter.ts b/src/data/adHocFilter.ts index d2426b2d..2a4dfc94 100644 --- a/src/data/adHocFilter.ts +++ b/src/data/adHocFilter.ts @@ -3,10 +3,6 @@ import { getTable } from './ast'; export class AdHocFilter { private _targetTable = ''; - setTargetTable(table: string) { - this._targetTable = table; - } - setTargetTableFromQuery(query: string) { this._targetTable = getTable(query); if (this._targetTable === '') { @@ -18,12 +14,17 @@ export class AdHocFilter { if (sql === '' || !adHocFilters || adHocFilters.length === 0) { return sql; } - const filter = adHocFilters[0]; - if (filter.key?.includes('.')) { - this._targetTable = filter.key.split('.')[0]; + // sql can contain a query with double quotes around the database and table name, e.g. "default"."table", so we remove those + if (this._targetTable !== '' && !sql.replace(/"/g, '').match(new RegExp(`.*\\b${this._targetTable}\\b.*`, 'gi'))) { + return sql; + } + + if (this._targetTable === '') { + this._targetTable = getTable(sql); } - if (this._targetTable === '' || !sql.match(new RegExp(`.*\\b${this._targetTable}\\b.*`, 'gi'))) { + + if (this._targetTable === '') { return sql; } @@ -37,7 +38,7 @@ export class AdHocFilter { }) .map((f, i) => { const key = f.key.includes('.') ? f.key.split('.')[1] : f.key; - const value = isNaN(Number(f.value)) ? `\\'${f.value}\\'` : Number(f.value); + const value = `\\'${f.value}\\'`; const condition = i !== adHocFilters.length - 1 ? (f.condition ? f.condition : 'AND') : ''; const operator = convertOperatorToClickHouseOperator(f.operator); return ` ${key} ${operator} ${value} ${condition}`; diff --git a/src/data/logs.ts b/src/data/logs.ts index 0133d006..81f051eb 100644 --- a/src/data/logs.ts +++ b/src/data/logs.ts @@ -253,3 +253,13 @@ export const LOG_LEVEL_TO_IN_CLAUSE: LogLevelToInClause = (() => { return allLevels; }, {} as LogLevelToInClause); })(); + +export const allLogLevels = [ + 'critical', 'fatal', 'crit', 'alert', 'emerg', + 'error', 'err', 'eror', + 'warn', 'warning', + 'info', 'information', 'informational', + 'debug', 'dbug', + 'trace', + 'unknown' +]; diff --git a/src/data/sqlGenerator.test.ts b/src/data/sqlGenerator.test.ts index 95793f1c..98c32c9b 100644 --- a/src/data/sqlGenerator.test.ts +++ b/src/data/sqlGenerator.test.ts @@ -1,22 +1,22 @@ -import { AggregateType, ColumnHint, FilterOperator, QueryBuilderOptions, QueryType } from 'types/queryBuilder'; -import { generateSql, getColumnByHint, getColumnIndexByHint, getColumnsByHints, isAggregateQuery } from './sqlGenerator'; +import { AggregateType, BuilderMode, ColumnHint, FilterOperator, OrderByDirection, QueryBuilderOptions, QueryType, SelectedColumn, TimeUnit } from 'types/queryBuilder'; +import { _testExports, generateSql, getColumnByHint, getColumnIndexByHint, getColumnsByHints, isAggregateQuery } from './sqlGenerator'; describe('SQL Generator', () => { - it('generates logs sql', () => { + it('generates simple table query', () => { const opts: QueryBuilderOptions = { database: 'default', - table: 'logs', - queryType: QueryType.Logs, + table: 'sample', + queryType: QueryType.Table, columns: [ - { name: 'timestamp', type: 'DateTime', hint: ColumnHint.Time }, - { name: 'level', type: 'String', hint: ColumnHint.LogLevel }, - { name: 'message', type: 'String', hint: ColumnHint.LogMessage }, + { name: 'a', type: 'UInt64' }, + { name: 'b', type: 'String' }, + { name: 'c', type: 'String' }, ], limit: 1000, filters: [ { filterType: 'custom', - key: 'message', + key: 'b', type: 'String', condition: 'AND', operator: FilterOperator.IsNotNull @@ -24,143 +24,279 @@ describe('SQL Generator', () => { ], orderBy: [] }; - const expectedSql = ( - 'SELECT timestamp as timestamp, message as body, level as level ' + - 'FROM "default"."logs" WHERE ( message IS NOT NULL ) LIMIT 1000' - ); + + const expectedSqlParts = [ + 'SELECT a, b, c FROM "default"."sample"', + 'WHERE ( b IS NOT NULL ) LIMIT 1000' + ]; const sql = generateSql(opts); - expect(sql).toEqual(expectedSql); + expect(sql).toEqual(expectedSqlParts.join(' ')); }); - it('generates trace sql', () => { + it('generates aggregate table query', () => { const opts: QueryBuilderOptions = { - database: 'otel', - table: 'otel_traces', - queryType: QueryType.Traces, + database: 'default', + table: 'sample', + queryType: QueryType.Table, + mode: BuilderMode.Aggregate, columns: [ - { name: 'TraceId', type: 'String', hint: ColumnHint.TraceId }, - { name: 'SpanId', type: 'String', hint: ColumnHint.TraceSpanId }, - { name: 'ParentSpanId', type: 'String', hint: ColumnHint.TraceParentSpanId }, - { name: 'ServiceName', type: 'LowCardinality(String)', hint: ColumnHint.TraceServiceName }, - { name: 'SpanName', type: 'LowCardinality(String)', hint: ColumnHint.TraceOperationName }, - { name: 'Timestamp', type: 'DateTime64(9)', hint: ColumnHint.Time }, - { name: 'Duration', type: 'Int64', hint: ColumnHint.TraceDurationTime }, - { name: 'SpanAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceTags }, - { name: 'ResourceAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceServiceTags }, + { name: 'a', type: 'DateTime' }, + { name: 'b', type: 'String' }, + { name: 'c', type: 'String' }, + ], + aggregates: [ + { aggregateType: AggregateType.Count, column: '*', alias: 'd' } ], limit: 1000, filters: [ { filterType: 'custom', - key: '', // hint property is used instead of column name + key: 'b', type: 'String', condition: 'AND', - hint: ColumnHint.TraceId, - operator: FilterOperator.Equals, - value: '1234' + operator: FilterOperator.IsNotNull } ], + groupBy: ['a'], orderBy: [] }; - const expectedSql = ( - 'SELECT "TraceId" as traceID, "SpanId" as spanID, "ParentSpanId" as parentSpanID, "ServiceName" as serviceName, ' + - '"SpanName" as operationName, "Timestamp" as startTime, "Duration" as duration, ' + - 'arrayMap(key -> map(\'key\', key, \'value\',"SpanAttributes"[key]), mapKeys("SpanAttributes")) as tags, ' + - 'arrayMap(key -> map(\'key\', key, \'value\',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags ' + - 'FROM "otel"."otel_traces" WHERE ( TraceId = \'1234\' ) ORDER BY startTime ASC LIMIT 1000' - ); + + const expectedSqlParts = [ + 'SELECT a, b, c, count(*) as d FROM "default"."sample"', + 'WHERE ( b IS NOT NULL ) GROUP BY a LIMIT 1000' + ]; const sql = generateSql(opts); - expect(sql).toEqual(expectedSql); + expect(sql).toEqual(expectedSqlParts.join(' ')); }); - it('generates other sql', () => { + it('generates logs query', () => { const opts: QueryBuilderOptions = { database: 'default', - table: 'data', - queryType: QueryType.Table, + table: 'logs', + queryType: QueryType.Logs, columns: [ - { name: 'timestamp', type: 'DateTime' }, - { name: 'text', type: 'String' }, + { name: 'log_ts', type: 'DateTime', hint: ColumnHint.Time }, + { name: 'log_level', type: 'String', hint: ColumnHint.LogLevel }, + { name: 'log_body', type: 'String', hint: ColumnHint.LogMessage }, ], limit: 1000, - filters: [], - orderBy: [] + filters: [ + { + filterType: 'custom', + type: 'datetime', + key: '', + condition: 'AND', + hint: ColumnHint.Time, + operator: FilterOperator.WithInGrafanaTimeRange + }, + { + filterType: 'custom', + type: 'String', + key: '', + value: 'error', + condition: 'AND', + hint: ColumnHint.LogLevel, + operator: FilterOperator.Equals + } + ], + orderBy: [{ name: '', hint: ColumnHint.Time, dir: OrderByDirection.DESC }] }; - const expectedSql = ( - 'SELECT "timestamp", "text" FROM "default"."data" LIMIT 1000' - ); + + const expectedSqlParts = [ + 'SELECT log_ts as timestamp, log_body as body, log_level as level', + 'FROM "default"."logs"', + 'WHERE ( timestamp >= $__fromTime AND timestamp <= $__toTime )', + 'AND ( level = \'error\' )', + 'ORDER BY timestamp DESC LIMIT 1000' + ]; const sql = generateSql(opts); - expect(sql).toEqual(expectedSql); + expect(sql).toEqual(expectedSqlParts.join(' ')); }); - it('generates other sql with filters', () => { + it('generates simple time series query', () => { const opts: QueryBuilderOptions = { database: 'default', - table: 'data', - queryType: QueryType.Table, + table: 'time_data', + queryType: QueryType.TimeSeries, columns: [ - { name: 'timestamp', type: 'DateTime' }, - { name: 'text', type: 'String' }, + { name: 'time_field', type: 'DateTime', hint: ColumnHint.Time }, + { name: 'number_field', type: 'UInt64' }, ], - limit: 1000, + limit: 100, filters: [ { - operator: FilterOperator.WithInGrafanaTimeRange, filterType: 'custom', - key: 'created_at', - type: 'datetime', - condition: 'AND' - }, + key: 'number_field', + type: 'UInt64', + condition: 'AND', + operator: FilterOperator.GreaterThan, + value: 0 + } + ], + orderBy: [{ name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }] + }; + const expectedSqlParts = [ + 'SELECT time_field as time, number_field', + 'FROM "default"."time_data" WHERE ( number_field > 0 )', + 'ORDER BY time ASC LIMIT 100' + ]; + + const sql = generateSql(opts); + expect(sql).toEqual(expectedSqlParts.join(' ')); + }); + + it('generates aggregate time series query', () => { + const opts: QueryBuilderOptions = { + database: 'default', + table: 'time_data', + queryType: QueryType.TimeSeries, + columns: [ + { name: 'time_field', type: 'DateTime', hint: ColumnHint.Time }, + { name: 'number_field', type: 'UInt64' }, + ], + limit: 100, + aggregates: [{ aggregateType: AggregateType.Sum, column: 'number_field', alias: 'total' }], + filters: [ { filterType: 'custom', - key: 'event', - type: 'String', + key: 'number_field', + type: 'UInt64', condition: 'AND', - operator: FilterOperator.IsNotNull + operator: FilterOperator.GreaterThan, + value: 0 } ], - orderBy: [] + orderBy: [{ name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }] }; - const expectedSql = ( - 'SELECT "timestamp", "text" FROM "default"."data" ' + - 'WHERE ( created_at >= $__fromTime AND created_at <= $__toTime ) AND ( event IS NOT NULL ) ' + - 'LIMIT 1000' - ); + const expectedSqlParts = [ + 'SELECT time_field as time, number_field, sum(number_field) as total', + 'FROM "default"."time_data" WHERE ( number_field > 0 )', + 'GROUP BY time ORDER BY time ASC LIMIT 100' + ]; const sql = generateSql(opts); - expect(sql).toEqual(expectedSql); + expect(sql).toEqual(expectedSqlParts.join(' ')); }); - it('excludes LIMIT when limit is 0', () => { + it('generates trace ID query', () => { const opts: QueryBuilderOptions = { database: 'default', - table: 'data', - queryType: QueryType.Table, - limit: 0 + table: 'otel_traces', + queryType: QueryType.Traces, + columns: [ + { name: 'TraceId', type: 'String', hint: ColumnHint.TraceId }, + { name: 'SpanId', type: 'String', hint: ColumnHint.TraceSpanId }, + { name: 'ParentSpanId', type: 'String', hint: ColumnHint.TraceParentSpanId }, + { name: 'ServiceName', type: 'LowCardinality(String)', hint: ColumnHint.TraceServiceName }, + { name: 'SpanName', type: 'LowCardinality(String)', hint: ColumnHint.TraceOperationName }, + { name: 'Timestamp', type: 'DateTime64(9)', hint: ColumnHint.Time }, + { name: 'Duration', type: 'Int64', hint: ColumnHint.TraceDurationTime }, + { name: 'SpanAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceTags }, + { name: 'ResourceAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceServiceTags }, + ], + filters: [], + meta: { + minimized: true, + otelEnabled: true, + otelVersion: 'latest', + traceDurationUnit: TimeUnit.Nanoseconds, + isTraceIdMode: true, + traceId: 'abcdefg' + }, + limit: 1000, + orderBy: [] }; - const expectedSql = ( - 'SELECT FROM "default"."data"' - ); + const expectedSqlParts = [ + 'SELECT "TraceId" as traceID, "SpanId" as spanID, "ParentSpanId" as parentSpanID,', + '"ServiceName" as serviceName, "SpanName" as operationName, "Timestamp" as startTime,', + 'intDivOrZero("Duration", 1000000) as duration,', + 'arrayMap(key -> map(\'key\', key, \'value\',"SpanAttributes"[key]),', + 'mapKeys("SpanAttributes")) as tags,', + 'arrayMap(key -> map(\'key\', key, \'value\',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags', + 'FROM "default"."otel_traces" WHERE traceID = \'abcdefg\'', + 'LIMIT 1000' + ]; const sql = generateSql(opts); - expect(sql).toEqual(expectedSql); + expect(sql).toEqual(expectedSqlParts.join(' ')); }); - it('excludes LIMIT when limit is excluded', () => { + it('generates trace search query', () => { const opts: QueryBuilderOptions = { database: 'default', - table: 'data', - queryType: QueryType.Table + table: 'otel_traces', + queryType: QueryType.Traces, + columns: [ + { name: 'TraceId', type: 'String', hint: ColumnHint.TraceId }, + { name: 'SpanId', type: 'String', hint: ColumnHint.TraceSpanId }, + { name: 'ParentSpanId', type: 'String', hint: ColumnHint.TraceParentSpanId }, + { name: 'ServiceName', type: 'LowCardinality(String)', hint: ColumnHint.TraceServiceName }, + { name: 'SpanName', type: 'LowCardinality(String)', hint: ColumnHint.TraceOperationName }, + { name: 'Timestamp', type: 'DateTime64(9)', hint: ColumnHint.Time }, + { name: 'Duration', type: 'Int64', hint: ColumnHint.TraceDurationTime }, + { name: 'SpanAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceTags }, + { name: 'ResourceAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceServiceTags }, + ], + filters: [ + { + condition: 'AND', + filterType: 'custom', + hint: ColumnHint.Time, + key: '', + operator: FilterOperator.WithInGrafanaTimeRange, + type: 'datetime' + }, + { + condition: 'AND', + filterType: 'custom', + hint: ColumnHint.TraceParentSpanId, + key: '', + operator: FilterOperator.IsEmpty, + type: 'string', + value: '' + }, + { + condition: 'AND', + filterType: 'custom', + hint: ColumnHint.TraceDurationTime, + key: '', + operator: FilterOperator.GreaterThan, + type: 'UInt64', + value: 0 + }, + { + condition: 'AND', + filterType: 'custom', + hint: ColumnHint.TraceServiceName, + key: '', + operator: FilterOperator.IsAnything, + type: 'string', + value: '' + } + ], + meta: { + otelEnabled: true, + otelVersion: 'latest', + traceDurationUnit: TimeUnit.Nanoseconds + }, + limit: 1000, + orderBy: [ + { name: '', hint: ColumnHint.Time, dir: OrderByDirection.DESC }, + { name: '', hint: ColumnHint.TraceDurationTime, dir: OrderByDirection.DESC } + ] }; - const expectedSql = ( - 'SELECT FROM "default"."data"' - ); + const expectedSqlParts = [ + 'SELECT "TraceId" as traceID, "ServiceName" as serviceName, "SpanName" as operationName,', + '"Timestamp" as startTime, intDivOrZero("Duration", 1000000) as duration', + 'FROM "default"."otel_traces" WHERE ( Timestamp >= $__fromTime AND Timestamp <= $__toTime )', + 'AND ( ParentSpanId = \'\' ) AND ( Duration > 0 ) ORDER BY Timestamp DESC, Duration DESC LIMIT 1000' + ]; const sql = generateSql(opts); - expect(sql).toEqual(expectedSql); + expect(sql).toEqual(expectedSqlParts.join(' ')); }); }); @@ -216,3 +352,182 @@ describe('getColumnsByHints', () => { expect(getColumnsByHints(builderOptions, [ColumnHint.Time])).toHaveLength(0); }); }); + +describe('getColumnIdentifier', () => { + const cases: Array<{ input: SelectedColumn, expected: string }> = [ + { input: { name: '' }, expected: `` }, + { input: { name: ' ' }, expected: `" "` }, + { input: { name: 'test' }, expected: `test` }, + { input: { name: 'test with space' }, expected: `"test with space"` }, + { input: { name: 'test with alias', alias: 'a' }, expected: `"test with alias" as a` }, + { input: { name: 'test_with_alias', alias: 'b' }, expected: `test_with_alias as b` }, + ]; + + it.each(cases)('returns correct identifier (case %#)', (c) => { + expect(_testExports.getColumnIdentifier(c.input)).toEqual(c.expected); + }); +}); + +describe('getTableIdentifier', () => { + const cases: Array<{ input: { database: string, table: string }, expected: string }> = [ + { input: { database: '', table: '' }, expected: '' }, + { input: { database: 'database', table: '' }, expected: '"database"' }, + { input: { database: 'database', table: 'table' }, expected: '"database"."table"' }, + { input: { database: '', table: 'table' }, expected: '"table"' }, + ]; + + it.each(cases)('returns correct identifier (case %#)', (c) => { + expect(_testExports.getTableIdentifier(c.input.database, c.input.table)).toEqual(c.expected); + }); +}); + +describe('escapeIdentifier', () => { + const cases: Array<{ input: string, expected: string }> = [ + { input: '', expected: '' }, + { input: ' ', expected: `" "` }, + { input: 'x', expected: `"x"` }, + { input: 'x x x', expected: `"x x x"` }, + ]; + + it.each(cases)('returns escaped identifier (case %#)', (c) => { + expect(_testExports.escapeIdentifier(c.input)).toEqual(c.expected); + }); +}); + +describe('escapeValue', () => { + const cases: Array<{ input: string, expected: string }> = [ + { input: ``, expected: `''` }, + { input: ` `, expected: `' '` }, + { input: `$variable`, expected: `$variable` }, + { input: `count(column)`, expected: `count(column)` }, + { input: `'custom expression'`, expected: `'custom expression'` }, + { input: `plain text`, expected: `'plain text'` }, + { input: `text`, expected: `'text'` }, + { input: `"column"`, expected: `"column"` }, + { input: `invalid(`, expected: `invalid(` }, + { input: `invalid)`, expected: `invalid)` }, + { input: `$()'" `, expected: `$()'" ` }, + ]; + + it.each(cases)('returns escaped value (case %#)', (c) => { + expect(_testExports.escapeValue(c.input)).toEqual(c.expected); + }); +}); + +describe('concatQueryParts', () => { + it('concats query parts', () => { + const parts = [ + 'SELECT', + '', // empty strings should be ignored + ' ', // spaces allowed + '*', + 'FROM', + 'test' + ]; + const sql = _testExports.concatQueryParts(parts); + const expectedSql = 'SELECT * FROM test'; // 3 spaces expected before * + expect(sql).toEqual(expectedSql); + }); +}); + +describe('getOrderBy', () => { + it('returns empty order By', () => { + const options = {} as QueryBuilderOptions; + const sql = _testExports.getOrderBy(options); + const expectedSql = ''; + expect(sql).toEqual(expectedSql); + }); + + it('returns regular order By', () => { + const options = { + orderBy: [ + { name: 'normal', dir: OrderByDirection.ASC }, + { name: 'order', dir: OrderByDirection.DESC } + ] + } as QueryBuilderOptions; + const sql = _testExports.getOrderBy(options); + const expectedSql = 'normal ASC, order DESC'; + expect(sql).toEqual(expectedSql); + }); + + it('returns hinted order By', () => { + const options = { + columns: [{ name: 'hinted', hint: ColumnHint.Time }], + orderBy: [ + { name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }, + { name: 'normal', dir: OrderByDirection.ASC }, + { name: 'order', dir: OrderByDirection.DESC } + ] + } as QueryBuilderOptions; + const sql = _testExports.getOrderBy(options); + const expectedSql = 'hinted ASC, normal ASC, order DESC'; + expect(sql).toEqual(expectedSql); + }); +}); + +describe('getLimit', () => { + const cases: Array<{ input: number | undefined, expected: string }> = [ + { input: undefined, expected: '' }, + { input: -1, expected: '' }, + { input: 0, expected: '' }, + { input: 1, expected: 'LIMIT 1' }, + { input: 100, expected: 'LIMIT 100' }, + { input: 1000, expected: 'LIMIT 1000' }, + ]; + + it.each(cases)('returns correct LIMIT clause (case %#)', (c) => { + expect(_testExports.getLimit(c.input)).toEqual(c.expected); + }); +}); + +describe('getFilters', () => { + it('returns empty filter array', () => { + const options = {} as QueryBuilderOptions; + const sql = _testExports.getFilters(options); + const expectedSql = ''; + expect(sql).toEqual(expectedSql); + }); + + it('returns complex filter array', () => { + const options = { + columns: [{ name: 'hinted', hint: ColumnHint.Time }], + filters: [ + { + condition: 'AND', + filterType: 'custom', + hint: ColumnHint.Time, + key: '', + operator: FilterOperator.WithInGrafanaTimeRange, + type: 'datetime' + }, + { + condition: 'AND', + filterType: 'custom', + key: 'text', + operator: FilterOperator.IsEmpty, + type: 'string', + value: '' + }, + { + condition: 'AND', + filterType: 'custom', + key: 'volume', + operator: FilterOperator.GreaterThan, + type: 'UInt64', + value: 0 + }, + { + condition: 'AND', + filterType: 'custom', + key: 'should_be_excluded_from_filters', + operator: FilterOperator.IsAnything, + type: 'string', + value: '' + } + ] + } as QueryBuilderOptions; + const sql = _testExports.getFilters(options); + const expectedSql = '( hinted >= $__fromTime AND hinted <= $__toTime ) AND ( text = \'\' ) AND ( volume > 0 )'; + expect(sql).toEqual(expectedSql); + }); +}); diff --git a/src/data/sqlGenerator.ts b/src/data/sqlGenerator.ts index 6518ff8e..c00a6504 100644 --- a/src/data/sqlGenerator.ts +++ b/src/data/sqlGenerator.ts @@ -1,14 +1,88 @@ -import { getSqlFromQueryBuilderOptions, getOrderBy } from 'components/queryBuilder/utils'; -import { BooleanFilter, ColumnHint, DateFilterWithValue, FilterOperator, MultiFilter, NumberFilter, QueryBuilderOptions, QueryType, SelectedColumn, StringFilter, TimeUnit } from 'types/queryBuilder'; +import { BooleanFilter, BuilderMode, ColumnHint, DateFilterWithValue, FilterOperator, MultiFilter, NumberFilter, QueryBuilderOptions, QueryType, SelectedColumn, StringFilter, TimeUnit } from 'types/queryBuilder'; +/** + * Generates a SQL string for the given QueryBuilderOptions + */ export const generateSql = (options: QueryBuilderOptions): string => { - if (options.queryType === QueryType.Traces) { - return generateTraceQuery(options); + const hasTraceIdFilter = options.meta?.isTraceIdMode && options.meta?.traceId + if (options.queryType === QueryType.Traces && hasTraceIdFilter) { + return generateTraceIdQuery(options); + } else if (options.queryType === QueryType.Traces) { + return generateTraceSearchQuery(options); } else if (options.queryType === QueryType.Logs) { return generateLogsQuery(options); + } else if (options.queryType === QueryType.TimeSeries && options.mode !== BuilderMode.Trend) { + return generateSimpleTimeSeriesQuery(options); + } else if (options.queryType === QueryType.TimeSeries && options.mode === BuilderMode.Trend) { + return generateAggregateTimeSeriesQuery(options); + } else if (options.queryType === QueryType.Table) { + return generateTableQuery(options); + } + + return ''; +} + +/** + * Generates trace search query. + */ +const generateTraceSearchQuery = (options: QueryBuilderOptions): string => { + const { database, table } = options; + + const queryParts: string[] = []; + + // TODO: these columns could be a map or some other convenience function + const selectParts: string[] = []; + const traceId = getColumnByHint(options, ColumnHint.TraceId); + if (traceId !== undefined) { + selectParts.push(`${escapeIdentifier(traceId.name)} as traceID`); + } + + const traceServiceName = getColumnByHint(options, ColumnHint.TraceServiceName); + if (traceServiceName !== undefined) { + selectParts.push(`${escapeIdentifier(traceServiceName.name)} as serviceName`); + } + + const traceOperationName = getColumnByHint(options, ColumnHint.TraceOperationName); + if (traceOperationName !== undefined) { + selectParts.push(`${escapeIdentifier(traceOperationName.name)} as operationName`); + } + + const traceStartTime = getColumnByHint(options, ColumnHint.Time); + if (traceStartTime !== undefined) { + selectParts.push(`${escapeIdentifier(traceStartTime.name)} as startTime`); + } + + const traceDurationTime = getColumnByHint(options, ColumnHint.TraceDurationTime); + if (traceDurationTime !== undefined) { + const timeUnit = options.meta?.traceDurationUnit; + selectParts.push(getTraceDurationSelectSql(escapeIdentifier(traceDurationTime.name), timeUnit)); + } + + const selectPartsSql = selectParts.join(', '); + + queryParts.push('SELECT'); + queryParts.push(selectPartsSql); + queryParts.push('FROM'); + queryParts.push(getTableIdentifier(database, table)); + + const filterParts = getFilters(options); + if (filterParts) { + queryParts.push('WHERE'); + queryParts.push(filterParts); + } + + const orderBy = getOrderBy(options); + if (orderBy) { + queryParts.push('ORDER BY'); + queryParts.push(orderBy); + } + + const limit = getLimit(options.limit); + if (limit !== '') { + queryParts.push(limit); } - return getSqlFromQueryBuilderOptions(options); + return concatQueryParts(queryParts); } /** @@ -16,7 +90,7 @@ export const generateSql = (options: QueryBuilderOptions): string => { * Column aliases follow this structure: * https://grafana.com/docs/grafana/latest/explore/trace-integration/#data-frame-structure */ -const generateTraceQuery = (options: QueryBuilderOptions): string => { +const generateTraceIdQuery = (options: QueryBuilderOptions): string => { const { database, table } = options; const queryParts: string[] = []; @@ -76,10 +150,10 @@ const generateTraceQuery = (options: QueryBuilderOptions): string => { queryParts.push('FROM'); queryParts.push(getTableIdentifier(database, table)); - const hasTraceIdFilter = !options.meta?.isTraceSearchMode && options.meta?.traceId - const hasFilters = (options.filters?.length || 0) > 0; + const hasTraceIdFilter = options.meta?.isTraceIdMode && options.meta?.traceId + const filterParts = getFilters(options); - if (hasTraceIdFilter || hasFilters) { + if (hasTraceIdFilter || filterParts) { queryParts.push('WHERE'); } @@ -88,12 +162,14 @@ const generateTraceQuery = (options: QueryBuilderOptions): string => { queryParts.push(`traceID = '${traceId}'`); } - if (hasFilters) { - queryParts.push(getFilters(options)); + if (filterParts) { + queryParts.push(filterParts); } - if (traceStartTime !== undefined) { - queryParts.push('ORDER BY startTime ASC'); + const orderBy = getOrderBy(options); + if (orderBy) { + queryParts.push('ORDER BY'); + queryParts.push(orderBy); } const limit = getLimit(options.limit); @@ -111,7 +187,9 @@ const generateTraceQuery = (options: QueryBuilderOptions): string => { * * note: column order seems to matter as well as alias name */ -const generateLogsQuery = (options: QueryBuilderOptions): string => { +const generateLogsQuery = (_options: QueryBuilderOptions): string => { + // Copy columns so column aliases can be safely mutated + const options = { ..._options, columns: _options.columns?.map(c => ({ ...c })) }; const { database, table } = options; const queryParts: string[] = []; @@ -139,6 +217,12 @@ const generateLogsQuery = (options: QueryBuilderOptions): string => { selectParts.push(getColumnIdentifier(logLevel)); } + const traceId = getColumnByHint(options, ColumnHint.TraceId); + if (traceId !== undefined) { + traceId.alias = 'traceID'; + selectParts.push(getColumnIdentifier(traceId)); + } + options.columns?. filter(c => c.hint === undefined). // remove specialized columns forEach(c => selectParts.push(getColumnIdentifier(c))); @@ -150,14 +234,243 @@ const generateLogsQuery = (options: QueryBuilderOptions): string => { queryParts.push('FROM'); queryParts.push(getTableIdentifier(database, table)); - if ((options.filters?.length || 0) > 0) { + + const filterParts = getFilters(options); + const hasLogMessageFilter = logMessage && options.meta?.logMessageLike; + + if (filterParts || hasLogMessageFilter) { queryParts.push('WHERE'); - queryParts.push(getFilters(options)); } - if ((options.orderBy?.length || 0) > 0) { + if (filterParts) { + queryParts.push(filterParts); + } + + if (hasLogMessageFilter) { + if (filterParts) { + queryParts.push('AND'); + } + + queryParts.push(`(${logMessage.alias || logMessage.name} LIKE '%${options.meta!.logMessageLike}%')`); + } + + const orderBy = getOrderBy(options); + if (orderBy) { + queryParts.push('ORDER BY'); + queryParts.push(orderBy); + } + + const limit = getLimit(options.limit); + if (limit !== '') { + queryParts.push(limit); + } + + return concatQueryParts(queryParts); +} + +/** + * Generates a simple time series query. Includes user selected columns. + */ +const generateSimpleTimeSeriesQuery = (_options: QueryBuilderOptions): string => { + // Copy columns so column aliases can be safely mutated + const options = { ..._options, columns: _options.columns?.map(c => ({ ...c })) }; + const { database, table } = options; + + const queryParts: string[] = []; + + const selectParts: string[] = []; + const selectNames = new Set(); + const timeColumn = getColumnByHint(options, ColumnHint.Time); + if (timeColumn !== undefined) { + timeColumn.alias = 'time'; + selectParts.push(getColumnIdentifier(timeColumn)); + selectNames.add(timeColumn.alias); + } + + const columnsExcludingTimeColumn = options.columns?.filter(c => c.hint !== ColumnHint.Time); + columnsExcludingTimeColumn?.forEach(c => { + selectParts.push(getColumnIdentifier(c)); + selectNames.add(c.alias || c.name); + }); + + const aggregateSelectParts: string[] = []; + options.aggregates?.forEach(agg => { + const alias = agg.alias ? ` as ${agg.alias.replace(/ /g, '_')}` : ''; + const name = `${agg.aggregateType}(${agg.column})`; + aggregateSelectParts.push(`${name}${alias}`); + selectNames.add(alias ? alias.substring(4) : name); + }); + + options.groupBy?.forEach(g => { + if (selectNames.has(g)) { + // don't add if already selected + return; + } + + selectParts.push(g) + }); + + // (v3) aggregate selections go AFTER group by + aggregateSelectParts.forEach(a => selectParts.push(a)); + + const selectPartsSql = selectParts.join(', '); + + queryParts.push('SELECT'); + queryParts.push(selectPartsSql); + queryParts.push('FROM'); + queryParts.push(getTableIdentifier(database, table)); + + const filterParts = getFilters(options); + if (filterParts) { + queryParts.push('WHERE'); + queryParts.push(filterParts); + } + + const hasAggregates = (options.aggregates?.length || 0 > 0); + const hasGroupBy = (options.groupBy?.length || 0 > 0); + if (hasAggregates || hasGroupBy) { + queryParts.push('GROUP BY'); + } + + if ((options.groupBy?.length || 0) > 0) { + const groupByTime = timeColumn !== undefined ? `, ${timeColumn.alias}` : ''; + queryParts.push(`${options.groupBy!.join(', ')}${groupByTime}`); + } else if (hasAggregates && timeColumn) { + queryParts.push(timeColumn.alias!); + } + + const orderBy = getOrderBy(options); + if (orderBy) { + queryParts.push('ORDER BY'); + queryParts.push(orderBy); + } + + const limit = getLimit(options.limit); + if (limit !== '') { + queryParts.push(limit); + } + + return concatQueryParts(queryParts); +} + +/** + * Generates an aggregate time series query. + */ +const generateAggregateTimeSeriesQuery = (_options: QueryBuilderOptions): string => { + // Copy columns so column aliases can be safely mutated + const options = { ..._options, columns: _options.columns?.map(c => ({ ...c })) }; + const { database, table } = options; + + const queryParts: string[] = []; + const selectParts: string[] = []; + + const timeColumn = getColumnByHint(options, ColumnHint.Time); + if (timeColumn !== undefined) { + timeColumn.name = `$__timeInterval(${timeColumn.name})`; + timeColumn.alias = 'time'; + selectParts.push(getColumnIdentifier(timeColumn)); + } + + options.groupBy?.forEach(g => selectParts.push(g)); + + options.aggregates?.forEach(agg => { + const alias = agg.alias ? ` as ${agg.alias.replace(/ /g, '_')}` : ''; + const name = `${agg.aggregateType}(${agg.column})`; + selectParts.push(`${name}${alias}`); + }); + + const selectPartsSql = selectParts.join(', '); + + queryParts.push('SELECT'); + queryParts.push(selectPartsSql); + queryParts.push('FROM'); + queryParts.push(getTableIdentifier(database, table)); + + const filterParts = getFilters(options); + if (filterParts) { + queryParts.push('WHERE'); + queryParts.push(filterParts); + } + + queryParts.push('GROUP BY'); + if ((options.groupBy?.length || 0) > 0) { + const groupByTime = timeColumn !== undefined ? `, ${timeColumn.alias}` : ''; + queryParts.push(`${options.groupBy!.join(', ')}${groupByTime}`); + } else if (timeColumn) { + queryParts.push(timeColumn.alias!); + } + + const orderBy = getOrderBy(options); + if (orderBy) { + queryParts.push('ORDER BY'); + queryParts.push(orderBy); + } + + const limit = getLimit(options.limit); + if (limit !== '') { + queryParts.push(limit); + } + + return concatQueryParts(queryParts); +} + +/** + * Generates a table query. + */ +const generateTableQuery = (options: QueryBuilderOptions): string => { + const { database, table } = options; + const isAggregateMode = options.mode === BuilderMode.Aggregate; + + const queryParts: string[] = []; + const selectParts: string[] = []; + const selectNames = new Set(); + + options.columns?.forEach(c => { + selectParts.push(getColumnIdentifier(c)); + selectNames.add(c.alias || c.name); + }); + + if (isAggregateMode) { + options.aggregates?.forEach(agg => { + const alias = agg.alias ? ` as ${agg.alias.replace(/ /g, '_')}` : ''; + const name = `${agg.aggregateType}(${agg.column})`; + selectParts.push(`${name}${alias}`); + selectNames.add(alias ? alias.substring(4) : name); + }); + + options.groupBy?.forEach(g => { + if (selectNames.has(g)) { + // don't add if already selected + return; + } + + // user must manually select groupBys, for flexibility + // selectParts.push(g) + }); + } + + const selectPartsSql = selectParts.join(', '); + + queryParts.push('SELECT'); + queryParts.push(selectPartsSql); + queryParts.push('FROM'); + queryParts.push(getTableIdentifier(database, table)); + + const filterParts = getFilters(options); + if (filterParts) { + queryParts.push('WHERE'); + queryParts.push(filterParts); + } + + if (isAggregateMode && (options.groupBy?.length || 0) > 0) { + queryParts.push('GROUP BY'); + queryParts.push(options.groupBy!.join(', ')); + } + + const orderBy = getOrderBy(options); + if (orderBy) { queryParts.push('ORDER BY'); - queryParts.push(getOrderBy(options.orderBy, false)); + queryParts.push(orderBy); } const limit = getLimit(options.limit); @@ -170,7 +483,7 @@ const generateLogsQuery = (options: QueryBuilderOptions): string => { export const isAggregateQuery = (builder: QueryBuilderOptions): boolean => (builder.aggregates?.length || 0) > 0; export const getColumnByHint = (options: QueryBuilderOptions, hint: ColumnHint): SelectedColumn | undefined => options.columns?.find(c => c.hint === hint); -export const getColumnIndexByHint = (options: QueryBuilderOptions, hint: ColumnHint): number => options.columns?.findIndex(c => c.hint === hint) || -1; +export const getColumnIndexByHint = (options: QueryBuilderOptions, hint: ColumnHint): number => (options.columns || []).findIndex(c => c.hint === hint); export const getColumnsByHints = (options: QueryBuilderOptions, hints: readonly ColumnHint[]): readonly SelectedColumn[] => { const columns = []; @@ -187,13 +500,11 @@ export const getColumnsByHints = (options: QueryBuilderOptions, hints: readonly const getColumnIdentifier = (col: SelectedColumn): string => { let colName = col.name; - if (colName.includes(' ')) { - colName = escapeIdentifier(col.name); - } - // allow for functions like count() if (colName.includes('(') || colName.includes(')') || colName.includes('"') || colName.includes('"')) { colName = col.name + } else if (colName.includes(' ')) { + colName = escapeIdentifier(col.name); } if (col.alias) { @@ -212,6 +523,14 @@ const escapeIdentifier = (id: string): string => { return id === '' ? '' : `"${id}"`; } +const escapeValue = (value: string): string => { + if (value.includes('$') || value.includes('(') || value.includes(')') || value.includes('\'') || value.includes('"')) { + return value; + } + + return `'${value}'`; +} + /** * Returns the a SELECT column for trace duration. * Time unit is used to convert the value to milliseconds, as is required by Grafana's Trace panel. @@ -253,6 +572,33 @@ const concatQueryParts = (parts: readonly string[]): string => { return query; } +/** + * Returns the order by list, excluding the "ORDER BY" keyword. + */ +const getOrderBy = (options: QueryBuilderOptions): string => { + const orderByParts: string[] = []; + if ((options.orderBy?.length || 0) > 0) { + options.orderBy?.forEach(o => { + let colName = o.name; + const hintedColumn = o.hint && getColumnByHint(options, o.hint); + if (hintedColumn) { + colName = hintedColumn.alias || hintedColumn.name; + } + + if (!colName) { + return; + } + + orderByParts.push(`${colName} ${o.dir}`); + }); + } + + return orderByParts.join(', '); +}; + +/** + * Returns the limit clause including the "LIMIT" keyword + */ const getLimit = (limit?: number | undefined): string => { limit = Math.max(0, limit || 0); if (limit > 0) { @@ -262,35 +608,49 @@ const getLimit = (limit?: number | undefined): string => { return ''; }; +/** + * Returns the filters in the WHERE clause, excluding the "WHERE" keyword + */ const getFilters = (options: QueryBuilderOptions): string => { const filters = options.filters || []; const builtFilters: string[] = []; for (const filter of filters) { + if (filter.operator === FilterOperator.IsAnything) { + continue; + } + const filterParts: string[] = []; let column = filter.key; let type = filter.type; const hintedColumn = filter.hint && getColumnByHint(options, filter.hint); if (hintedColumn) { - column = hintedColumn.name; + column = hintedColumn.alias || hintedColumn.name; type = hintedColumn.type || type; } if (!column) { continue; } + + if (filter.mapKey) { + column += `['${filter.mapKey}']`; + } + filterParts.push(column); let operator: string = filter.operator; let negate = false; - if (filter.operator === FilterOperator.NotLike) { + if (filter.operator === FilterOperator.IsEmpty || filter.operator === FilterOperator.IsNotEmpty) { + operator = ''; + } else if (filter.operator === FilterOperator.NotLike) { operator = 'LIKE'; negate = true; } else if (filter.operator === FilterOperator.OutsideGrafanaTimeRange) { operator = ''; negate = true; - } else if (filter.operator === FilterOperator.WithInGrafanaTimeRange){ + } else if (filter.operator === FilterOperator.WithInGrafanaTimeRange) { operator = ''; } @@ -300,6 +660,10 @@ const getFilters = (options: QueryBuilderOptions): string => { if (isNullFilter(filter.operator)) { // empty + } else if (filter.operator === FilterOperator.IsEmpty) { + filterParts.push(`= ''`); + } else if (filter.operator === FilterOperator.IsNotEmpty) { + filterParts.push(`!= ''`); } else if (isBooleanFilter(type)) { filterParts.push(String((filter as BooleanFilter).value)); } else if (isNumberFilter(type)) { @@ -322,19 +686,19 @@ const getFilters = (options: QueryBuilderOptions): string => { } break; default: - filterParts.push(String((filter as DateFilterWithValue).value || 'TODAY')); + filterParts.push(escapeValue(String((filter as DateFilterWithValue).value || 'TODAY'))); } } } else if (isStringFilter(type, filter.operator)) { if (filter.operator === FilterOperator.Like || filter.operator === FilterOperator.NotLike) { filterParts.push(`'%${filter.value || ''}%'`); } else { - filterParts.push(formatStringValue((filter as StringFilter).value || '')); + filterParts.push(escapeValue((filter as StringFilter).value || '')); } } else if (isMultiFilter(type, filter.operator)) { - filterParts.push(`(${(filter as MultiFilter).value?.map(v => formatStringValue(v)).join(', ')})`); + filterParts.push(`(${(filter as MultiFilter).value?.map(v => escapeValue(v)).join(', ')})`); } else { - filterParts.push(formatStringValue((filter as StringFilter).value || '')); + filterParts.push(escapeValue((filter as StringFilter).value || '')); } if (negate) { @@ -368,4 +732,27 @@ const isDateFilterWithoutValue = (type: string, operator: FilterOperator): boole const isDateFilter = (type: string): boolean => isDateType(type); const isStringFilter = (type: string, operator: FilterOperator): boolean => isStringType(type) && !(operator === FilterOperator.In || operator === FilterOperator.NotIn); const isMultiFilter = (type: string, operator: FilterOperator): boolean => isStringType(type) && (operator === FilterOperator.In || operator === FilterOperator.NotIn); -const formatStringValue = (filter: string): string => filter.startsWith('$') ? (filter || '') : `'${filter || ''}'`; + +/** + * When filtering in the logs panel in explore view, we need a way to + * map from the SQL generator's aliases back to the original column hints + * so that filters can be added properly. + */ +export const logAliasToColumnHints: Map = new Map([ + ['timestamp', ColumnHint.Time], + ['body', ColumnHint.LogMessage], + ['level', ColumnHint.LogLevel], + ['traceID', ColumnHint.TraceId], +]); + + +export const _testExports = { + getColumnIdentifier, + getTableIdentifier, + escapeIdentifier, + escapeValue, + concatQueryParts, + getOrderBy, + getLimit, + getFilters, +}; diff --git a/src/data/utils.test.ts b/src/data/utils.test.ts index 8838f4d8..cd879b73 100644 --- a/src/data/utils.test.ts +++ b/src/data/utils.test.ts @@ -1,5 +1,8 @@ import { QueryBuilderOptions, QueryType } from "types/queryBuilder"; -import { columnLabelToPlaceholder, isBuilderOptionsRunnable } from "./utils"; +import { columnLabelToPlaceholder, isBuilderOptionsRunnable, transformQueryResponseWithTraceAndLogLinks } from "./utils"; +import { newMockDatasource } from "__mocks__/datasource"; +import { CoreApp, DataFrame, DataQueryRequest, DataQueryResponse, Field, FieldType } from "@grafana/data"; +import { CHBuilderQuery, CHQuery, EditorType } from "types/sql"; describe('isBuilderOptionsRunnable', () => { it('should return false for empty builder options', () => { @@ -35,3 +38,102 @@ describe('columnLabelToPlaceholder', () => { expect(actual).toEqual(expected); }); }); + +describe('transformQueryResponseWithTraceAndLogLinks', () => { + const buildTestRequestResponse = (builderOptions: Partial): [DataQueryRequest, DataQueryResponse] => { + const inputQuery: CHBuilderQuery = { + refId: 'A', + editorType: EditorType.Builder, + builderOptions: { + database: '', + table: '', + queryType: QueryType.Traces, + ...builderOptions + }, + pluginVersion: '', + rawSql: '' + }; + + const request: DataQueryRequest = { + requestId: '', + interval: '', + intervalMs: 0, + range: {} as any, + scopedVars: {} as any, + targets: [inputQuery], + timezone: '', + app: CoreApp.Explore, + startTime: 0 + }; + + const field: Field = { + name: 'traceID', + type: FieldType.string, + config: {}, + values: [] + } + const data: DataFrame[] = [{ + fields: [field], + length: 1, + refId: 'A' + }]; + const response: DataQueryResponse = { data }; + + return [request, response]; + }; + + it('inserts links into trace query. Copy trace columns, default log columns.', async () => { + const mockDatasource = newMockDatasource(); + const getDefaultTraceDatabase = jest.spyOn(mockDatasource, 'getDefaultTraceDatabase'); + const getDefaultTraceTable = jest.spyOn(mockDatasource, 'getDefaultTraceTable'); + const getDefaultTraceColumns = jest.spyOn(mockDatasource, 'getDefaultTraceColumns'); + const getDefaultLogsDatabase = jest.spyOn(mockDatasource, 'getDefaultLogsDatabase'); + const getDefaultLogsTable = jest.spyOn(mockDatasource, 'getDefaultLogsTable'); + const getDefaultLogsColumns = jest.spyOn(mockDatasource, 'getDefaultLogsColumns'); + + const builderOptions: Partial = { + queryType: QueryType.Traces, + columns: [{ name: 'a' }] + }; + + const [request, response] = buildTestRequestResponse(builderOptions); + const out = transformQueryResponseWithTraceAndLogLinks(mockDatasource, request, response); + + const links = out?.data[0]?.fields[0]?.config?.links; + expect(links).not.toBeUndefined(); + expect(links).toHaveLength(2); + expect(getDefaultTraceDatabase).not.toHaveBeenCalled(); + expect(getDefaultTraceTable).not.toHaveBeenCalled(); + expect(getDefaultTraceColumns).not.toHaveBeenCalled(); + expect(getDefaultLogsDatabase).toHaveBeenCalled(); + expect(getDefaultLogsTable).toHaveBeenCalled(); + expect(getDefaultLogsColumns).toHaveBeenCalled(); + }); + + it('inserts links into logs query. Copy logs columns, default trace columns.', async () => { + const mockDatasource = newMockDatasource(); + const getDefaultTraceDatabase = jest.spyOn(mockDatasource, 'getDefaultTraceDatabase'); + const getDefaultTraceTable = jest.spyOn(mockDatasource, 'getDefaultTraceTable'); + const getDefaultTraceColumns = jest.spyOn(mockDatasource, 'getDefaultTraceColumns'); + const getDefaultLogsDatabase = jest.spyOn(mockDatasource, 'getDefaultLogsDatabase'); + const getDefaultLogsTable = jest.spyOn(mockDatasource, 'getDefaultLogsTable'); + const getDefaultLogsColumns = jest.spyOn(mockDatasource, 'getDefaultLogsColumns'); + + const builderOptions: Partial = { + queryType: QueryType.Logs + }; + + const [request, response] = buildTestRequestResponse(builderOptions); + const out = transformQueryResponseWithTraceAndLogLinks(mockDatasource, request, response); + + const links = out?.data[0]?.fields[0]?.config?.links; + expect(links).not.toBeUndefined(); + expect(links).toHaveLength(2); + expect(getDefaultTraceDatabase).toHaveBeenCalled(); + expect(getDefaultTraceTable).toHaveBeenCalled(); + expect(getDefaultTraceColumns).toHaveBeenCalled(); + expect(getDefaultLogsDatabase).not.toHaveBeenCalled(); + expect(getDefaultLogsTable).not.toHaveBeenCalled(); + expect(getDefaultLogsColumns).not.toHaveBeenCalled(); + }); +}); diff --git a/src/data/utils.ts b/src/data/utils.ts index d64f46ed..f120aed7 100644 --- a/src/data/utils.ts +++ b/src/data/utils.ts @@ -1,4 +1,8 @@ -import { QueryBuilderOptions, QueryType } from "types/queryBuilder" +import { CoreApp, DataFrame, DataQueryRequest, DataQueryResponse } from "@grafana/data"; +import { ColumnHint, FilterOperator, OrderByDirection, QueryBuilderOptions, QueryType, StringFilter } from "types/queryBuilder" +import { CHBuilderQuery, CHQuery, EditorType } from "types/sql"; +import { Datasource } from "./CHDatasource"; +import { pluginVersion } from "utils/version"; /** * Returns true if the builder options contain enough information to start showing a query @@ -13,6 +17,25 @@ export const isBuilderOptionsRunnable = (builderOptions: QueryBuilderOptions): b ); }; +/** + * Converts QueryBuilderOptions to Grafana format + * src: https://github.com/grafana/sqlds/blob/main/query.go#L20 + */ +export const mapQueryBuilderOptionsToGrafanaFormat = (t?: QueryBuilderOptions): number => { + switch (t?.queryType) { + case QueryType.Table: + return 1; + case QueryType.Logs: + return 2; + case QueryType.TimeSeries: + return 0; + case QueryType.Traces: + return t.meta?.isTraceIdMode ? 3 : 1; + default: + return 1 << 8; // an unused u32, defaults to timeseries/graph on plugin backend. + } +}; + /** * Converts QueryType to Grafana format * src: https://github.com/grafana/sqlds/blob/main/query.go#L20 @@ -57,3 +80,176 @@ export const mapGrafanaFormatToQueryType = (f?: number): QueryType => { * Example: "Test Column" -> "test_column" */ export const columnLabelToPlaceholder = (label: string) => label.toLowerCase().replace(/ /g, '_'); + +/** + * Mutates the DataQueryResponse to include trace/log links on the traceID field. + * The link will open a second query editor in split view + * on the explore page with the selected trace ID. + * + * Requires defaults to be configured when crossing query types. + */ +export const transformQueryResponseWithTraceAndLogLinks = (datasource: Datasource, req: DataQueryRequest, res: DataQueryResponse): DataQueryResponse => { + res.data.forEach((frame: DataFrame) => { + const originalQuery = req.targets.find(t => t.refId === frame.refId) as CHBuilderQuery; + if (!originalQuery) { + return; + } + + const traceField = frame.fields.find(field => field.name.toLowerCase() === 'traceid' || field.name.toLowerCase() === 'trace_id'); + if (!traceField) { + return; + } + + const traceIdQuery: CHBuilderQuery = { + editorType: EditorType.Builder, + /** + * Evil bug: + * The rawSql value might contain time filters such as $__fromTime and $__toTime. + * Grafana sees these time range filters as data links and will refuse to enable the traceID link if these are present. + * Set rawSql to empty since it gets regenerated when the panel renders anyway. + */ + rawSql: '', + builderOptions: {} as QueryBuilderOptions, + pluginVersion, + refId: 'Trace ID' + }; + + if (originalQuery.editorType === EditorType.Builder && originalQuery.builderOptions.queryType === QueryType.Traces) { + // Copy fields directly from trace search + + traceIdQuery.builderOptions = { + ...originalQuery.builderOptions, + filters: [], // Clear filters and orderBy since it's an exact ID lookup + orderBy: [], + meta: { + ...originalQuery.builderOptions.meta, + minimized: true, + isTraceIdMode: true, + traceId: '${__value.raw}' + } + }; + } else { + // Create new query based on trace defaults + + const otelVersion = datasource.getTraceOtelVersion(); + const options: QueryBuilderOptions = { + database: datasource.getDefaultTraceDatabase() || traceIdQuery.builderOptions.database || datasource.getDefaultDatabase(), + table: datasource.getDefaultTraceTable() || datasource.getDefaultTable() || traceIdQuery.builderOptions.table, + queryType: QueryType.Traces, + columns: [], + filters: [], + orderBy: [], + meta: { + minimized: true, + isTraceIdMode: true, + traceId: '${__value.raw}', + traceDurationUnit: datasource.getDefaultTraceDurationUnit(), + otelEnabled: Boolean(otelVersion), + otelVersion: otelVersion, + } + }; + + const defaultColumns = datasource.getDefaultTraceColumns(); + for (let [hint, colName] of defaultColumns) { + options.columns!.push({ name: colName, hint }); + } + + traceIdQuery.builderOptions = options; + } + + const traceLogsQuery: CHBuilderQuery = { + editorType: EditorType.Builder, + rawSql: '', + builderOptions: {} as QueryBuilderOptions, + pluginVersion, + refId: 'Trace Logs' + }; + + if (originalQuery.editorType === EditorType.Builder && originalQuery.builderOptions.queryType === QueryType.Logs) { + // Copy fields directly from log search + traceLogsQuery.builderOptions = { + ...originalQuery.builderOptions, + filters: [ + { + type: 'string', + operator: FilterOperator.Equals, + filterType: 'custom', + key: '', + hint: ColumnHint.TraceId, + condition: 'AND', + value: '${__value.raw}' + } as StringFilter + ], + orderBy: [{ name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }], + meta: { + ...originalQuery.builderOptions.meta, + minimized: true, + } + }; + } else { + // Create new query based on log defaults + + const otelVersion = datasource.getLogsOtelVersion(); + const options: QueryBuilderOptions = { + database: datasource.getDefaultLogsDatabase() || traceLogsQuery.builderOptions.database || datasource.getDefaultDatabase(), + table: datasource.getDefaultLogsTable() || datasource.getDefaultTable() || traceLogsQuery.builderOptions.table, + queryType: QueryType.Logs, + columns: [], + orderBy: [{ name: '', hint: ColumnHint.Time, dir: OrderByDirection.ASC }], + filters: [ + { + type: 'string', + operator: FilterOperator.Equals, + filterType: 'custom', + key: '', + hint: ColumnHint.TraceId, + condition: 'AND', + value: '${__value.raw}' + } as StringFilter + ], + meta: { + minimized: true, + otelEnabled: Boolean(otelVersion), + otelVersion: otelVersion, + } + }; + + const defaultColumns = datasource.getDefaultLogsColumns(); + for (let [hint, colName] of defaultColumns) { + options.columns!.push({ name: colName, hint }); + } + + traceLogsQuery.builderOptions = options; + } + + const openInNewWindow = req.app !== CoreApp.Explore; + traceField.config.links = []; + traceField.config.links!.push({ + title: 'View trace', + targetBlank: openInNewWindow, + url: '', + internal: { + query: traceIdQuery, + datasourceUid: traceIdQuery.datasource?.uid!, + datasourceName: traceIdQuery.datasource?.type!, + panelsState: { + trace: { + spanId: '${__value.raw}' + } + } + } + }); + traceField.config.links!.push({ + title: 'View logs', + targetBlank: openInNewWindow, + url: '', + internal: { + query: traceLogsQuery, + datasourceUid: traceLogsQuery.datasource?.uid!, + datasourceName: traceLogsQuery.datasource?.type!, + } + }); + }); + + return res; +}; diff --git a/src/hooks/useBuilderOptionsState.test.ts b/src/hooks/useBuilderOptionsState.test.ts index 34e8eb8a..65a48e46 100644 --- a/src/hooks/useBuilderOptionsState.test.ts +++ b/src/hooks/useBuilderOptionsState.test.ts @@ -1,5 +1,5 @@ import { ColumnHint, QueryType } from "types/queryBuilder"; -import { setAllOptions, setColumnByHint, setDatabase, setOptions, setOtelEnabled, setOtelVersion, setQueryType, setTable, testFuncs } from "./useBuilderOptionsState"; +import { setAllOptions, setBuilderMinimized, setColumnByHint, setDatabase, setOptions, setOtelEnabled, setOtelVersion, setQueryType, setTable, testFuncs } from "./useBuilderOptionsState"; const { reducer, buildInitialState } = testFuncs; describe('reducer', () => { @@ -124,6 +124,13 @@ describe('reducer', () => { // Updated column is filtered and pushed to end of array expect(nextState.columns![3].name).toEqual('next_timestamp'); }); + it('applies SetBuilderMinimized action', async () => { + const prevState = buildInitialState(); + const action = setBuilderMinimized(true); + + const nextState = reducer(prevState, action); + expect(nextState.meta?.minimized).toBe(true); + }); }); diff --git a/src/hooks/useBuilderOptionsState.ts b/src/hooks/useBuilderOptionsState.ts index a5ee4a0a..f0331e38 100644 --- a/src/hooks/useBuilderOptionsState.ts +++ b/src/hooks/useBuilderOptionsState.ts @@ -11,6 +11,7 @@ enum BuilderOptionsActionType { SetOtelEnabled = 'set_otel_enabled', SetOtelVersion = 'set_otel_version', SetColumnByHint = 'set_column_by_hint', + SetBuilderMinimized = 'set_builder_minimized', }; type QueryBuilderOptionsReducerAction = { @@ -35,6 +36,8 @@ export const setTable = (table: string): BuilderOptionsReducerAction => createAc export const setOtelEnabled = (otelEnabled: boolean): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetOtelEnabled, { meta: { otelEnabled } }); export const setOtelVersion = (otelVersion: string): BuilderOptionsReducerAction => createAction(BuilderOptionsActionType.SetOtelVersion, { meta: { otelVersion } }); export const setColumnByHint = (column: SelectedColumn): GenericReducerAction => createGenericAction(BuilderOptionsActionType.SetColumnByHint, { column }); +export const setBuilderMinimized = (minimized: boolean): GenericReducerAction => createGenericAction(BuilderOptionsActionType.SetBuilderMinimized, { minimized }); + const reducer = (state: QueryBuilderOptions, action: BuilderOptionsReducerAction): QueryBuilderOptions => { const actionFn = actions.get(action.type); @@ -43,7 +46,7 @@ const reducer = (state: QueryBuilderOptions, action: BuilderOptionsReducerAction } const nextState = actionFn(state, action); - // console.log('ACTION:', action.type, 'PAYLOAD:', action.payload, 'NEXT STATE:', nextState); + // console.log('ACTION:', action.type, 'PAYLOAD:', action.payload, 'PREV STATE:', state, 'NEXT STATE:', nextState); return nextState; }; @@ -112,6 +115,12 @@ const actions = new Map { + const minimized = action.payload.minimized as boolean; + return mergeBuilderOptionsState(state, { + meta: { minimized } + }); }], ]); diff --git a/src/hooks/useDatabases.ts b/src/hooks/useDatabases.ts index 5d2af5c1..b7be2229 100644 --- a/src/hooks/useDatabases.ts +++ b/src/hooks/useDatabases.ts @@ -13,7 +13,7 @@ export default (datasource: Datasource): readonly string[] => { fetchDatabases(). then(databases => setDatabases(databases)). catch((ex: any) => { - console.error(ex); + console.error('Failed to fetch databases', ex); }); }, [datasource]); diff --git a/src/hooks/useTables.ts b/src/hooks/useTables.ts index cfdbd778..f0d55e4a 100644 --- a/src/hooks/useTables.ts +++ b/src/hooks/useTables.ts @@ -19,7 +19,7 @@ export default (datasource: Datasource, database: string): readonly string[] => setTables(tables); }). catch((ex: any) => { - console.error(ex); + console.error('Failed to fetch tables for database:', database, ex); }); return () => { diff --git a/src/hooks/useUniqueMapKeys.test.ts b/src/hooks/useUniqueMapKeys.test.ts new file mode 100644 index 00000000..42ca7721 --- /dev/null +++ b/src/hooks/useUniqueMapKeys.test.ts @@ -0,0 +1,65 @@ +import { renderHook } from '@testing-library/react'; +import { act } from 'react-dom/test-utils'; +import { Datasource } from 'data/CHDatasource'; +import useUniqueMapKeys from './useUniqueMapKeys'; + +describe('useUniqueMapKeys', () => { + it('should return empty array if invalid datasource is provided', async () => { + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useUniqueMapKeys(undefined!, 'col', 'db', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should return empty array if empty column string is provided', async () => { + const mockDs = {} as Datasource; + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useUniqueMapKeys(mockDs, '', 'db', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should return empty array if empty database string is provided', async () => { + const mockDs = {} as Datasource; + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useUniqueMapKeys(mockDs, 'col', '', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should return empty array if empty table string is provided', async () => { + const mockDs = {} as Datasource; + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useUniqueMapKeys(mockDs, 'col', 'db', '')); + result = r.result; + }); + + expect(result!.current).toHaveLength(0); + }); + + it('should fetch unique map keys', async () => { + const mockDs = {} as Datasource; + mockDs.fetchUniqueMapKeys = jest.fn((col: string, db: string, table: string) => Promise.resolve(['a', 'b'])); + + let result: { current: readonly string[] }; + await act(async () => { + const r = renderHook(() => useUniqueMapKeys(mockDs, 'col', 'db', 'table')); + result = r.result; + }); + + expect(result!.current).toHaveLength(2); + }); +}); diff --git a/src/hooks/useUniqueMapKeys.ts b/src/hooks/useUniqueMapKeys.ts new file mode 100644 index 00000000..61730da9 --- /dev/null +++ b/src/hooks/useUniqueMapKeys.ts @@ -0,0 +1,41 @@ +import { useState, useEffect, useRef } from 'react'; +import { Datasource } from 'data/CHDatasource'; + +export default (datasource: Datasource, mapColumn: string, database: string, table: string): readonly string[] => { + const [keys, setKeys] = useState([]); + + useEffect(() => { + if (!datasource || !mapColumn || !database || !table) { + return; + } + + let ignore = false; + datasource. + fetchUniqueMapKeys(mapColumn, database, table). + then(tables => { + if (ignore) { + return; + } + setKeys(tables); + }). + catch((ex: any) => { + console.error('Failed to fetch map keys for column:', mapColumn, database, table, ex); + }); + + return () => { + ignore = true; + }; + }, [datasource, mapColumn, database, table]); + + // Immediately return empty array on change so keys aren't stale + const lastDatabase = useRef(''); + const lastTable = useRef(''); + if (database !== lastDatabase.current || table !== lastTable.current) { + lastDatabase.current = database; + lastTable.current = table; + setKeys([]); + return []; + } + + return keys; +} diff --git a/src/labels.ts b/src/labels.ts index 7e891655..f0de7bb8 100644 --- a/src/labels.ts +++ b/src/labels.ts @@ -1,6 +1,86 @@ +import { ColumnHint } from "types/queryBuilder"; + export default { components: { Config: { + ConfigEditor: { + serverAddress: { + label: 'Server address', + placeholder: 'Server address', + tooltip: 'ClickHouse host address', + error: 'Server address required' + }, + serverPort: { + label: 'Server port', + insecureNativePort: '9000', + insecureHttpPort: '8123', + secureNativePort: '9440', + secureHttpPort: '8443', + tooltip: 'ClickHouse server port', + error: 'Port is required' + }, + path: { + label: 'HTTP URL Path', + tooltip: 'Additional URL path for HTTP requests', + placeholder: 'additional-path' + }, + protocol: { + label: 'Protocol', + tooltip: 'Native or HTTP for server protocol', + }, + username: { + label: 'Username', + placeholder: 'default', + tooltip: 'ClickHouse username', + }, + password: { + label: 'Password', + placeholder: 'password', + tooltip: 'ClickHouse password', + }, + tlsSkipVerify: { + label: 'Skip TLS Verify', + tooltip: 'Skip TLS Verify', + }, + tlsClientAuth: { + label: 'TLS Client Auth', + tooltip: 'TLS Client Auth', + }, + tlsAuthWithCACert: { + label: 'With CA Cert', + tooltip: 'Needed for verifying self-signed TLS Certs', + }, + tlsCACert: { + label: 'CA Cert', + placeholder: 'CA Cert. Begins with -----BEGIN CERTIFICATE-----', + }, + tlsClientCert: { + label: 'Client Cert', + placeholder: 'Client Cert. Begins with -----BEGIN CERTIFICATE-----', + }, + tlsClientKey: { + label: 'Client Key', + placeholder: 'Client Key. Begins with -----BEGIN RSA PRIVATE KEY-----', + }, + secure: { + label: 'Secure Connection', + tooltip: 'Toggle on if the connection is secure', + }, + secureSocksProxy: { + label: 'Enable Secure Socks Proxy', + tooltip: 'Enable proxying the datasource connection through the secure socks proxy to a different network.', + }, + }, + HttpHeadersConfig: { + title: 'HTTP Headers', + description: 'Add HTTP headers when querying the database', + headerNameLabel: 'Header Name', + headerNamePlaceholder: 'X-Custom-Header', + insecureHeaderValueLabel: 'Header Value', + secureHeaderValueLabel: 'Secure Header Value', + secureLabel: 'Secure', + addHeaderLabel: 'Add Header' + }, DefaultDatabaseTableConfig: { title: 'Default DB and table', database: { @@ -48,7 +128,7 @@ export default { label: 'Default trace table', description: 'the default table used by the trace query builder', name: 'defaultTable', - placeholder: 'traces' + placeholder: 'otel_traces' }, columns: { title: 'Default columns', @@ -105,7 +185,7 @@ export default { label: 'Default log table', description: 'the default table used by the logs query builder', name: 'defaultTable', - placeholder: 'logs' + placeholder: 'otel_logs' }, columns: { title: 'Default columns', @@ -141,6 +221,10 @@ export default { confirmText: 'Yes', }, }, + expandBuilderButton: { + label: 'Show full query', + tooltip: 'Shows the full query builder' + }, QueryTypeSwitcher: { label: 'Query Type', tooltip: 'Sets the layout for the query builder', @@ -161,7 +245,7 @@ export default { tooltip: 'A list of columns to include in the query' }, OtelVersionSelect: { - label: 'Use OTEL', + label: 'Use OTel', tooltip: 'Enables Open Telemetry schema versioning' }, LimitEditor: { @@ -188,6 +272,7 @@ export default { label: 'Filters', tooltip: `List of filters`, addLabel: 'Filter', + mapKeyPlaceholder: 'map key' }, GroupByEditor: { label: 'Group By', @@ -210,6 +295,15 @@ export default { label: 'Live View', tooltip: 'Enable to update logs in real time' }, + logMessageFilter: { + label: 'Message Filter', + tooltip: 'Applies a LIKE filter to the log message body', + clearButton: 'Clear' + }, + logLevelFilter: { + label: 'Level Filter', + tooltip: 'Applies a filter to the log level' + }, }, TimeSeriesQueryBuilder: { simpleQueryModeLabel: 'Simple', @@ -293,6 +387,21 @@ export default { logs: 'Logs', timeseries: 'Time Series', traces: 'Traces', + }, + ColumnHint: { + [ColumnHint.Time]: 'Time', + + [ColumnHint.LogLevel]: 'Level', + [ColumnHint.LogMessage]: 'Message', + + [ColumnHint.TraceId]: 'Trace ID', + [ColumnHint.TraceSpanId]: 'Span ID', + [ColumnHint.TraceParentSpanId]: 'Parent Span ID', + [ColumnHint.TraceServiceName]: 'Service Name', + [ColumnHint.TraceOperationName]: 'Operation Name', + [ColumnHint.TraceDurationTime]: 'Duration Time', + [ColumnHint.TraceTags]: 'Tags', + [ColumnHint.TraceServiceTags]: 'Service Tags', } } } diff --git a/src/otel.ts b/src/otel.ts index a8f45035..3046715a 100644 --- a/src/otel.ts +++ b/src/otel.ts @@ -1,33 +1,58 @@ import { ColumnHint, TimeUnit } from "types/queryBuilder"; +const defaultLogsTable = 'otel_logs'; +const defaultTraceTable = 'otel_traces' + export interface OtelVersion { name: string; version: string; + specUrl?: string; + logsTable: string; logColumnMap: Map; + logLevels: string[]; + traceTable: string; traceColumnMap: Map; traceDurationUnit: TimeUnit.Nanoseconds; } +const otel129: OtelVersion = { + name: '1.2.9', + version: '1.29.0', + specUrl: 'https://opentelemetry.io/docs/specs/otel', + logsTable: defaultLogsTable, + logColumnMap: new Map([ + [ColumnHint.Time, 'Timestamp'], + [ColumnHint.LogMessage, 'Body'], + [ColumnHint.LogLevel, 'SeverityText'], + [ColumnHint.TraceId, 'TraceId'], + ]), + logLevels: [ + 'TRACE', + 'DEBUG', + 'INFO', + 'WARN', + 'ERROR', + 'FATAL' + ], + traceTable: defaultTraceTable, + traceColumnMap: new Map([ + [ColumnHint.Time, 'Timestamp'], + [ColumnHint.TraceId, 'TraceId'], + [ColumnHint.TraceSpanId, 'SpanId'], + [ColumnHint.TraceParentSpanId, 'ParentSpanId'], + [ColumnHint.TraceServiceName, 'ServiceName'], + [ColumnHint.TraceOperationName, 'SpanName'], + [ColumnHint.TraceDurationTime, 'Duration'], + [ColumnHint.TraceTags, 'SpanAttributes'], + [ColumnHint.TraceServiceTags, 'ResourceAttributes'], + ]), + traceDurationUnit: TimeUnit.Nanoseconds, +}; + export const versions: readonly OtelVersion[] = [ - { - name: 'latest', - version: '1.26.0', - logColumnMap: new Map([ - [ColumnHint.Time, 'Timestamp'], - [ColumnHint.LogMessage, 'Body'], - [ColumnHint.LogLevel, 'SeverityText'], - ]), - traceColumnMap: new Map([ - [ColumnHint.Time, 'Timestamp'], - [ColumnHint.TraceId, 'TraceId'], - [ColumnHint.TraceSpanId, 'SpanId'], - [ColumnHint.TraceParentSpanId, 'ParentSpanId'], - [ColumnHint.TraceServiceName, 'ServiceName'], - [ColumnHint.TraceOperationName, 'SpanName'], - [ColumnHint.TraceDurationTime, 'Duration'], - [ColumnHint.TraceTags, 'SpanAttributes'], - [ColumnHint.TraceServiceTags, 'ResourceAttributes'], - ]), - traceDurationUnit: TimeUnit.Nanoseconds, - }, + // When selected, will always keep OTEL config up to date as new versions are added + { ...otel129, name: `latest (${otel129.name})`, version: 'latest' }, + otel129, ]; + +export const getLatestVersion = (): OtelVersion => versions[0]; diff --git a/src/selectors.ts b/src/selectors.ts index 3f522401..663bae2e 100644 --- a/src/selectors.ts +++ b/src/selectors.ts @@ -1,68 +1,5 @@ import { E2ESelectors } from '@grafana/e2e-selectors'; export const Components = { - ConfigEditor: { - ServerAddress: { - label: 'Server address', - placeholder: 'Server TCP address', - tooltip: 'ClickHouse native TCP server address', - }, - ServerPort: { - label: 'Server port', - placeholder: (secure: string) => `Typically ${secure === 'true' ? '9440' : '9000'}`, - tooltip: 'ClickHouse native TCP port. Typically 9000 for unsecure, 9440 for secure', - }, - Path: { - label: 'Path', - placeholder: 'Additional URL path for HTTP requests', - tooltip: 'Additional URL path for HTTP requests', - }, - Protocol: { - label: 'Protocol', - tooltip: 'Native or HTTP for transport', - }, - Username: { - label: 'Username', - placeholder: 'Username', - tooltip: 'ClickHouse username', - }, - Password: { - label: 'Password', - placeholder: 'Password', - tooltip: 'ClickHouse password', - }, - TLSSkipVerify: { - label: 'Skip TLS Verify', - tooltip: 'Skip TLS Verify', - }, - TLSClientAuth: { - label: 'TLS Client Auth', - tooltip: 'TLS Client Auth', - }, - TLSAuthWithCACert: { - label: 'With CA Cert', - tooltip: 'Needed for verifying self-signed TLS Certs', - }, - TLSCACert: { - label: 'CA Cert', - placeholder: 'CA Cert. Begins with -----BEGIN CERTIFICATE-----', - }, - TLSClientCert: { - label: 'Client Cert', - placeholder: 'Client Cert. Begins with -----BEGIN CERTIFICATE-----', - }, - TLSClientKey: { - label: 'Client Key', - placeholder: 'Client Key. Begins with -----BEGIN RSA PRIVATE KEY-----', - }, - Secure: { - label: 'Secure Connection', - tooltip: 'Toggle on if the connection is secure', - }, - SecureSocksProxy: { - label: 'Enable Secure Socks Proxy', - tooltip: 'Enable proxying the datasource connection through the secure socks proxy to a different network.', - }, - }, QueryEditor: { CodeEditor: { input: () => '.monaco-editor textarea', @@ -176,7 +113,23 @@ export const Components = { }, }, }, + Config: { + HttpHeaderConfig: { + headerEditor: 'config__http-header-config__header-editor', + addHeaderButton: 'config__http-header-config__add-header-button', + removeHeaderButton: 'config__http-header-config__remove-header-button', + headerSecureSwitch: 'config__http-header-config__header-secure-switch', + headerNameInput: 'config__http-header-config__header-name-input', + headerValueInput: 'config__http-header-config__header-value-input' + } + }, QueryBuilder: { + expandBuilderButton: 'query-builder__expand-builder-button', + LogsQueryBuilder: { + LogMessageLikeInput: { + input: 'query-builder__logs-query-builder__log-message-like-input__input', + } + }, AggregateEditor: { sectionLabel: 'query-builder__aggregate-editor__section-label', itemWrapper: 'query-builder__aggregate-editor__item-wrapper', @@ -191,6 +144,9 @@ export const Components = { }, LimitEditor: { input: 'query-builder__limit-editor__input' + }, + TraceIdInput: { + input: 'query-builder__trace-id-input__input' } } }; diff --git a/src/tracking.test.ts b/src/tracking.test.ts index 30068fa2..6081c7a9 100644 --- a/src/tracking.test.ts +++ b/src/tracking.test.ts @@ -1,133 +1,373 @@ -import { analyzeQueries } from 'tracking'; -import { CHQuery, EditorType } from 'types/sql'; +import { ClickhouseCounters, analyzeQueries } from 'tracking'; +import { CHBuilderQuery, CHQuery, CHSqlQuery, EditorType } from 'types/sql'; import { QueryType, BuilderMode } from 'types/queryBuilder'; +interface AnalyzeQueriesTestCase { + description: string; + queries: CHQuery[]; + expectedCounters: ClickhouseCounters; +} + describe('analyzeQueries', () => { - [ - // { - // description: 'should count 1 sql query (with a default mode of auto)', - // queries: [{ editorType: EditorType.SQL, QueryType: QueryType.Table }], - // expectedCounters: { - // sql_queries: 1, - // sql_query_format_auto: 1, - // sql_query_format_table: 0, - // sql_query_format_logs: 0, - // sql_query_format_time_series: 0, - // sql_query_format_trace: 0, - // builder_queries: 0, - // builder_table_queries: 0, - // builder_aggregate_queries: 0, - // builder_time_series_queries: 0, - // }, - // }, - { - description: 'should count 1 sql query with a mode of Table', - queries: [{ editorType: EditorType.SQL, queryType: QueryType.Table }], + const baseQuery: Partial = { + pluginVersion: '', + rawSql: '', + refId: '' + }; + + const emptyCounters: ClickhouseCounters = { + sql_queries: 0, + sql_query_type_table: 0, + sql_query_type_logs: 0, + sql_query_type_timeseries: 0, + sql_query_type_traces: 0, + + builder_queries: 0, + builder_query_type_table: 0, + builder_query_type_table_simple: 0, + builder_query_type_table_aggregate: 0, + builder_query_type_logs: 0, + builder_query_type_timeseries: 0, + builder_query_type_timeseries_simple: 0, + builder_query_type_timeseries_aggregate: 0, + builder_query_type_traces: 0, + builder_query_type_traces_search: 0, + builder_query_type_traces_id: 0, + builder_minimized_queries: 0, + builder_otel_queries: 0 + }; + + const cases: AnalyzeQueriesTestCase[] = [ + { + description: 'should count 1 sql query with no query types', + queries: [{ + ...baseQuery, + editorType: EditorType.SQL + } as CHSqlQuery], expectedCounters: { + ...emptyCounters, + sql_queries: 1 + } + }, + { + description: 'should count 1 sql query with Table type', + queries: [{ + ...baseQuery, + editorType: EditorType.SQL, + queryType: QueryType.Table + } as CHSqlQuery], + expectedCounters: { + ...emptyCounters, sql_queries: 1, - sql_query_format_table: 1, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, - builder_queries: 0, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, - }, + sql_query_type_table: 1 + } }, { - description: 'should count 1 sql query with a mode of Logs', - queries: [{ editorType: EditorType.SQL, queryType: QueryType.Logs }], + description: 'should count 1 sql query with Logs type', + queries: [{ + ...baseQuery, + editorType: EditorType.SQL, + queryType: QueryType.Logs + } as CHSqlQuery], expectedCounters: { + ...emptyCounters, sql_queries: 1, - sql_query_format_table: 0, - sql_query_format_logs: 1, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, - builder_queries: 0, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, - }, + sql_query_type_logs: 1 + } }, { - description: 'should count 1 sql query with a mode of Time Series', - queries: [{ editorType: EditorType.SQL, queryType: QueryType.TimeSeries }], + description: 'should count 1 sql query with TimeSeries type', + queries: [{ + ...baseQuery, + editorType: EditorType.SQL, + queryType: QueryType.TimeSeries + } as CHSqlQuery], expectedCounters: { + ...emptyCounters, sql_queries: 1, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 1, - sql_query_format_trace: 0, - builder_queries: 0, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, - }, + sql_query_type_timeseries: 1 + } }, { - description: 'should count 1 sql query with a mode of Traces', - queries: [{ editorType: EditorType.SQL, queryType: QueryType.Traces }], + description: 'should count 1 sql query with Traces type', + queries: [{ + ...baseQuery, + editorType: EditorType.SQL, + queryType: QueryType.Traces + } as CHSqlQuery], expectedCounters: { + ...emptyCounters, sql_queries: 1, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 1, - builder_queries: 0, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, - }, - }, - { - description: 'should count 1 builder query (with a default mode of Table)', - queries: [{ editorType: EditorType.Builder, builderOptions: { mode: BuilderMode.List } }], - expectedCounters: { - sql_queries: 0, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, + sql_query_type_traces: 1 + } + }, + + { + description: 'should count 1 builder query with no builderOptions', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1 + } + }, + { + description: 'should count 1 builder query with empty builderOptions', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: {} + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1 + } + }, + { + description: 'should count 1 builder query with Table type, no mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Table + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_table: 1, + builder_query_type_table_simple: 1 // Table defaults to simple + } + }, + { + description: 'should count 1 builder query with Table type, simple mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Table, + mode: BuilderMode.List + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_table: 1, + builder_query_type_table_simple: 1 + } + }, + { + description: 'should count 1 builder query with Table type, aggregate mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Table, + mode: BuilderMode.Aggregate + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_table: 1, + builder_query_type_table_aggregate: 1 + } + }, + { + description: 'should count 1 builder query with Logs type', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Logs + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_logs: 1, + } + }, + { + description: 'should count 1 builder query with TimeSeries type, no mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.TimeSeries + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_timeseries: 1, + builder_query_type_timeseries_simple: 1 // TimeSeries defaults to simple + } + }, + { + description: 'should count 1 builder query with TimeSeries type, simple mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.TimeSeries, + mode: BuilderMode.Aggregate + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_timeseries: 1, + builder_query_type_timeseries_simple: 1 + } + }, + { + description: 'should count 1 builder query with TimeSeries type, aggregate mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.TimeSeries, + mode: BuilderMode.Trend + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_timeseries: 1, + builder_query_type_timeseries_aggregate: 1 + } + }, + { + description: 'should count 1 builder query with Traces type, no mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Traces + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, builder_queries: 1, - builder_table_queries: 1, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, - }, + builder_query_type_traces: 1, + builder_query_type_traces_search: 1 // Traces defaults to search mode + } }, { - description: 'should count 1 builder query with a mode of Aggregate', - queries: [{ editorType: EditorType.Builder, builderOptions: { mode: BuilderMode.Aggregate } }], + description: 'should count 1 builder query with Traces type, trace ID mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Traces, + meta: { + isTraceIdMode: true + } + } + } as CHBuilderQuery], expectedCounters: { - sql_queries: 0, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, + ...emptyCounters, builder_queries: 1, - builder_table_queries: 0, - builder_aggregate_queries: 1, - builder_time_series_queries: 0, - }, + builder_query_type_traces: 1, + builder_query_type_traces_id: 1 + } }, { - description: 'should count 1 builder query with a mode of Time Series', - queries: [{ editorType: EditorType.Builder, builderOptions: { mode: BuilderMode.Trend } }], + description: 'should count 1 builder query with Traces type, trace search mode', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Traces, + meta: { + isTraceIdMode: false + } + } + } as CHBuilderQuery], expectedCounters: { - sql_queries: 0, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, + ...emptyCounters, builder_queries: 1, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 1, - }, - }, - ].forEach((t) => { - it(t.description, () => { - expect(analyzeQueries(t.queries as CHQuery[])).toMatchObject(t.expectedCounters); - }); + builder_query_type_traces: 1, + builder_query_type_traces_search: 1 + } + }, + { + description: 'should count 1 minimized query', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Table, + meta: { + minimized: true + } + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_table: 1, + builder_query_type_table_simple: 1, + builder_minimized_queries: 1 + } + }, + { + description: 'should count 1 otel query', + queries: [{ + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Table, + meta: { + otelEnabled: true + } + } + } as CHBuilderQuery], + expectedCounters: { + ...emptyCounters, + builder_queries: 1, + builder_query_type_table: 1, + builder_query_type_table_simple: 1, + builder_otel_queries: 1 + } + }, + { + description: 'should count 3 queries, mixed types', + queries: [ + { + ...baseQuery, + editorType: EditorType.SQL + } as CHSqlQuery, + { + ...baseQuery, + editorType: EditorType.Builder, + builderOptions: { + queryType: QueryType.Table + } + } as CHBuilderQuery, + { + ...baseQuery, + editorType: EditorType.SQL + } as CHSqlQuery], + expectedCounters: { + ...emptyCounters, + sql_queries: 2, + builder_queries: 1, + builder_query_type_table: 1, + builder_query_type_table_simple: 1, + } + }, + { + description: 'should count 0 queries', + queries: [], + expectedCounters: { ...emptyCounters } + }, + ]; + + it.each(cases)('$description', (c) => { + expect(analyzeQueries(c.queries)).toMatchObject(c.expectedCounters); }); }); diff --git a/src/tracking.ts b/src/tracking.ts index a62a167c..34359bcc 100644 --- a/src/tracking.ts +++ b/src/tracking.ts @@ -2,22 +2,30 @@ import { reportInteraction } from '@grafana/runtime'; import { CHQuery, EditorType } from 'types/sql'; import { QueryType, BuilderMode } from 'types/queryBuilder'; -// TODO: v4, determine new/updated fields to track - export const trackClickhouseDashboardLoaded = (props: ClickhouseDashboardLoadedProps) => { reportInteraction('grafana_ds_clickhouse_dashboard_loaded', props); }; export type ClickhouseCounters = { sql_queries: number; - sql_query_format_table: number; - sql_query_format_logs: number; - sql_query_format_time_series: number; - sql_query_format_trace: number; + sql_query_type_table: number; + sql_query_type_logs: number; + sql_query_type_timeseries: number; + sql_query_type_traces: number; + builder_queries: number; - builder_table_queries: number; - builder_aggregate_queries: number; - builder_time_series_queries: number; + builder_query_type_table: number; + builder_query_type_table_simple: number; + builder_query_type_table_aggregate: number; + builder_query_type_logs: number; + builder_query_type_timeseries: number; + builder_query_type_timeseries_simple: number; + builder_query_type_timeseries_aggregate: number; + builder_query_type_traces: number; + builder_query_type_traces_search: number; + builder_query_type_traces_id: number; + builder_minimized_queries: number; + builder_otel_queries: number; }; export interface ClickhouseDashboardLoadedProps extends ClickhouseCounters { @@ -29,44 +37,85 @@ export interface ClickhouseDashboardLoadedProps extends ClickhouseCounters { } export const analyzeQueries = (queries: CHQuery[]): ClickhouseCounters => { - const counters = { + const c: ClickhouseCounters = { sql_queries: 0, - sql_query_format_table: 0, - sql_query_format_logs: 0, - sql_query_format_time_series: 0, - sql_query_format_trace: 0, + sql_query_type_table: 0, + sql_query_type_logs: 0, + sql_query_type_timeseries: 0, + sql_query_type_traces: 0, + builder_queries: 0, - builder_table_queries: 0, - builder_aggregate_queries: 0, - builder_time_series_queries: 0, + builder_query_type_table: 0, + builder_query_type_table_simple: 0, + builder_query_type_table_aggregate: 0, + builder_query_type_logs: 0, + builder_query_type_timeseries: 0, + builder_query_type_timeseries_simple: 0, + builder_query_type_timeseries_aggregate: 0, + builder_query_type_traces: 0, + builder_query_type_traces_search: 0, + builder_query_type_traces_id: 0, + builder_minimized_queries: 0, + builder_otel_queries: 0 }; - queries.forEach(query => { - switch (query.editorType) { - case EditorType.SQL: - counters.sql_queries++; - if (query.queryType === QueryType.Table) { - counters.sql_query_format_table++; - } else if (query.queryType === QueryType.Logs) { - counters.sql_query_format_logs++; - } else if (query.queryType === QueryType.TimeSeries) { - counters.sql_query_format_time_series++; - } else if (query.queryType === QueryType.Traces) { - counters.sql_query_format_trace++; + queries.forEach(q => { + if (q.editorType === EditorType.SQL) { + c.sql_queries++; + + if (q.queryType === QueryType.Table) { + c.sql_query_type_table++; + } else if (q.queryType === QueryType.Logs) { + c.sql_query_type_logs++; + } else if (q.queryType === QueryType.TimeSeries) { + c.sql_query_type_timeseries++; + } else if (q.queryType === QueryType.Traces) { + c.sql_query_type_traces++; + } + } else if (q.editorType === EditorType.Builder) { + c.builder_queries++; + + if (!q.builderOptions) { + return; + } + + if (q.builderOptions.queryType === QueryType.Table) { + c.builder_query_type_table++; + + if (q.builderOptions.mode === BuilderMode.Aggregate) { + c.builder_query_type_table_aggregate++; + } else { + c.builder_query_type_table_simple++; + } + } else if (q.builderOptions.queryType === QueryType.Logs) { + c.builder_query_type_logs++; + } else if (q.builderOptions.queryType === QueryType.TimeSeries) { + c.builder_query_type_timeseries++; + + if (q.builderOptions.mode === BuilderMode.Trend) { + c.builder_query_type_timeseries_aggregate++; + } else { + c.builder_query_type_timeseries_simple++; } - break; - case EditorType.Builder: - counters.builder_queries++; - if (query.builderOptions.mode === BuilderMode.Aggregate) { - counters.builder_aggregate_queries++; - } else if (query.builderOptions.mode === BuilderMode.List) { - counters.builder_table_queries++; - } else if (query.builderOptions.mode === BuilderMode.Trend) { - counters.builder_time_series_queries++; + } else if (q.builderOptions.queryType === QueryType.Traces) { + c.builder_query_type_traces++; + + if (q.builderOptions.meta?.isTraceIdMode) { + c.builder_query_type_traces_id++; + } else { + c.builder_query_type_traces_search++; } - break; + } + + if (q.builderOptions.meta?.minimized) { + c.builder_minimized_queries++; + } + + if (q.builderOptions.meta?.otelEnabled) { + c.builder_otel_queries++; + } } }); - return counters; + return c; }; diff --git a/src/types/config.ts b/src/types/config.ts index 9ed7ee84..59690dc3 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,6 +1,11 @@ -import { DataSourceJsonData } from '@grafana/data'; +import { DataSourceJsonData, KeyValue } from '@grafana/data'; export interface CHConfig extends DataSourceJsonData { + /** + * The version of the plugin this config was saved with + */ + version: string; + host: string; port: number; protocol: Protocol; @@ -23,21 +28,32 @@ export interface CHConfig extends DataSourceJsonData { logs?: CHLogsConfig; traces?: CHTracesConfig; + httpHeaders?: CHHttpHeader[]; + customSettings?: CHCustomSetting[]; enableSecureSocksProxy?: boolean; } -export interface CHCustomSetting { - setting: string; - value: string; -} +interface CHSecureConfigProperties { + password?: string; -export interface CHSecureConfig { - password: string; tlsCACert?: string; tlsClientCert?: string; tlsClientKey?: string; } +export type CHSecureConfig = CHSecureConfigProperties | KeyValue; + +export interface CHHttpHeader { + name: string; + value: string; + secure: boolean; +} + +export interface CHCustomSetting { + setting: string; + value: string; +} + export interface CHLogsConfig { defaultDatabase?: string; diff --git a/src/types/queryBuilder.ts b/src/types/queryBuilder.ts index 1b831376..27b0e5f0 100644 --- a/src/types/queryBuilder.ts +++ b/src/types/queryBuilder.ts @@ -37,12 +37,23 @@ export interface QueryBuilderOptions { * Contains metadata for editor-specific use cases. */ meta?: { + /** + * When enabled, will hide most/all of the query builder options. + * + * Intended to be used for trace ID lookups where we only care to show the visualization panel + */ + minimized?: boolean; + // Logs liveView?: boolean; + logMessageLike?: string; // Trace - isTraceSearchMode?: boolean; traceDurationUnit?: TimeUnit; + /** + * true for trace ID mode, false for trace search mode + */ + isTraceIdMode?: boolean; traceId?: string; // Logs & Traces @@ -150,10 +161,30 @@ export enum OrderByDirection { export interface OrderBy { name: string; dir: OrderByDirection; + /** + * true if this orderBy was configured to be present by default + */ default?: boolean; + + /** + * If provided, SQL generator will ignore "name" and instead + * find the intended column by the hint + */ + hint?: ColumnHint; } export enum FilterOperator { + /** + * A placeholder filter that gets excluded from SQL generation + */ + IsAnything = 'IS ANYTHING', + + /** + * Compares to an empty string + */ + IsEmpty = 'IS EMPTY', + IsNotEmpty = 'IS NOT EMPTY', + IsNull = 'IS NULL', IsNotNull = 'IS NOT NULL', Equals = '=', @@ -172,7 +203,14 @@ export enum FilterOperator { export interface CommonFilterProps { filterType: 'custom'; - key: string; // Column name + /** + * Column name + */ + key: string; + /** + * key used when using a map type: exampleMap['mapKey'] + */ + mapKey?: string; type: string; condition: 'AND' | 'OR'; @@ -183,28 +221,39 @@ export interface CommonFilterProps { id?: string; /** * If provided, SQL generator will ignore "key" and instead - * find the intended column by the hint + * find the intended column by the hint. + * + * Note that the column MUST be present in the selected columns array in order + * for the filter to be applied unless key is also provided. */ hint?: ColumnHint; } export interface NullFilter extends CommonFilterProps { - operator: FilterOperator.IsNull | FilterOperator.IsNotNull; + operator: FilterOperator.IsAnything | FilterOperator.IsNull | FilterOperator.IsNotNull; } export interface BooleanFilter extends CommonFilterProps { type: 'boolean'; - operator: FilterOperator.Equals | FilterOperator.NotEquals; + operator: FilterOperator.IsAnything | FilterOperator.Equals | FilterOperator.NotEquals; value: boolean; } export interface StringFilter extends CommonFilterProps { - operator: FilterOperator.Equals | FilterOperator.NotEquals | FilterOperator.Like | FilterOperator.NotLike; + operator: + | FilterOperator.IsAnything + | FilterOperator.IsEmpty + | FilterOperator.IsNotEmpty + | FilterOperator.Equals + | FilterOperator.NotEquals + | FilterOperator.Like + | FilterOperator.NotLike; value: string; } export interface NumberFilter extends CommonFilterProps { operator: + | FilterOperator.IsAnything | FilterOperator.Equals | FilterOperator.NotEquals | FilterOperator.LessThan @@ -217,6 +266,7 @@ export interface NumberFilter extends CommonFilterProps { export interface DateFilterWithValue extends CommonFilterProps { type: 'datetime' | 'date'; operator: + | FilterOperator.IsAnything | FilterOperator.Equals | FilterOperator.NotEquals | FilterOperator.LessThan @@ -228,13 +278,13 @@ export interface DateFilterWithValue extends CommonFilterProps { export interface DateFilterWithoutValue extends CommonFilterProps { type: 'datetime' | 'date'; - operator: FilterOperator.WithInGrafanaTimeRange | FilterOperator.OutsideGrafanaTimeRange; + operator: FilterOperator.IsAnything | FilterOperator.WithInGrafanaTimeRange | FilterOperator.OutsideGrafanaTimeRange; } export type DateFilter = DateFilterWithValue | DateFilterWithoutValue; export interface MultiFilter extends CommonFilterProps { - operator: FilterOperator.In | FilterOperator.NotIn; + operator: FilterOperator.IsAnything | FilterOperator.In | FilterOperator.NotIn; value: string[]; } diff --git a/src/views/CHConfigEditor.test.tsx b/src/views/CHConfigEditor.test.tsx index 27c73f7e..040d6763 100644 --- a/src/views/CHConfigEditor.test.tsx +++ b/src/views/CHConfigEditor.test.tsx @@ -2,9 +2,9 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { ConfigEditor } from './CHConfigEditor'; import { mockConfigEditorProps } from '__mocks__/ConfigEditor'; -import { Components } from 'selectors'; import '@testing-library/jest-dom'; import { Protocol } from 'types/config'; +import allLabels from 'labels'; jest.mock('@grafana/runtime', () => { const original = jest.requireActual('@grafana/runtime'); @@ -23,13 +23,15 @@ jest.mock('@grafana/runtime', () => { }); describe('ConfigEditor', () => { + const labels = allLabels.components.Config.ConfigEditor; + it('new editor', () => { render(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.ServerAddress.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.ServerPort.placeholder('false'))).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.Username.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.Password.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.Path.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.serverAddress.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.serverPort.insecureHttpPort)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.username.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.password.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.path.placeholder)).toBeInTheDocument(); }); it('with password', async () => { render( @@ -42,9 +44,9 @@ describe('ConfigEditor', () => { }} /> ); - expect(screen.getByPlaceholderText(Components.ConfigEditor.ServerAddress.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.ServerPort.placeholder('false'))).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.Username.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.serverAddress.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.serverPort.insecureHttpPort)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.username.placeholder)).toBeInTheDocument(); const a = screen.getByText('Reset'); expect(a).toBeInTheDocument(); }); @@ -55,11 +57,11 @@ describe('ConfigEditor', () => { {...mockConfigEditorProps()} options={{ ...mockConfigEditorProps().options, - jsonData: { ...mockConfigEditorProps().options.jsonData, path }, + jsonData: { ...mockConfigEditorProps().options.jsonData, path, protocol: Protocol.Http }, }} /> ); - expect(screen.queryByPlaceholderText(Components.ConfigEditor.Path.placeholder)).toHaveValue(path); + expect(screen.queryByPlaceholderText(labels.path.placeholder)).toHaveValue(path); }); it('with secure connection', async () => { render( @@ -71,7 +73,7 @@ describe('ConfigEditor', () => { }} /> ); - expect(screen.queryByPlaceholderText(Components.ConfigEditor.ServerPort.placeholder('true'))).toBeInTheDocument(); + expect(screen.queryByPlaceholderText(labels.serverPort.secureHttpPort)).toBeInTheDocument(); }); it('with protocol', async () => { render( @@ -88,7 +90,7 @@ describe('ConfigEditor', () => { }); it('without tlsCACert', async () => { render(); - expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSCACert.placeholder)).not.toBeInTheDocument(); + expect(screen.queryByPlaceholderText(labels.tlsCACert.placeholder)).not.toBeInTheDocument(); }); it('with tlsCACert', async () => { render( @@ -100,12 +102,12 @@ describe('ConfigEditor', () => { }} /> ); - expect(screen.getByPlaceholderText(Components.ConfigEditor.TLSCACert.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.tlsCACert.placeholder)).toBeInTheDocument(); }); it('without tlsAuth', async () => { render(); - expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientCert.placeholder)).not.toBeInTheDocument(); - expect(screen.queryByPlaceholderText(Components.ConfigEditor.TLSClientKey.placeholder)).not.toBeInTheDocument(); + expect(screen.queryByPlaceholderText(labels.tlsClientCert.placeholder)).not.toBeInTheDocument(); + expect(screen.queryByPlaceholderText(labels.tlsClientKey.placeholder)).not.toBeInTheDocument(); }); it('with tlsAuth', async () => { render( @@ -117,8 +119,8 @@ describe('ConfigEditor', () => { }} /> ); - expect(screen.getByPlaceholderText(Components.ConfigEditor.TLSClientCert.placeholder)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(Components.ConfigEditor.TLSClientKey.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.tlsClientCert.placeholder)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(labels.tlsClientKey.placeholder)).toBeInTheDocument(); }); it('with additional properties', async () => { const jsonDataOverrides = { @@ -130,7 +132,7 @@ describe('ConfigEditor', () => { customSettings: [{ setting: 'test-setting', value: 'test-value' }], }; render(); - expect(screen.getByText(Components.ConfigEditor.SecureSocksProxy.label)).toBeInTheDocument(); + expect(screen.getByText(labels.secureSocksProxy.label)).toBeInTheDocument(); expect(screen.getByDisplayValue(jsonDataOverrides.customSettings[0].setting)).toBeInTheDocument(); expect(screen.getByDisplayValue(jsonDataOverrides.customSettings[0].value)).toBeInTheDocument(); }); diff --git a/src/views/CHConfigEditor.tsx b/src/views/CHConfigEditor.tsx index 83c2b53a..caec1c3e 100644 --- a/src/views/CHConfigEditor.tsx +++ b/src/views/CHConfigEditor.tsx @@ -6,7 +6,6 @@ import { } from '@grafana/data'; import { RadioButtonGroup, Switch, Input, SecretInput, Button, Field, HorizontalGroup } from '@grafana/ui'; import { CertificationKey } from '../components/ui/CertificationKey'; -import { Components } from 'selectors'; import { CHConfig, CHCustomSetting, CHSecureConfig, CHLogsConfig, Protocol, CHTracesConfig } from 'types/config'; import { gte as versionGte } from 'semver'; import { ConfigSection, ConfigSubSection, DataSourceDescription } from '@grafana/experimental'; @@ -17,14 +16,16 @@ import { DefaultDatabaseTableConfig } from 'components/configEditor/DefaultDatab import { QuerySettingsConfig } from 'components/configEditor/QuerySettingsConfig'; import { LogsConfig } from 'components/configEditor/LogsConfig'; import { TracesConfig } from 'components/configEditor/TracesConfig'; -import { useMigrateV3Config } from './CHConfigEditorHooks'; +import { HttpHeadersConfig } from 'components/configEditor/HttpHeadersConfig'; +import allLabels from 'labels'; +import { onHttpHeadersChange, useConfigDefaults, useMigrateV3Config } from './CHConfigEditorHooks'; -export interface ConfigEditorProps extends DataSourcePluginOptionsEditorProps {} +export interface ConfigEditorProps extends DataSourcePluginOptionsEditorProps {} export const ConfigEditor: React.FC = (props) => { const { options, onOptionsChange } = props; const { jsonData, secureJsonFields } = options; - const labels = Components.ConfigEditor; + const labels = allLabels.components.Config.ConfigEditor; const secureJsonData = (options.secureJsonData || {}) as CHSecureConfig; const hasTLSCACert = secureJsonFields && secureJsonFields.tlsCACert; const hasTLSClientCert = secureJsonFields && secureJsonFields.tlsClientCert; @@ -34,7 +35,8 @@ export const ConfigEditor: React.FC = (props) => { { label: 'HTTP', value: Protocol.Http }, ]; - useMigrateV3Config(options, onOptionsChange); + useMigrateV3Config(options, onOptionsChange); + useConfigDefaults(options, onOptionsChange); const onPortChange = (port: string) => { onOptionsChange({ @@ -152,7 +154,8 @@ export const ConfigEditor: React.FC = (props) => { const [customSettings, setCustomSettings] = useState(jsonData.customSettings || []); - const hasAdditionalSettings = !!( + const hasAdditionalSettings = Boolean( + window.location.hash || // if trying to link to section on page, open all settings (React breaks this?) options.jsonData.defaultDatabase || options.jsonData.defaultTable || options.jsonData.dialTimeout || @@ -164,6 +167,11 @@ export const ConfigEditor: React.FC = (props) => { options.jsonData.traces ); + const defaultPort = jsonData.secure ? + (jsonData.protocol === Protocol.Native ? labels.serverPort.secureNativePort : labels.serverPort.secureHttpPort) : + (jsonData.protocol === Protocol.Native ? labels.serverPort.insecureNativePort : labels.serverPort.insecureHttpPort); + const portDescription = `${labels.serverPort.tooltip} (default for ${jsonData.secure ? 'secure' : ''} ${jsonData.protocol}: ${defaultPort})` + return ( <> = (props) => { onPortChange(e.currentTarget.value)} - label={labels.ServerPort.label} - aria-label={labels.ServerPort.label} - placeholder={labels.ServerPort.placeholder(jsonData.secure?.toString() || 'false')} + onChange={e => onPortChange(e.currentTarget.value)} + label={labels.serverPort.label} + aria-label={labels.serverPort.label} + placeholder={defaultPort} /> - - - - + + options={protocolOptions} disabledOptions={[]} @@ -227,7 +225,7 @@ export const ConfigEditor: React.FC = (props) => { onChange={(e) => onProtocolToggle(e!)} /> - + = (props) => { onChange={(e) => onSwitchToggle('secure', e.currentTarget.checked)} /> + + { jsonData.protocol === Protocol.Http && + + + + } + { jsonData.protocol === Protocol.Http && + onHttpHeadersChange(headers, options, onOptionsChange)} + /> + } + = (props) => { /> = (props) => { /> = (props) => { onCertificateChangeFactory('tlsCACert', e.currentTarget.value)} - placeholder={labels.TLSCACert.placeholder} - label={labels.TLSCACert.label} + placeholder={labels.tlsCACert.placeholder} + label={labels.tlsCACert.label} onClick={() => onResetClickFactory('tlsCACert')} /> )} @@ -283,14 +303,14 @@ export const ConfigEditor: React.FC = (props) => { onCertificateChangeFactory('tlsClientCert', e.currentTarget.value)} - placeholder={labels.TLSClientCert.placeholder} - label={labels.TLSClientCert.label} + placeholder={labels.tlsClientCert.placeholder} + label={labels.tlsClientCert.label} onClick={() => onResetClickFactory('tlsClientCert')} /> onCertificateChangeFactory('tlsClientKey', e.currentTarget.value)} onClick={() => onResetClickFactory('tlsClientKey')} /> @@ -301,26 +321,26 @@ export const ConfigEditor: React.FC = (props) => { - + = (props) => { {config.featureToggles['secureSocksDSProxyEnabled'] && versionGte(config.buildInfo.version, '10.0.0') && ( { it('should not call onOptionsChange if no v3 fields are present', async () => { @@ -31,6 +32,7 @@ describe('useMigrateV3Config', () => { const expectedOptions = { jsonData: { + version: pluginVersion, host: 'address', dialTimeout: '8' } @@ -54,6 +56,7 @@ describe('useMigrateV3Config', () => { const expectedOptions = { jsonData: { + version: pluginVersion, host: 'new', dialTimeout: '8' } @@ -62,3 +65,158 @@ describe('useMigrateV3Config', () => { expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); }); }); + +describe('onHttpHeadersChange', () => { + it('should properly sort headers into secure/plain config fields', async () => { + const onOptionsChange = jest.fn(); + const headers: CHHttpHeader[] = [ + { + name: 'X-Existing-Auth-Header', + value: '', + secure: true + }, + { + name: 'X-Existing-Header', + value: 'existing value', + secure: false + }, + { + name: 'Authorization', + value: 'secret1234', + secure: true + }, + { + name: 'X-Custom-Header', + value: 'plain text value', + secure: false + }, + ]; + const opts = { + jsonData: { + httpHeaders: [ + { + name: 'X-Existing-Auth-Header', + value: '', + secure: true + }, + { + name: 'X-Existing-Header', + value: 'existing value', + secure: false + }, + ] + }, + secureJsonFields: { + 'secureHttpHeaders.X-Existing-Auth-Header': true + }, + } as any as DataSourceSettings; + + onHttpHeadersChange(headers, opts, onOptionsChange); + + const expectedOptions = { + jsonData: { + httpHeaders: [ + { + name: 'X-Existing-Auth-Header', + value: '', + secure: true + }, + { + name: 'X-Existing-Header', + value: 'existing value', + secure: false + }, + { + name: 'Authorization', + value: '', + secure: true + }, + { + name: 'X-Custom-Header', + value: 'plain text value', + secure: false + }, + ] + }, + secureJsonFields: { + 'secureHttpHeaders.X-Existing-Auth-Header': true, + 'secureHttpHeaders.Authorization': true + }, + secureJsonData: { + 'secureHttpHeaders.Authorization': 'secret1234' + } + }; + expect(onOptionsChange).toHaveBeenCalledTimes(1); + expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); + }); +}); + +describe('useConfigDefaults', () => { + it('should add plugin version', async () => { + const onOptionsChange = jest.fn(); + const options = { + jsonData: { + } + } as any as DataSourceSettings; + + renderHook(opts => useConfigDefaults(opts, onOptionsChange), { initialProps: options }); + + const expectedOptions = { + jsonData: { + version: pluginVersion, + } + }; + expect(onOptionsChange).toHaveBeenCalledTimes(1); + expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); + }); + + it('should overwrite plugin version', async () => { + const onOptionsChange = jest.fn(); + const options = { + jsonData: { + version: '3.0.0', + } + } as any as DataSourceSettings; + + renderHook(opts => useConfigDefaults(opts, onOptionsChange), { initialProps: options }); + + const expectedOptions = { + jsonData: { + version: pluginVersion, + } + }; + expect(onOptionsChange).toHaveBeenCalledTimes(1); + expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); + }); + + // TODO: There's no defaults being set yet. Right now it only adds the plugin version + // it('should apply defaults for unset config fields', async () => { + // const onOptionsChange = jest.fn(); + // const options = { + // jsonData: { + // } + // } as any as DataSourceSettings; + + // renderHook(opts => useConfigDefaults(opts, onOptionsChange), { initialProps: options }); + + // const expectedOptions = { + // jsonData: { + // } + // }; + // expect(onOptionsChange).toHaveBeenCalledTimes(1); + // expect(onOptionsChange).toHaveBeenCalledWith(expect.objectContaining(expectedOptions)); + // }); + + it('should not call onOptionsChange after defaults are already set', async () => { + const onOptionsChange = jest.fn(); + const options = { + jsonData: { + } + } as any as DataSourceSettings; + + const hook = renderHook(opts => useConfigDefaults(opts, onOptionsChange), { initialProps: options }); + hook.rerender(); + + expect(onOptionsChange).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/views/CHConfigEditorHooks.ts b/src/views/CHConfigEditorHooks.ts index eea4908c..9234e4db 100644 --- a/src/views/CHConfigEditorHooks.ts +++ b/src/views/CHConfigEditorHooks.ts @@ -1,37 +1,137 @@ -import { DataSourceSettings } from "@grafana/data"; +import { DataSourceSettings, KeyValue } from "@grafana/data"; import { useEffect, useRef } from "react"; -import { CHConfig } from "types/config"; +import { CHConfig, CHHttpHeader, CHSecureConfig } from "types/config"; +// import { getLatestVersion } from "otel"; +import { pluginVersion } from "utils/version"; /** * Migrates v3 config to latest config schema. * Copies and removes old "server" to "host" field * Copies and removes old "timeout" to "dialTimeout" field */ -export const useMigrateV3Config = (options: DataSourceSettings, onOptionsChange: (opts: DataSourceSettings) => void) => { +export const useMigrateV3Config = (options: DataSourceSettings, onOptionsChange: (opts: DataSourceSettings) => void) => { const { jsonData } = options; const v3ServerField = (jsonData as any)['server']; const v3TimeoutField = (jsonData as any)['timeout']; const migrated = useRef(!v3ServerField && !v3TimeoutField); useEffect(() => { - if (!migrated.current) { - const nextJsonOptions = { ...options.jsonData }; - - if (v3ServerField && !nextJsonOptions.host) { - nextJsonOptions.host = v3ServerField; - } - delete (nextJsonOptions as any)['server']; + if (migrated.current) { + return; + } + const nextJsonOptions = { ...options.jsonData }; + nextJsonOptions.version = pluginVersion; - if (v3TimeoutField && !nextJsonOptions.dialTimeout) { - nextJsonOptions.dialTimeout = v3TimeoutField; - } - delete (nextJsonOptions as any)['timeout']; + if (v3ServerField && !nextJsonOptions.host) { + nextJsonOptions.host = v3ServerField; + } + delete (nextJsonOptions as any)['server']; - onOptionsChange({ - ...options, - jsonData: nextJsonOptions, - }); - migrated.current = true; + if (v3TimeoutField && !nextJsonOptions.dialTimeout) { + nextJsonOptions.dialTimeout = v3TimeoutField; } + delete (nextJsonOptions as any)['timeout']; + + onOptionsChange({ + ...options, + jsonData: nextJsonOptions, + }); + migrated.current = true; }, [v3ServerField, v3TimeoutField, options, onOptionsChange]); }; + +/** + * Handles saving HTTP headers to Grafana config. + * + * All header keys go to the unsecure config. + * If the header is marked as secure, its value goes to the + * secure json config where it is hidden. + */ +export const onHttpHeadersChange = (headers: CHHttpHeader[], options: DataSourceSettings, onOptionsChange: (opts: DataSourceSettings) => void) => { + const httpHeaders: CHHttpHeader[] = []; + const secureHttpHeaderKeys: KeyValue = {}; + const secureHttpHeaderValues: KeyValue = {}; + + for (let header of headers) { + if (!header.name) { + continue; + } + + if (header.secure) { + const key = `secureHttpHeaders.${header.name}`; + secureHttpHeaderKeys[key] = true; + + if (header.value) { + secureHttpHeaderValues[key] = header.value; + header.value = ''; + } + } + + httpHeaders.push(header); + } + + const currentSecureJsonFields: KeyValue = { ...options.secureJsonFields }; + for (let key in currentSecureJsonFields) { + if (!secureHttpHeaderKeys[key] && key.startsWith('secureHttpHeaders.')) { + // Remove key from secureJsonData when it is no longer present in header config + secureHttpHeaderKeys[key] = false; + secureHttpHeaderValues[key] = ''; + } + } + + onOptionsChange({ + ...options, + jsonData: { + ...options.jsonData, + httpHeaders + }, + secureJsonFields: { + ...options.secureJsonFields, + ...secureHttpHeaderKeys + }, + secureJsonData: { + ...options.secureJsonData, + ...secureHttpHeaderValues + }, + }); +} + +/** + * Applies default settings to config options. + */ +export const useConfigDefaults = (options: DataSourceSettings, onOptionsChange: (opts: DataSourceSettings) => void) => { + const appliedDefaults = useRef(false); + useEffect(() => { + if (appliedDefaults.current) { + return; + } + + const jsonData = { ...options.jsonData }; + jsonData.version = pluginVersion; // Always overwrite version + // const latestOtelVersion = getLatestVersion(); + + // TODO: Should OTel be enabled by default for new datasources? + + if (!jsonData.logs) { + // jsonData.logs = { + // defaultTable: latestOtelVersion.logsTable, + // otelEnabled: true, + // otelVersion: latestOtelVersion.version + // }; + } + + if (!jsonData.traces) { + // jsonData.traces = { + // defaultTable: latestOtelVersion.traceTable, + // otelEnabled: true, + // otelVersion: latestOtelVersion.version + // }; + } + + onOptionsChange({ + ...options, + jsonData, + }); + appliedDefaults.current = true; + }, [options, onOptionsChange]); +} diff --git a/src/views/CHQueryEditor.tsx b/src/views/CHQueryEditor.tsx index e811adfa..ff612afe 100644 --- a/src/views/CHQueryEditor.tsx +++ b/src/views/CHQueryEditor.tsx @@ -9,7 +9,7 @@ import { CHConfig } from 'types/config'; import { QueryBuilder } from 'components/queryBuilder/QueryBuilder'; import { generateSql } from 'data/sqlGenerator'; import { SqlEditor } from 'components/SqlEditor'; -import { isBuilderOptionsRunnable, mapQueryTypeToGrafanaFormat } from 'data/utils'; +import { isBuilderOptionsRunnable, mapQueryBuilderOptionsToGrafanaFormat } from 'data/utils'; import { setAllOptions, useBuilderOptionsState } from 'hooks/useBuilderOptionsState'; import { pluginVersion } from 'utils/version'; import { migrateCHQuery } from 'data/migration'; @@ -26,7 +26,7 @@ export const CHQueryEditor = (props: CHQueryEditorProps) => { return ( <>
- +
@@ -68,7 +68,7 @@ const CHEditorByType = (props: CHQueryEditorProps) => { editorType: EditorType.Builder, rawSql: sql, builderOptions, - format: mapQueryTypeToGrafanaFormat(builderOptions.queryType) + format: mapQueryBuilderOptionsToGrafanaFormat(builderOptions) }); // TODO: fix dependency warning with "useEffectEvent" once added to stable version of react