From 78e6fc177c4df738672576cb495e38c832d2cbcc Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 12 Dec 2024 15:21:25 +0000 Subject: [PATCH 01/48] feat: allow external auth server endpoint ID passing method to be configured --- envoy/auth_server/.env.example | 5 + envoy/auth_server/auth/auth_handler.go | 30 ++--- envoy/auth_server/auth/auth_handler_test.go | 84 +++++++++++--- .../auth_server/auth/endpoint_id_extractor.go | 82 ++++++++++++++ .../auth/endpoint_id_extractor_test.go | 107 ++++++++++++++++++ envoy/auth_server/main.go | 28 ++++- local/kubernetes/envoy-ext-authz.yaml | 4 + 7 files changed, 298 insertions(+), 42 deletions(-) create mode 100644 envoy/auth_server/auth/endpoint_id_extractor.go create mode 100644 envoy/auth_server/auth/endpoint_id_extractor_test.go diff --git a/envoy/auth_server/.env.example b/envoy/auth_server/.env.example index fe742dfa..a8ba94c3 100644 --- a/envoy/auth_server/.env.example +++ b/envoy/auth_server/.env.example @@ -8,3 +8,8 @@ GRPC_HOST_PORT=path_auth_data_server:50051 # Default is "false" if not set. # GRPC_USE_INSECURE=true is required to run ext_authz locally in Docker Compose GRPC_USE_INSECURE=true + +# OPTIONAL: The type of extractor to use for the endpoint ID. +# Options are "url_path" or "header". +# Default is "url_path" if not set. +ENDPOINT_ID_EXTRACTOR=url_path diff --git a/envoy/auth_server/auth/auth_handler.go b/envoy/auth_server/auth/auth_handler.go index d0aec0ee..892a2bd8 100644 --- a/envoy/auth_server/auth/auth_handler.go +++ b/envoy/auth_server/auth/auth_handler.go @@ -7,7 +7,6 @@ package auth import ( "context" "fmt" - "strings" envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" @@ -20,8 +19,7 @@ import ( ) const ( - // TODO_MVP(@commoddity): Eliminate the hard-coding of this prefix for all endpoints. - // See this thread for more information: https://github.com/buildwithgrove/path/pull/52/files#r1859632536 + // TODO_MVP(@commoddity): pathPrefix = "/v1/" reqHeaderEndpointID = "x-endpoint-id" // Set on all service requests @@ -51,6 +49,9 @@ type AuthHandler struct { APIKeyAuthorizer Authorizer JWTAuthorizer Authorizer + // The endpoint ID extractor to be used for the request + EndpointIDExtractor EndpointIDExtractor + Logger polylog.Logger } @@ -65,8 +66,6 @@ func (a *AuthHandler) Check( ctx context.Context, checkReq *envoy_auth.CheckRequest, ) (*envoy_auth.CheckResponse, error) { - a.Logger.Info().Str("path", checkReq.GetAttributes().GetRequest().GetHttp().GetPath()).Msg("path") - // Get the HTTP request req := checkReq.GetAttributes().GetRequest().GetHttp() if req == nil { @@ -85,12 +84,15 @@ func (a *AuthHandler) Check( return getDeniedCheckResponse("headers not found", envoy_type.StatusCode_BadRequest), nil } - // Extract the endpoint ID from the path - endpointID, err := extractEndpointID(path) + // Extract the endpoint ID from the request + // It may be extracted from the URL path or the headers + endpointID, err := a.EndpointIDExtractor.extractGatewayEndpointID(req) if err != nil { - return getDeniedCheckResponse(err.Error(), envoy_type.StatusCode_Forbidden), nil + return getDeniedCheckResponse(err.Error(), envoy_type.StatusCode_BadRequest), nil } + a.Logger.Info().Str("path", path).Str("endpoint_id", endpointID).Msg("handling check request") + // Fetch GatewayEndpoint from endpoint store gatewayEndpoint, ok := a.getGatewayEndpoint(endpointID) if !ok { @@ -112,18 +114,6 @@ func (a *AuthHandler) Check( /* --------------------------------- Helpers -------------------------------- */ -// extractEndpointID extracts the endpoint ID from the URL path. -// The endpoint ID is the part of the path after "/v1/" and is used to identify the GatewayEndpoint. -func extractEndpointID(urlPath string) (string, error) { - if strings.HasPrefix(urlPath, pathPrefix) { - if endpointID := strings.TrimPrefix(urlPath, pathPrefix); endpointID != "" { - return endpointID, nil - } - return "", fmt.Errorf("endpoint ID not provided") - } - return "", fmt.Errorf("invalid path: %s", urlPath) -} - // getGatewayEndpoint fetches the GatewayEndpoint from the endpoint store and a bool indicating if it was found func (a *AuthHandler) getGatewayEndpoint(endpointID string) (*proto.GatewayEndpoint, bool) { return a.EndpointStore.GetGatewayEndpoint(endpointID) diff --git a/envoy/auth_server/auth/auth_handler_test.go b/envoy/auth_server/auth/auth_handler_test.go index 3ee0b205..fe2532a4 100644 --- a/envoy/auth_server/auth/auth_handler_test.go +++ b/envoy/auth_server/auth/auth_handler_test.go @@ -19,11 +19,12 @@ import ( func Test_Check(t *testing.T) { tests := []struct { - name string - checkReq *envoy_auth.CheckRequest - expectedResp *envoy_auth.CheckResponse - endpointID string - mockEndpointReturn *proto.GatewayEndpoint + name string + checkReq *envoy_auth.CheckRequest + expectedResp *envoy_auth.CheckResponse + endpointID string + endpointIDExtractor EndpointIDExtractor + mockEndpointReturn *proto.GatewayEndpoint }{ { name: "should return OK check response if check request is valid and user is authorized to access endpoint with rate limit headers set", @@ -54,7 +55,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "endpoint_free", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "endpoint_free", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "endpoint_free", Auth: &proto.Auth{ @@ -102,7 +104,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "endpoint_unlimited", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "endpoint_unlimited", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "endpoint_unlimited", Auth: &proto.Auth{ @@ -147,7 +150,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "api_key_endpoint", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "api_key_endpoint", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "api_key_endpoint", Auth: &proto.Auth{ @@ -186,7 +190,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "jwt_endpoint", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "jwt_endpoint", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "jwt_endpoint", Auth: &proto.Auth{ @@ -227,7 +232,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "public_endpoint", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "public_endpoint", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "public_endpoint", Auth: &proto.Auth{ @@ -237,6 +243,44 @@ func Test_Check(t *testing.T) { }, }, }, + { + name: "should return ok check response if endpoint ID is passed via header", + checkReq: &envoy_auth.CheckRequest{ + Attributes: &envoy_auth.AttributeContext{ + Request: &envoy_auth.AttributeContext_Request{ + Http: &envoy_auth.AttributeContext_HttpRequest{ + Path: "/v1", + Headers: map[string]string{ + reqHeaderEndpointID: "endpoint_id_from_header", + }, + }, + }, + }, + }, + expectedResp: &envoy_auth.CheckResponse{ + Status: &status.Status{ + Code: int32(codes.OK), + Message: "ok", + }, + HttpResponse: &envoy_auth.CheckResponse_OkResponse{ + OkResponse: &envoy_auth.OkHttpResponse{ + Headers: []*envoy_core.HeaderValueOption{ + {Header: &envoy_core.HeaderValue{Key: reqHeaderEndpointID, Value: "endpoint_id_from_header"}}, + }, + }, + }, + }, + endpointIDExtractor: &HeaderExtractor{}, + endpointID: "endpoint_id_from_header", + mockEndpointReturn: &proto.GatewayEndpoint{ + EndpointId: "endpoint_id_from_header", + Auth: &proto.Auth{ + AuthType: &proto.Auth_NoAuth{ + NoAuth: &proto.NoAuth{}, + }, + }, + }, + }, { name: "should return denied check response if gateway endpoint not found", checkReq: &envoy_auth.CheckRequest{ @@ -265,8 +309,9 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "endpoint_not_found", - mockEndpointReturn: &proto.GatewayEndpoint{}, + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "endpoint_not_found", + mockEndpointReturn: &proto.GatewayEndpoint{}, }, { name: "should return denied check response if user is not authorized to access endpoint using API key auth", @@ -296,7 +341,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "endpoint_api_key", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "endpoint_api_key", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "endpoint_api_key", Auth: &proto.Auth{ @@ -336,7 +382,8 @@ func Test_Check(t *testing.T) { }, }, }, - endpointID: "endpoint_jwt_auth", + endpointIDExtractor: &URLPathExtractor{}, + endpointID: "endpoint_jwt_auth", mockEndpointReturn: &proto.GatewayEndpoint{ EndpointId: "endpoint_jwt_auth", Auth: &proto.Auth{ @@ -365,10 +412,11 @@ func Test_Check(t *testing.T) { } authHandler := &AuthHandler{ - EndpointStore: mockStore, - APIKeyAuthorizer: &APIKeyAuthorizer{}, - JWTAuthorizer: &JWTAuthorizer{}, - Logger: polyzero.NewLogger(), + EndpointStore: mockStore, + APIKeyAuthorizer: &APIKeyAuthorizer{}, + JWTAuthorizer: &JWTAuthorizer{}, + EndpointIDExtractor: test.endpointIDExtractor, + Logger: polyzero.NewLogger(), } resp, err := authHandler.Check(context.Background(), test.checkReq) diff --git a/envoy/auth_server/auth/endpoint_id_extractor.go b/envoy/auth_server/auth/endpoint_id_extractor.go new file mode 100644 index 00000000..552cd1f8 --- /dev/null +++ b/envoy/auth_server/auth/endpoint_id_extractor.go @@ -0,0 +1,82 @@ +package auth + +import ( + "fmt" + "strings" + + envoy_auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" +) + +type EndpointIDExtractorType string + +const ( + EndpointIDExtractorTypeURLPath EndpointIDExtractorType = "url_path" + EndpointIDExtractorTypeHeader EndpointIDExtractorType = "header" +) + +func (e EndpointIDExtractorType) IsValid() bool { + switch e { + case EndpointIDExtractorTypeURLPath, EndpointIDExtractorTypeHeader: + return true + default: + return false + } +} + +// EndpointIDExtractor defines an interface for extracting an endpoint ID +// from a given source, which could be a URL path or an HTTP header. +type EndpointIDExtractor interface { + // Extract extracts the endpoint ID from the provided source. + // The sourceType parameter specifies whether the source is a "path" or "header". + extractGatewayEndpointID(req *envoy_auth.AttributeContext_HttpRequest) (string, error) +} + +// URLPathExtractor satisfies the EndpointIDExtractor interface. +var _ EndpointIDExtractor = &URLPathExtractor{} + +// URLPathExtractor is an implementation of the EndpointIDExtractor interface +// that extracts the endpoint ID from the URL path. +type URLPathExtractor struct{} + +// ExtractGatewayEndpointID extracts the endpoint ID from the URL path. +// The endpoint ID is expected to be the first segment of the path after the pathPrefix (/v1/) +// +// eg. http://eth.path.grove.city/v1/1a2b3c4d -> endpointID = "1a2b3c4d" +func (p *URLPathExtractor) extractGatewayEndpointID(req *envoy_auth.AttributeContext_HttpRequest) (string, error) { + path := req.GetPath() + + if strings.HasPrefix(path, pathPrefix) { + segments := strings.Split(strings.TrimPrefix(path, pathPrefix), "/") + if len(segments) > 0 && segments[0] != "" { + return segments[0], nil + } + return "", fmt.Errorf("endpoint ID not provided") + } + + return "", fmt.Errorf("invalid path: %s", path) +} + +// HeaderExtractor satisfies the EndpointIDExtractor interface. +var _ EndpointIDExtractor = &HeaderExtractor{} + +// HeaderExtractor is an implementation of the EndpointIDExtractor interface +// that extracts the endpoint ID from the HTTP headers. +type HeaderExtractor struct{} + +// ExtractGatewayEndpointID extracts the endpoint ID from the HTTP headers. +// The endpoint ID is expected to be in the "x-endpoint-id" header. +// +// eg. Header = "x-endpoint-id: 1a2b3c4d" -> endpointID = "1a2b3c4d" +func (h *HeaderExtractor) extractGatewayEndpointID(req *envoy_auth.AttributeContext_HttpRequest) (string, error) { + headers := req.GetHeaders() + + endpointID, ok := headers[reqHeaderEndpointID] + if !ok { + return "", fmt.Errorf("endpoint ID header not found") + } + if endpointID == "" { + return "", fmt.Errorf("endpoint ID not provided") + } + + return endpointID, nil +} diff --git a/envoy/auth_server/auth/endpoint_id_extractor_test.go b/envoy/auth_server/auth/endpoint_id_extractor_test.go new file mode 100644 index 00000000..35a96ac5 --- /dev/null +++ b/envoy/auth_server/auth/endpoint_id_extractor_test.go @@ -0,0 +1,107 @@ +package auth + +import ( + "testing" + + envoy_auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" +) + +func Test_URLPathExtractor(t *testing.T) { + tests := []struct { + name string + request *envoy_auth.AttributeContext_HttpRequest + want string + wantErr bool + }{ + { + name: "should extract endpoint ID from valid path", + request: &envoy_auth.AttributeContext_HttpRequest{ + Path: "/v1/1a2b3c4d", + }, + want: "1a2b3c4d", + wantErr: false, + }, + { + name: "should return error for path without endpoint ID", + request: &envoy_auth.AttributeContext_HttpRequest{ + Path: "/v1/", + }, + want: "", + wantErr: true, + }, + { + name: "should return error for invalid path", + request: &envoy_auth.AttributeContext_HttpRequest{ + Path: "/invalid/1a2b3c4d", + }, + want: "", + wantErr: true, + }, + } + + extractor := &URLPathExtractor{} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := extractor.extractGatewayEndpointID(test.request) + if (err != nil) != test.wantErr { + t.Errorf("extractGatewayEndpointID() error = %v, wantErr %v", err, test.wantErr) + return + } + if got != test.want { + t.Errorf("extractGatewayEndpointID() = %v, want %v", got, test.want) + } + }) + } +} + +func Test_HeaderExtractor(t *testing.T) { + tests := []struct { + name string + request *envoy_auth.AttributeContext_HttpRequest + want string + wantErr bool + }{ + { + name: "should extract endpoint ID from header", + request: &envoy_auth.AttributeContext_HttpRequest{ + Headers: map[string]string{ + "x-endpoint-id": "1a2b3c4d", + }, + }, + want: "1a2b3c4d", + wantErr: false, + }, + { + name: "should return error if header is missing", + request: &envoy_auth.AttributeContext_HttpRequest{ + Headers: map[string]string{}, + }, + want: "", + wantErr: true, + }, + { + name: "should return error if header is empty", + request: &envoy_auth.AttributeContext_HttpRequest{ + Headers: map[string]string{ + "x-endpoint-id": "", + }, + }, + want: "", + wantErr: true, + }, + } + + extractor := &HeaderExtractor{} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := extractor.extractGatewayEndpointID(test.request) + if (err != nil) != test.wantErr { + t.Errorf("extractGatewayEndpointID() error = %v, wantErr %v", err, test.wantErr) + return + } + if got != test.want { + t.Errorf("extractGatewayEndpointID() = %v, want %v", got, test.want) + } + }) + } +} diff --git a/envoy/auth_server/main.go b/envoy/auth_server/main.go index cbedc737..4f44bc4b 100644 --- a/envoy/auth_server/main.go +++ b/envoy/auth_server/main.go @@ -29,11 +29,14 @@ const ( envVarGRPCHostPort = "GRPC_HOST_PORT" envVarGRPCUseInsecure = "GRPC_USE_INSECURE" defaultGRPCUseInsecureCredentials = false + envVarEndpointIDExtractor = "ENDPOINT_ID_EXTRACTOR" + defaultEndpointIDExtractor = auth.EndpointIDExtractorTypeURLPath ) type options struct { grpcHostPort string grpcUseInsecureCredentials bool + endpointIDExtractor auth.EndpointIDExtractorType } func gatherOptions() options { @@ -49,9 +52,15 @@ func gatherOptions() options { } } + endpointIDExtractor := auth.EndpointIDExtractorType(os.Getenv(envVarEndpointIDExtractor)) + if !endpointIDExtractor.IsValid() { + endpointIDExtractor = defaultEndpointIDExtractor + } + return options{ grpcHostPort: grpcHostPort, grpcUseInsecureCredentials: grpcUseInsecureCredentials, + endpointIDExtractor: endpointIDExtractor, } } @@ -97,12 +106,23 @@ func main() { panic(err) } + // Determine which gateway endpoint ID extractor to use + // If the extractor is not set, use the default "url_path" extractor + var endpointIDExtractor auth.EndpointIDExtractor + switch opts.endpointIDExtractor { + case auth.EndpointIDExtractorTypeURLPath: + endpointIDExtractor = &auth.URLPathExtractor{} + case auth.EndpointIDExtractorTypeHeader: + endpointIDExtractor = &auth.HeaderExtractor{} + } + // Create a new AuthHandler to handle the request auth authHandler := &auth.AuthHandler{ - EndpointStore: endpointStore, - APIKeyAuthorizer: &auth.APIKeyAuthorizer{}, - JWTAuthorizer: &auth.JWTAuthorizer{}, - Logger: logger, + EndpointStore: endpointStore, + APIKeyAuthorizer: &auth.APIKeyAuthorizer{}, + JWTAuthorizer: &auth.JWTAuthorizer{}, + EndpointIDExtractor: endpointIDExtractor, + Logger: logger, } // Create a new gRPC server for handling auth requests from Envoy diff --git a/local/kubernetes/envoy-ext-authz.yaml b/local/kubernetes/envoy-ext-authz.yaml index a1565925..cf0ea716 100644 --- a/local/kubernetes/envoy-ext-authz.yaml +++ b/local/kubernetes/envoy-ext-authz.yaml @@ -25,6 +25,10 @@ spec: # Default is "false" if not set. - name: GRPC_USE_INSECURE value: "true" # Remove this value if using a TLS-enabled connection. + # OPTIONAL: The type of extractor to use for the endpoint ID. + # Default is "url_path" if not set. + - name: ENDPOINT_ID_EXTRACTOR + value: "url_path" ports: - containerPort: 10003 --- From 2f163b0e561cd7921111b88db0ec96d85d1a2514 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 12 Dec 2024 16:14:24 +0000 Subject: [PATCH 02/48] fix: configure envoy template to allow /v1 and /v1/ --- envoy/auth_server/auth/auth_handler.go | 2 +- .../auth_server/auth/endpoint_id_extractor.go | 3 +- envoy/auth_server/main.go | 5 +++ envoy/envoy.template.yaml | 5 ++- local/kubernetes/envoy-ext-authz.yaml | 1 + local/path/envoy.yaml | 33 +++++++++++++++++++ 6 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 local/path/envoy.yaml diff --git a/envoy/auth_server/auth/auth_handler.go b/envoy/auth_server/auth/auth_handler.go index 892a2bd8..2588dc74 100644 --- a/envoy/auth_server/auth/auth_handler.go +++ b/envoy/auth_server/auth/auth_handler.go @@ -91,7 +91,7 @@ func (a *AuthHandler) Check( return getDeniedCheckResponse(err.Error(), envoy_type.StatusCode_BadRequest), nil } - a.Logger.Info().Str("path", path).Str("endpoint_id", endpointID).Msg("handling check request") + a.Logger.Info().Str("endpoint_id", endpointID).Msg("handling check request") // Fetch GatewayEndpoint from endpoint store gatewayEndpoint, ok := a.getGatewayEndpoint(endpointID) diff --git a/envoy/auth_server/auth/endpoint_id_extractor.go b/envoy/auth_server/auth/endpoint_id_extractor.go index 552cd1f8..5fe8e78e 100644 --- a/envoy/auth_server/auth/endpoint_id_extractor.go +++ b/envoy/auth_server/auth/endpoint_id_extractor.go @@ -50,10 +50,9 @@ func (p *URLPathExtractor) extractGatewayEndpointID(req *envoy_auth.AttributeCon if len(segments) > 0 && segments[0] != "" { return segments[0], nil } - return "", fmt.Errorf("endpoint ID not provided") } - return "", fmt.Errorf("invalid path: %s", path) + return "", fmt.Errorf("endpoint ID not provided") } // HeaderExtractor satisfies the EndpointIDExtractor interface. diff --git a/envoy/auth_server/main.go b/envoy/auth_server/main.go index 4f44bc4b..d87ab143 100644 --- a/envoy/auth_server/main.go +++ b/envoy/auth_server/main.go @@ -25,6 +25,7 @@ import ( // TODO_CONSIDER(@commoddity): Make this configurable. See thread here: https://github.com/buildwithgrove/path/pull/52/files/1a3e7a11f159f5b8d3c414f2417f7879bcfab410..258136504608c1269a27047bb9bded1ab4fefcc8#r1859409934 const port = 10003 +// TODO_TECHDEBT(@commoddity): Make these values part of the config YAML and remove the dependency on environment variables. const ( envVarGRPCHostPort = "GRPC_HOST_PORT" envVarGRPCUseInsecure = "GRPC_USE_INSECURE" @@ -53,7 +54,11 @@ func gatherOptions() options { } endpointIDExtractor := auth.EndpointIDExtractorType(os.Getenv(envVarEndpointIDExtractor)) + if endpointIDExtractor == "" { + endpointIDExtractor = defaultEndpointIDExtractor + } if !endpointIDExtractor.IsValid() { + fmt.Printf("invalid endpoint ID extractor type: %s, using default: %s\n", endpointIDExtractor, defaultEndpointIDExtractor) endpointIDExtractor = defaultEndpointIDExtractor } diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index ea30a089..05d3f618 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -178,9 +178,12 @@ static_resources: disabled: true # Main API route for /v1 with path modifications. - match: - prefix: "/v1" + safe_regex: + regex: "^/v1(/.*)?$" route: cluster: path_gateway_service + # Ensure the request is forwarded to PATH with a trailing slash. + prefix_rewrite: "/v1/" # Configures rate limiting descriptors. rate_limits: - actions: diff --git a/local/kubernetes/envoy-ext-authz.yaml b/local/kubernetes/envoy-ext-authz.yaml index cf0ea716..7ac7907a 100644 --- a/local/kubernetes/envoy-ext-authz.yaml +++ b/local/kubernetes/envoy-ext-authz.yaml @@ -26,6 +26,7 @@ spec: - name: GRPC_USE_INSECURE value: "true" # Remove this value if using a TLS-enabled connection. # OPTIONAL: The type of extractor to use for the endpoint ID. + # Options are "url_path" or "header". # Default is "url_path" if not set. - name: ENDPOINT_ID_EXTRACTOR value: "url_path" diff --git a/local/path/envoy.yaml b/local/path/envoy.yaml new file mode 100644 index 00000000..91159e05 --- /dev/null +++ b/local/path/envoy.yaml @@ -0,0 +1,33 @@ + # Routing configuration for incoming requests. + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] # Matches all domains. + routes: + # Route for /healthz + - match: + path: "/healthz" + route: + cluster: path_gateway_service + typed_per_filter_config: + envoy.filters.http.ext_authz: + "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute + disabled: true + # Main API route for /v1 with path modifications. + - match: + safe_regex: + regex: "^/v1/?$" + route: + cluster: path_gateway_service + prefix_rewrite: "/v1/" + # Configures rate limiting descriptors. + rate_limits: + - actions: + # Sends descriptors for rate limiting based on custom headers. + - request_headers: + header_name: "x-rl-endpoint-id" + descriptor_key: "x-rl-endpoint-id" + - request_headers: + header_name: "x-rl-throughput" + descriptor_key: "x-rl-throughput" \ No newline at end of file From 72ec29132b431ae6659cc6594711f16dab12d077 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 12 Dec 2024 16:25:17 +0000 Subject: [PATCH 03/48] chore: update TODO comment in auth handler --- envoy/auth_server/auth/auth_handler.go | 3 ++- envoy/auth_server/main.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/envoy/auth_server/auth/auth_handler.go b/envoy/auth_server/auth/auth_handler.go index 2588dc74..bc95368e 100644 --- a/envoy/auth_server/auth/auth_handler.go +++ b/envoy/auth_server/auth/auth_handler.go @@ -19,7 +19,8 @@ import ( ) const ( - // TODO_MVP(@commoddity): + // TODO_TECHDEBT(@commoddity): This path segment should be configurable via a single source of truth. + // Not sure the best way to do this as it is referred to in multiple disparate places (eg. envoy.yaml, PATH's router.go & here) pathPrefix = "/v1/" reqHeaderEndpointID = "x-endpoint-id" // Set on all service requests diff --git a/envoy/auth_server/main.go b/envoy/auth_server/main.go index d87ab143..953cf62e 100644 --- a/envoy/auth_server/main.go +++ b/envoy/auth_server/main.go @@ -25,7 +25,7 @@ import ( // TODO_CONSIDER(@commoddity): Make this configurable. See thread here: https://github.com/buildwithgrove/path/pull/52/files/1a3e7a11f159f5b8d3c414f2417f7879bcfab410..258136504608c1269a27047bb9bded1ab4fefcc8#r1859409934 const port = 10003 -// TODO_TECHDEBT(@commoddity): Make these values part of the config YAML and remove the dependency on environment variables. +// TODO_TECHDEBT(@commoddity): Make these values part of PATH's config YAML and remove the dependency on environment variables. const ( envVarGRPCHostPort = "GRPC_HOST_PORT" envVarGRPCUseInsecure = "GRPC_USE_INSECURE" From 0b4f183b985cb19f6562523a4c123e35dd42d00d Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Fri, 13 Dec 2024 13:03:05 +0000 Subject: [PATCH 04/48] chore: remove envoy.yaml example file --- local/path/envoy.yaml | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 local/path/envoy.yaml diff --git a/local/path/envoy.yaml b/local/path/envoy.yaml deleted file mode 100644 index 91159e05..00000000 --- a/local/path/envoy.yaml +++ /dev/null @@ -1,33 +0,0 @@ - # Routing configuration for incoming requests. - route_config: - name: local_route - virtual_hosts: - - name: local_service - domains: ["*"] # Matches all domains. - routes: - # Route for /healthz - - match: - path: "/healthz" - route: - cluster: path_gateway_service - typed_per_filter_config: - envoy.filters.http.ext_authz: - "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute - disabled: true - # Main API route for /v1 with path modifications. - - match: - safe_regex: - regex: "^/v1/?$" - route: - cluster: path_gateway_service - prefix_rewrite: "/v1/" - # Configures rate limiting descriptors. - rate_limits: - - actions: - # Sends descriptors for rate limiting based on custom headers. - - request_headers: - header_name: "x-rl-endpoint-id" - descriptor_key: "x-rl-endpoint-id" - - request_headers: - header_name: "x-rl-throughput" - descriptor_key: "x-rl-throughput" \ No newline at end of file From 71536be717a1c6a6ba58a9b12aafaa3d8239e861 Mon Sep 17 00:00:00 2001 From: commoddity <47662958+commoddity@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:11:22 +0000 Subject: [PATCH 05/48] Apply suggestions from code review Co-authored-by: Daniel Olshansky --- envoy/auth_server/auth/endpoint_id_extractor.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/envoy/auth_server/auth/endpoint_id_extractor.go b/envoy/auth_server/auth/endpoint_id_extractor.go index 5fe8e78e..9400235a 100644 --- a/envoy/auth_server/auth/endpoint_id_extractor.go +++ b/envoy/auth_server/auth/endpoint_id_extractor.go @@ -14,6 +14,7 @@ const ( EndpointIDExtractorTypeHeader EndpointIDExtractorType = "header" ) +// IsValid ensure the endpoint ID extractor type is supported func (e EndpointIDExtractorType) IsValid() bool { switch e { case EndpointIDExtractorTypeURLPath, EndpointIDExtractorTypeHeader: @@ -23,8 +24,8 @@ func (e EndpointIDExtractorType) IsValid() bool { } } -// EndpointIDExtractor defines an interface for extracting an endpoint ID -// from a given source, which could be a URL path or an HTTP header. +// EndpointIDExtractor defines an interface for extracting an endpoint ID from a given source. +// This could be a URL path, HTTP header, etc... type EndpointIDExtractor interface { // Extract extracts the endpoint ID from the provided source. // The sourceType parameter specifies whether the source is a "path" or "header". @@ -38,7 +39,7 @@ var _ EndpointIDExtractor = &URLPathExtractor{} // that extracts the endpoint ID from the URL path. type URLPathExtractor struct{} -// ExtractGatewayEndpointID extracts the endpoint ID from the URL path. +// extractGatewayEndpointID extracts the endpoint ID from the URL path. // The endpoint ID is expected to be the first segment of the path after the pathPrefix (/v1/) // // eg. http://eth.path.grove.city/v1/1a2b3c4d -> endpointID = "1a2b3c4d" From e5a12fa65b9edf44c934eed2085d3eb669cc3168 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Fri, 13 Dec 2024 14:39:43 +0000 Subject: [PATCH 06/48] chore: review comments and update docs --- Makefile | 19 ++ docusaurus/docs/develop/envoy/introduction.md | 165 +++++++++++++----- envoy/auth_server/auth/auth_handler.go | 1 + .../auth_server/auth/endpoint_id_extractor.go | 13 +- envoy/auth_server/main.go | 20 ++- 5 files changed, 165 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index ac519316..d7c86493 100644 --- a/Makefile +++ b/Makefile @@ -247,6 +247,25 @@ go_docs: ## Start Go documentation server docusaurus_start: ## Start docusaurus server cd docusaurus && npm i && npm run start +####################### +#### Test Requests #### +####################### + +.PHONY: test_request_with_url_path +test_request_with_url_path: ## Test the auth_server URL path endpoint ID extractor (must have Envoy running) + curl http://anvil.localhost:3001/v1/endpoint_3 \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + +.PHONY: test_request_with_header +test_request_with_header: ## Test the auth_server header endpoint ID extractor (must have Envoy running) + curl http://anvil.localhost:3001/v1 \ + -X POST \ + -H "Content-Type: application/json" \ + -H "x-endpoint-id: endpoint_3" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + ############################### ### Makefile imports ### ############################### diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index c09d9b73..d3f7f298 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -14,16 +14,18 @@ title: Introduction - [1. Quickstart](#1-quickstart) - [2. Overview](#2-overview) - [2.1. Components](#21-components) - - [2.2 URL Format](#22-url-format) - [3. Envoy Proxy](#3-envoy-proxy) - [3.1. Contents](#31-contents) - [3.2. Envoy HTTP Filters](#32-envoy-http-filters) - [3.3. Request Lifecycle](#33-request-lifecycle) -- [4. Gateway Endpoint Authorization](#4-gateway-endpoint-authorization) - - [4.1 JSON Web Token (JWT) Authorization](#41-json-web-token-jwt-authorization) - - [4.2 API Key Authorization](#42-api-key-authorization) - - [4.3 No Authorization](#43-no-authorization) -- [5. External Authorization Server](#5-external-authorization-server) +- [4. Specifying the Gateway Endpoint ID](#4-specifying-the-gateway-endpoint-id) + - [4.1 URL Path](#41-url-path) + - [4.2 Header](#42-header) +- [5. Gateway Endpoint Authorization](#5-gateway-endpoint-authorization) + - [5.1 JSON Web Token (JWT) Authorization](#51-json-web-token-jwt-authorization) + - [5.2 API Key Authorization](#52-api-key-authorization) + - [5.3 No Authorization](#53-no-authorization) +- [6. External Authorization Server](#6-external-authorization-server) - [5.1. External Auth Service Sequence Diagram](#51-external-auth-service-sequence-diagram) - [5.2. External Auth Service Environment Variables](#52-external-auth-service-environment-variables) - [5.3. External Auth Service Getting Started](#53-external-auth-service-getting-started) @@ -66,7 +68,9 @@ Specifically, this is split into two logical parts: ### 2.1. Components -> 💡 **Tip:** A [Tiltfile](https://github.com/buildwithgrove/path/blob/main/Tiltfile) is provided to run all of these services locally. +:::tip +A [Tiltfile](https://github.com/buildwithgrove/path/blob/main/Tiltfile) is provided to run all of these services locally. +::: - **PATH Service**: The service that handles requests after they have been authorized. - **Envoy Proxy**: A proxy server that handles incoming requests, performs auth checks, and routes authorized requests to the `PATH` service. @@ -115,26 +119,7 @@ graph TD GRPCServer <-.-> DataSource ``` -### 2.2 URL Format -When auth is enabled, the required URL format for the PATH service is: - -``` -https://./v1/ -``` - -For example, if the `SERVICE_NAME` is `eth` and the `GATEWAY_ENDPOINT_ID` is `a1b2c3d4`: - -``` -https://eth.rpc.grove.city/v1/a1b2c3d4 -``` - -Requests are rejected if either of the following are true: - -- The `` is missing -- ID is not present in the `Go External Authorization Server`'s `Gateway Endpoint Store` - -
## 3. Envoy Proxy @@ -222,20 +207,105 @@ sequenceDiagram Service->>-Client: 12. Return Response ``` -## 4. Gateway Endpoint Authorization +## 4. Specifying the Gateway Endpoint ID + +The Auth Server may extract the Gateway Endpoint ID from the request in one of two ways: + +1. [URL Path](#221-url-path) +2. [Header](#222-header) + +This is determined by the `ENDPOINT_ID_EXTRACTOR` environment variable in the `auth_server/.env` file. + +Valid options are: + +- `url_path` +- `header` + +If the `ENDPOINT_ID_EXTRACTOR` environment variable is not set, the default `url_path` extractor is used. + +:::warning +Requests are rejected if either of the following are true: + +- The `` is missing +- ID is not present in the `Go External Authorization Server`'s `Gateway Endpoint Store` +::: + +:::info +Regardless of which extractor is used, the Gateway Endpoint ID will always be set in the `x-endpoint-id` header if the reuqest is forwarded to the PATH Service. +::: + +### 4.1 URL Path + +When using the `url_path` extractor, the Gateway Endpoint ID must be specified in the URL path. + +``` +https://./v1/ +``` + +For example, if the `SERVICE_NAME` is `eth` and the `GATEWAY_ENDPOINT_ID` is `a1b2c3d4`: + +``` +curl http://anvil.localhost:3001/v1/endpoint_3 \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' +``` + +### 4.2 Header + +When using the `header` extractor, the Gateway Endpoint ID must be specified in the `x-endpoint-id` header. + +``` +-H "x-endpoint-id: " +``` + +For example, if the `x-endpoint-id` header is set to `a1b2c3d4`: + +``` +curl http://anvil.localhost:3001/v1 \ + -X POST \ + -H "Content-Type: application/json" \ + -H "x-endpoint-id: endpoint_3" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' +``` + +:::tip +Make targets are provided to send test requests with both the `url_path` and `header` extractors. + +``` +make test_request_with_url_path +make test_request_with_header +``` +:::info +`endpoint_3` is the endpoint from the example `.gateway-endpoints.yaml` file that requires no authorization. + +See the [Gateway Endpoint YAML File](#522-gateway-endpoint-yaml-file) section for more information on the `GatewayEndpoint` data structure. +::: + +
+ +## 5. Gateway Endpoint Authorization The `Go External Authorization Server` evaluates whether incoming requests are authorized to access the PATH service based on the `AuthType` field of the `GatewayEndpoint` proto struct. Three authorization types are supported: -1. [JSON Web Token (JWT) Authorization](#41-json-web-token-jwt-authorization) -2. [API Key Authorization](#42-api-key-authorization) -3. [No Authorization](#43-no-authorization) +1. [JSON Web Token (JWT) Authorization](#51-json-web-token-jwt-authorization) +2. [API Key Authorization](#52-api-key-authorization) +3. [No Authorization](#53-no-authorization) -### 4.1 JSON Web Token (JWT) Authorization +### 5.1 JSON Web Token (JWT) Authorization For GatewayEndpoints with the `AuthType` field set to `JWT_AUTH`, a valid JWT issued by the auth provider specified in the `envoy.yaml` file is required to access the PATH service. +:::tip +Auth0 is an example of a JWT issuer that can be used with PATH. + +Their docs page on JWTs gives a good overview of the JWT format and how to issue JWTs to your users: + +- [Auth0 JWT Docs](https://auth0.com/docs/secure/tokens/json-web-tokens) +::: + _Example Request Header:_ ```bash @@ -252,9 +322,11 @@ _Example auth provider user ID header:_ x-jwt-user-id: auth0|a12b3c4d5e6f7g8h9 ``` -> 💡 For more information, see the [Envoy JWT Authn Docs](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter) +:::info +For more information, see the [Envoy JWT Authn Docs](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter) +::: -### 4.2 API Key Authorization +### 5.2 API Key Authorization For GatewayEndpoints with the `AuthType` field set to `API_KEY_AUTH`, a static API key is required to access the PATH service. @@ -266,15 +338,17 @@ _Example Request Header:_ The `Go External Authorization Server` will use the `authorization` header to make an authorization decision; if the `GatewayEndpoint`'s `Auth.ApiKey` field matches the `API_KEY` value, the request will be authorized. -### 4.3 No Authorization +### 5.3 No Authorization For GatewayEndpoints with the `AuthType` field set to `NO_AUTH`, no authorization is required to access the PATH service. All requests for GatewayEndpoints with the `AuthType` field set to `NO_AUTH` will be authorized by the `Go External Authorization Server`. -## 5. External Authorization Server +## 6. External Authorization Server -> 💡 See [PATH PADS Repository](https://github.com/buildwithgrove/path-auth-data-server) for more information on authorization service provided by Grove for PATH support. +:::info +See [PATH PADS Repository](https://github.com/buildwithgrove/path-auth-data-server) for more information on authorization service provided by Grove for PATH support. +::: The `envoy/auth_server` directory contains the `Go External Authorization Server` called by the Envoy `ext_authz` filter. It evaluates whether incoming requests are authorized to access the PATH service. @@ -339,7 +413,9 @@ service GatewayEndpoints { The `Remote gRPC Server` is responsible for providing the `Go External Authorization Server` with data on which endpoints are authorized to use the PATH service. -> ℹ️ **Note:** The implementation of the remote gRPC server is up to the Gateway operator but PADS is provided as a functional implementation for most users. +:::info +The implementation of the remote gRPC server is up to the Gateway operator but PADS is provided as a functional implementation for most users. +::: #### 5.2.1. PATH Auth Data Server @@ -393,7 +469,9 @@ endpoints: capacity_limit_period: "CAPACITY_LIMIT_PERIOD_MONTHLY" ``` -> 💡 **TIP:** The PADS repo also provides a [YAML schema for the `gateway-endpoints.yaml` file](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/gateway-endpoints.schema.yaml), which can be used to validate the configuration. +:::tip +The PADS repo also provides a [YAML schema for the `gateway-endpoints.yaml` file](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/gateway-endpoints.schema.yaml), which can be used to validate the configuration. +::: #### 5.2.3. Implementing a Custom Remote gRPC Server @@ -404,7 +482,9 @@ The custom implementation must use the methods defined in the `GatewayEndpoints` - `FetchAuthDataSync` - `StreamAuthDataUpdates` -> 💡 **TIP:** Forking the PADS repo is the easiest way to get started, though any gRPC server implementation that adheres to the `gateway_endpoint.proto` service definition should suffice. +:::tip +Forking the PADS repo is the easiest way to get started, though any gRPC server implementation that adheres to the `gateway_endpoint.proto` service definition should suffice. +::: ## 6. Rate Limiter @@ -443,9 +523,12 @@ The custom implementation must use the methods defined in the `GatewayEndpoints` requests_per_unit: 30 ``` - > 💡 **NOTE:** The default throughput limit is **30 requests per second** for GatewayEndpoints with the `PLAN_FREE` plan type based on the `x-rl-endpoint-id` and `x-rl-plan` descriptors. +:::info +The default throughput limit is **30 requests per second** for GatewayEndpoints with the `PLAN_FREE` plan type based on the `x-rl-endpoint-id` and `x-rl-plan` descriptors. + +_The rate limiting configuration may be configured to suit the needs of the Gateway Operator in the `ratelimit.yaml` file._ +::: - _The rate limiting configuration may be configured to suit the needs of the Gateway Operator in the `ratelimit.yaml` file._ ### 6.2. Documentation and Examples diff --git a/envoy/auth_server/auth/auth_handler.go b/envoy/auth_server/auth/auth_handler.go index bc95368e..9a7f9d90 100644 --- a/envoy/auth_server/auth/auth_handler.go +++ b/envoy/auth_server/auth/auth_handler.go @@ -89,6 +89,7 @@ func (a *AuthHandler) Check( // It may be extracted from the URL path or the headers endpointID, err := a.EndpointIDExtractor.extractGatewayEndpointID(req) if err != nil { + a.Logger.Info().Err(err).Msg("unable to extract endpoint ID from request") return getDeniedCheckResponse(err.Error(), envoy_type.StatusCode_BadRequest), nil } diff --git a/envoy/auth_server/auth/endpoint_id_extractor.go b/envoy/auth_server/auth/endpoint_id_extractor.go index 9400235a..bab92845 100644 --- a/envoy/auth_server/auth/endpoint_id_extractor.go +++ b/envoy/auth_server/auth/endpoint_id_extractor.go @@ -7,14 +7,20 @@ import ( envoy_auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" ) +// EndpointIDExtractorType specifies the type of endpoint ID extractor to use. type EndpointIDExtractorType string const ( + // EndpointIDExtractorTypeURLPath specifies that the endpoint ID is extracted from the URL path. + // Example: http://eth.path.grove.city/v1/1a2b3c4d -> endpointID = "1a2b3c4d" EndpointIDExtractorTypeURLPath EndpointIDExtractorType = "url_path" - EndpointIDExtractorTypeHeader EndpointIDExtractorType = "header" + + // EndpointIDExtractorTypeHeader specifies that the endpoint ID is extracted from the HTTP headers. + // Example: Header = "x-endpoint-id: 1a2b3c4d" -> endpointID = "1a2b3c4d" + EndpointIDExtractorTypeHeader EndpointIDExtractorType = "header" ) -// IsValid ensure the endpoint ID extractor type is supported +// IsValid ensure the endpoint ID extractor type is supported. func (e EndpointIDExtractorType) IsValid() bool { switch e { case EndpointIDExtractorTypeURLPath, EndpointIDExtractorTypeHeader: @@ -27,8 +33,7 @@ func (e EndpointIDExtractorType) IsValid() bool { // EndpointIDExtractor defines an interface for extracting an endpoint ID from a given source. // This could be a URL path, HTTP header, etc... type EndpointIDExtractor interface { - // Extract extracts the endpoint ID from the provided source. - // The sourceType parameter specifies whether the source is a "path" or "header". + // extractGatewayEndpointID extracts the endpoint ID from the check request. extractGatewayEndpointID(req *envoy_auth.AttributeContext_HttpRequest) (string, error) } diff --git a/envoy/auth_server/main.go b/envoy/auth_server/main.go index 953cf62e..2bdf36d8 100644 --- a/envoy/auth_server/main.go +++ b/envoy/auth_server/main.go @@ -25,7 +25,7 @@ import ( // TODO_CONSIDER(@commoddity): Make this configurable. See thread here: https://github.com/buildwithgrove/path/pull/52/files/1a3e7a11f159f5b8d3c414f2417f7879bcfab410..258136504608c1269a27047bb9bded1ab4fefcc8#r1859409934 const port = 10003 -// TODO_TECHDEBT(@commoddity): Make these values part of PATH's config YAML and remove the dependency on environment variables. +// TODO_MVP(@commoddity): Make these values part of PATH's config YAML and remove the dependency on environment variables. const ( envVarGRPCHostPort = "GRPC_HOST_PORT" envVarGRPCUseInsecure = "GRPC_USE_INSECURE" @@ -79,6 +79,16 @@ func connectGRPC(hostPort string, useInsecureCredentials bool) (*grpc.ClientConn return grpc.NewClient(hostPort, transport) } +func getEndpointIDExtractor(endpointIDExtractorType auth.EndpointIDExtractorType) auth.EndpointIDExtractor { + switch endpointIDExtractorType { + case auth.EndpointIDExtractorTypeURLPath: + return &auth.URLPathExtractor{} + case auth.EndpointIDExtractorTypeHeader: + return &auth.HeaderExtractor{} + } + return nil // this should never happen +} + func main() { // Initialize new polylog logger @@ -113,13 +123,7 @@ func main() { // Determine which gateway endpoint ID extractor to use // If the extractor is not set, use the default "url_path" extractor - var endpointIDExtractor auth.EndpointIDExtractor - switch opts.endpointIDExtractor { - case auth.EndpointIDExtractorTypeURLPath: - endpointIDExtractor = &auth.URLPathExtractor{} - case auth.EndpointIDExtractorTypeHeader: - endpointIDExtractor = &auth.HeaderExtractor{} - } + endpointIDExtractor := getEndpointIDExtractor(opts.endpointIDExtractor) // Create a new AuthHandler to handle the request auth authHandler := &auth.AuthHandler{ From e8a768954d3da527e41a3d05f744eb9bc8e880a2 Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Fri, 13 Dec 2024 10:02:03 -0500 Subject: [PATCH 07/48] Support using HTTP Headers to specify target service ID --- request/parser.go | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/request/parser.go b/request/parser.go index 2010db5a..21fd3ade 100644 --- a/request/parser.go +++ b/request/parser.go @@ -22,6 +22,11 @@ import ( "github.com/buildwithgrove/path/protocol" ) +// HTTPHeaderTargetServiceID is the key used to lookup the HTTP header specifying the target service's ID. +// Please see the following link for more details on not including `X-` prefix in the HTTP header parameter names. +// https://www.rfc-editor.org/rfc/rfc6648#section-3 +const HTTPHeaderTargetServiceID = "Target-Service-ID" + type ( Parser struct { Backend Backend @@ -45,7 +50,7 @@ func NewParser(backend Backend, enabledServices map[protocol.ServiceID]gateway.Q func (p *Parser) GetQoSService(ctx context.Context, req *http.Request) (protocol.ServiceID, gateway.QoSService, error) { - serviceID, err := p.getServiceID(req.Host) + serviceID, err := p.getServiceID(req) if err != nil { return "", nil, err } @@ -65,22 +70,35 @@ func (p *Parser) GetHTTPErrorResponse(ctx context.Context, err error) gateway.HT return &parserErrorResponse{err: err.Error(), code: http.StatusNotFound} } -// getServiceID gets the service ID from the request host +// getServiceID extracts the target service ID from the supplied HTTP request. +// As of now, it supports two options for specifying the target service ID, in the order of priority: +// 1. The value of the HTTP Header Target-Service-ID, if defined. +// e.g. `Target-Service-ID: eth` is interpreted as `eth` target service ID. +// 2. The subdomain of the HTTP request's Host field. // eg. host = "eth.gateway.pokt.network" -> serviceID = "eth" -func (p *Parser) getServiceID(host string) (protocol.ServiceID, error) { - hostParts := strings.Split(host, ".") +func (p *Parser) getServiceID(req *http.Request) (protocol.ServiceID, error) { + // Prefer the custom HTTP Header for specification of the Target Service ID + svcID := req.Header.Get(HTTPHeaderTargetServiceID) + if svcID != "" { + return p.getServiceIDFromAlias(svcID), nil + } + + // Fallback to using the HTTP request's host field's domain if the custom HTTP header is not set. + hostParts := strings.Split(req.Host, ".") if len(hostParts) < 2 { return "", errNoServiceIDProvided } subdomain := hostParts[0] + return p.getServiceIDFromAlias(subdomain), nil +} - var serviceID protocol.ServiceID - if serviceIDFromAlias, ok := p.Backend.GetServiceIDFromAlias(subdomain); ok { - serviceID = serviceIDFromAlias - } else { - serviceID = protocol.ServiceID(subdomain) +// TODO_TECHDEBT(@adshmh): consider removing the alias concept altogether: it look like a DNS/Load Balancer level concept rather than a gateway feature. +// getServiceIDFromAlias returns the service ID for the supplied alias. The serviceAlias is returned as-is if no matching service IDs are found. +func (p *Parser) getServiceIDFromAlias(serviceAlias string) protocol.ServiceID { + if serviceIDFromAlias, ok := p.Backend.GetServiceIDFromAlias(serviceAlias); ok { + return serviceIDFromAlias } - return serviceID, nil + return protocol.ServiceID(serviceAlias) } From 043bb8ed5e455ca87b9ee21f512ce039461aa9a8 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Fri, 13 Dec 2024 17:24:40 +0000 Subject: [PATCH 08/48] fix: add lua filter to envoy template to allow setting service id header from subdomain --- Makefile | 2 +- docusaurus/docs/develop/envoy/introduction.md | 40 +++++++++---------- envoy/auth_server/auth/auth_handler.go | 6 +-- envoy/auth_server/auth/authorizer_jwt.go | 2 +- .../auth_server/auth/endpoint_id_extractor.go | 8 ++-- .../auth/endpoint_id_extractor_test.go | 4 +- envoy/envoy.template.yaml | 37 ++++++++++++----- envoy/ratelimit.yaml | 8 ++-- request/parser.go | 12 +++--- 9 files changed, 70 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index c0bd5c3a..99053f68 100644 --- a/Makefile +++ b/Makefile @@ -271,7 +271,7 @@ test_request_with_header: ## Test the auth_server header endpoint ID extractor ( curl http://anvil.localhost:3001/v1 \ -X POST \ -H "Content-Type: application/json" \ - -H "x-endpoint-id: endpoint_3" \ + -H "endpoint-id: endpoint_3" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ############################### diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index d3f7f298..d3692c28 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -152,8 +152,8 @@ Envoy acts as a gateway, handling incoming requests, performing auth checks, and The PATH Auth Server uses the following [Envoy HTTP filters](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/http_filters) to handle authorization: -- **[header_mutation](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_mutation_filter)**: Ensures the request does not have the `x-jwt-user-id` header set before it is forwarded upstream. -- **[jwt_authn](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter)**: Performs JWT verification and sets the `x-jwt-user-id` header. +- **[header_mutation](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_mutation_filter)**: Ensures the request does not have the `jwt-user-id` header set before it is forwarded upstream. +- **[jwt_authn](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter)**: Performs JWT verification and sets the `jwt-user-id` header. - **[ext_authz](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter)**: Performs authorization checks using the PATH Auth Server external authorization server. - **[ratelimit](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter)**: Performs rate limiting checks using the Rate Limiter service. @@ -175,7 +175,7 @@ sequenceDiagram opt if JWT is present Envoy->>+JWTFilter: 2. Parse JWT (if present) - JWTFilter->>-Envoy: 3. Return parsed x-jwt-user-id (if present) + JWTFilter->>-Envoy: 3. Return parsed jwt-user-id (if present) end Envoy->>+AuthServer: 4. Forward Request @@ -231,7 +231,7 @@ Requests are rejected if either of the following are true: ::: :::info -Regardless of which extractor is used, the Gateway Endpoint ID will always be set in the `x-endpoint-id` header if the reuqest is forwarded to the PATH Service. +Regardless of which extractor is used, the Gateway Endpoint ID will always be set in the `endpoint-id` header if the reuqest is forwarded to the PATH Service. ::: ### 4.1 URL Path @@ -253,19 +253,19 @@ curl http://anvil.localhost:3001/v1/endpoint_3 \ ### 4.2 Header -When using the `header` extractor, the Gateway Endpoint ID must be specified in the `x-endpoint-id` header. +When using the `header` extractor, the Gateway Endpoint ID must be specified in the `endpoint-id` header. ``` --H "x-endpoint-id: " +-H "endpoint-id: " ``` -For example, if the `x-endpoint-id` header is set to `a1b2c3d4`: +For example, if the `endpoint-id` header is set to `a1b2c3d4`: ``` curl http://anvil.localhost:3001/v1 \ -X POST \ -H "Content-Type: application/json" \ - -H "x-endpoint-id: endpoint_3" \ + -H "endpoint-id: endpoint_3" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` @@ -312,14 +312,14 @@ _Example Request Header:_ -H "Authorization: Bearer " ``` -The `jwt_authn` filter will verify the JWT and, if valid, set the `x-jwt-user-id` header from the `sub` claim of the JWT. An invalid JWT will result in an error. +The `jwt_authn` filter will verify the JWT and, if valid, set the `jwt-user-id` header from the `sub` claim of the JWT. An invalid JWT will result in an error. -The `Go External Authorization Server` will use the `x-jwt-user-id` header to make an authorization decision; if the `GatewayEndpoint`'s `Auth.AuthorizedUsers` field contains the `x-jwt-user-id` value, the request will be authorized. +The `Go External Authorization Server` will use the `jwt-user-id` header to make an authorization decision; if the `GatewayEndpoint`'s `Auth.AuthorizedUsers` field contains the `jwt-user-id` value, the request will be authorized. _Example auth provider user ID header:_ ``` -x-jwt-user-id: auth0|a12b3c4d5e6f7g8h9 +jwt-user-id: auth0|a12b3c4d5e6f7g8h9 ``` :::info @@ -490,9 +490,9 @@ Forking the PADS repo is the easiest way to get started, though any gRPC server ### 6.1. Rate Limit Configuration -1. The `Go External Authorization Server` sets the `x-rl-endpoint-id` and `x-rl-plan` headers if the `GatewayEndpoint` for the request should be rate limited. +1. The `Go External Authorization Server` sets the `rl-endpoint-id` and `rl-throughput` headers if the `GatewayEndpoint` for the request should be rate limited. -2. Envoy Proxy is configured to forward the `x-rl-endpoint-id` and `x-rl-plan` headers to the rate limiter service as descriptors. +2. Envoy Proxy is configured to forward the `rl-endpoint-id` and `rl-throughput` headers to the rate limiter service as descriptors. _envoy.yaml_ @@ -500,11 +500,11 @@ Forking the PADS repo is the easiest way to get started, though any gRPC server rate_limits: - actions: - request_headers: - header_name: "x-rl-endpoint-id" - descriptor_key: "x-rl-endpoint-id" + header_name: "rl-endpoint-id" + descriptor_key: "rl-endpoint-id" - request_headers: - header_name: "x-rl-plan" - descriptor_key: "x-rl-plan" + header_name: "rl-throughput" + descriptor_key: "rl-throughput" ``` 3. Rate limiting is configured through the [`/envoy/ratelimit.yaml`](https://github.com/buildwithgrove/path/blob/main/envoy/ratelimit.yaml) file. @@ -514,9 +514,9 @@ Forking the PADS repo is the easiest way to get started, though any gRPC server ```yaml domain: rl descriptors: - - key: x-rl-endpoint-id + - key: rl-endpoint-id descriptors: - - key: x-rl-plan + - key: rl-throughput value: "PLAN_FREE" rate_limit: unit: second @@ -524,7 +524,7 @@ Forking the PADS repo is the easiest way to get started, though any gRPC server ``` :::info -The default throughput limit is **30 requests per second** for GatewayEndpoints with the `PLAN_FREE` plan type based on the `x-rl-endpoint-id` and `x-rl-plan` descriptors. +The default throughput limit is **30 requests per second** for GatewayEndpoints with the `PLAN_FREE` plan type based on the `rl-endpoint-id` and `rl-throughput` descriptors. _The rate limiting configuration may be configured to suit the needs of the Gateway Operator in the `ratelimit.yaml` file._ ::: diff --git a/envoy/auth_server/auth/auth_handler.go b/envoy/auth_server/auth/auth_handler.go index 9a7f9d90..622317fd 100644 --- a/envoy/auth_server/auth/auth_handler.go +++ b/envoy/auth_server/auth/auth_handler.go @@ -23,9 +23,9 @@ const ( // Not sure the best way to do this as it is referred to in multiple disparate places (eg. envoy.yaml, PATH's router.go & here) pathPrefix = "/v1/" - reqHeaderEndpointID = "x-endpoint-id" // Set on all service requests - reqHeaderRateLimitEndpointID = "x-rl-endpoint-id" // Set only on service requests that should be rate limited - reqHeaderRateLimitThroughput = "x-rl-throughput" // Set only on service requests that should be rate limited + reqHeaderEndpointID = "endpoint-id" // Set on all service requests + reqHeaderRateLimitEndpointID = "rl-endpoint-id" // Set only on service requests that should be rate limited + reqHeaderRateLimitThroughput = "rl-throughput" // Set only on service requests that should be rate limited errBody = `{"code": %d, "message": "%s"}` ) diff --git a/envoy/auth_server/auth/authorizer_jwt.go b/envoy/auth_server/auth/authorizer_jwt.go index e5b36074..f59c8c1b 100644 --- a/envoy/auth_server/auth/authorizer_jwt.go +++ b/envoy/auth_server/auth/authorizer_jwt.go @@ -4,7 +4,7 @@ import ( "github.com/buildwithgrove/path/envoy/auth_server/proto" ) -const reqHeaderJWTUserID = "x-jwt-user-id" // Defined in envoy.yaml +const reqHeaderJWTUserID = "jwt-user-id" // Defined in envoy.yaml // JWTAuthorizer is an Authorizer that ensures the request is authorized // by checking if the account user ID is in the GatewayEndpoint's authorized users. diff --git a/envoy/auth_server/auth/endpoint_id_extractor.go b/envoy/auth_server/auth/endpoint_id_extractor.go index bab92845..6e9b3a19 100644 --- a/envoy/auth_server/auth/endpoint_id_extractor.go +++ b/envoy/auth_server/auth/endpoint_id_extractor.go @@ -16,7 +16,7 @@ const ( EndpointIDExtractorTypeURLPath EndpointIDExtractorType = "url_path" // EndpointIDExtractorTypeHeader specifies that the endpoint ID is extracted from the HTTP headers. - // Example: Header = "x-endpoint-id: 1a2b3c4d" -> endpointID = "1a2b3c4d" + // Example: Header = "endpoint-id: 1a2b3c4d" -> endpointID = "1a2b3c4d" EndpointIDExtractorTypeHeader EndpointIDExtractorType = "header" ) @@ -69,12 +69,14 @@ var _ EndpointIDExtractor = &HeaderExtractor{} type HeaderExtractor struct{} // ExtractGatewayEndpointID extracts the endpoint ID from the HTTP headers. -// The endpoint ID is expected to be in the "x-endpoint-id" header. +// The endpoint ID is expected to be in the "endpoint-id" header. // -// eg. Header = "x-endpoint-id: 1a2b3c4d" -> endpointID = "1a2b3c4d" +// eg. Header = "endpoint-id: 1a2b3c4d" -> endpointID = "1a2b3c4d" func (h *HeaderExtractor) extractGatewayEndpointID(req *envoy_auth.AttributeContext_HttpRequest) (string, error) { headers := req.GetHeaders() + fmt.Println("headers", headers) + endpointID, ok := headers[reqHeaderEndpointID] if !ok { return "", fmt.Errorf("endpoint ID header not found") diff --git a/envoy/auth_server/auth/endpoint_id_extractor_test.go b/envoy/auth_server/auth/endpoint_id_extractor_test.go index 35a96ac5..3bd595fe 100644 --- a/envoy/auth_server/auth/endpoint_id_extractor_test.go +++ b/envoy/auth_server/auth/endpoint_id_extractor_test.go @@ -65,7 +65,7 @@ func Test_HeaderExtractor(t *testing.T) { name: "should extract endpoint ID from header", request: &envoy_auth.AttributeContext_HttpRequest{ Headers: map[string]string{ - "x-endpoint-id": "1a2b3c4d", + "endpoint-id": "1a2b3c4d", }, }, want: "1a2b3c4d", @@ -83,7 +83,7 @@ func Test_HeaderExtractor(t *testing.T) { name: "should return error if header is empty", request: &envoy_auth.AttributeContext_HttpRequest{ Headers: map[string]string{ - "x-endpoint-id": "", + "endpoint-id": "", }, }, want: "", diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 05d3f618..6b93ab12 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -90,15 +90,34 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog http_filters: - # Removes the `x-jwt-user-id` header before forwarding the request to the external authorization filter. + # Removes the `jwt-user-id` header before forwarding the request to the external authorization filter. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_mutation_filter - name: envoy.filters.http.header_mutation typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.header_mutation.v3.HeaderMutation mutations: request_mutations: - - remove: x-jwt-user-id - # Verifies JWT tokens and sets the `x-jwt-user-id` header based on the token claims. + - remove: jwt-user-id + # DEV_NOTE: uncomment the 'envoy.filters.http.lua' filter section if you wish for + # users to specify the target service ID using the subdomain of the request's host field. + # This will require the subdomain to be specified in the request's host field; otherwise + # the request will be rejected. eg. host = "eth.path.grove.city" -> Header: "target-service-id: eth" + # + # Extracts the Service ID from the subdomain of the request's host field and attaches it + # to the request as the `target-service-id` header. + # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter + # - name: envoy.filters.http.lua + # typed_config: + # "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + # inline_code: | + # function envoy_on_request(handle) + # local host = handle:headers():get(":authority") + # local subdomain = string.match(host, "^([^.]+)") + # if subdomain ~= nil and subdomain ~= "" then + # handle:headers():add("target-service-id", subdomain) + # end + # end + # Verifies JWT tokens and sets the `jwt-user-id` header based on the token claims. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter - name: envoy.filters.http.jwt_authn typed_config: @@ -109,7 +128,7 @@ static_resources: requires: requires_any: requirements: - # Allows requests without a JWT. In such cases, `x-jwt-user-id` is not set. + # Allows requests without a JWT. In such cases, `jwt-user-id` is not set. - allow_missing: {} # Enforces JWT validation if a token is present. - provider_name: auth_provider @@ -128,7 +147,7 @@ static_resources: timeout: 1s forward: true claim_to_headers: - - header_name: x-jwt-user-id + - header_name: jwt-user-id claim_name: sub # External authorization filter to handle permission checks. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filte @@ -189,8 +208,8 @@ static_resources: - actions: # Sends descriptors for rate limiting based on custom headers. - request_headers: - header_name: "x-rl-endpoint-id" - descriptor_key: "x-rl-endpoint-id" + header_name: "rl-endpoint-id" + descriptor_key: "rl-endpoint-id" - request_headers: - header_name: "x-rl-throughput" - descriptor_key: "x-rl-throughput" + header_name: "rl-throughput" + descriptor_key: "rl-throughput" diff --git a/envoy/ratelimit.yaml b/envoy/ratelimit.yaml index 110f58c0..f7cbe3f6 100644 --- a/envoy/ratelimit.yaml +++ b/envoy/ratelimit.yaml @@ -2,12 +2,12 @@ domain: rl # must match the domain in the envoy.filters.http.ratelimit definition in .envoy.yaml # Configure the rate limits under the descriptors key descriptors: - # The descriptors under `x-rl-endpoint-id` are applied to each unique value of `x-rl-endpoint-id` - - key: x-rl-endpoint-id - # The descriptors under `x-rl-throughput` are applied only to the specified value of `x-rl-throughput` + # The descriptors under `rl-endpoint-id` are applied to each unique value of `rl-endpoint-id` + - key: rl-endpoint-id + # The descriptors under `rl-throughput` are applied only to the specified value of `rl-throughput` descriptors: # [Rule] - Rate limit requests for throughput limit "30" at 30 requests per second - - key: x-rl-throughput + - key: rl-throughput value: "30" rate_limit: unit: second diff --git a/request/parser.go b/request/parser.go index 21fd3ade..bd591050 100644 --- a/request/parser.go +++ b/request/parser.go @@ -25,7 +25,7 @@ import ( // HTTPHeaderTargetServiceID is the key used to lookup the HTTP header specifying the target service's ID. // Please see the following link for more details on not including `X-` prefix in the HTTP header parameter names. // https://www.rfc-editor.org/rfc/rfc6648#section-3 -const HTTPHeaderTargetServiceID = "Target-Service-ID" +const HTTPHeaderTargetServiceID = "target-service-id" type ( Parser struct { @@ -72,15 +72,15 @@ func (p *Parser) GetHTTPErrorResponse(ctx context.Context, err error) gateway.HT // getServiceID extracts the target service ID from the supplied HTTP request. // As of now, it supports two options for specifying the target service ID, in the order of priority: -// 1. The value of the HTTP Header Target-Service-ID, if defined. -// e.g. `Target-Service-ID: eth` is interpreted as `eth` target service ID. +// 1. The value of the HTTP Header target-service-id, if defined. +// e.g. `target-service-id: eth` is interpreted as `eth` target service ID. // 2. The subdomain of the HTTP request's Host field. // eg. host = "eth.gateway.pokt.network" -> serviceID = "eth" func (p *Parser) getServiceID(req *http.Request) (protocol.ServiceID, error) { // Prefer the custom HTTP Header for specification of the Target Service ID - svcID := req.Header.Get(HTTPHeaderTargetServiceID) - if svcID != "" { - return p.getServiceIDFromAlias(svcID), nil + serviceID := req.Header.Get(HTTPHeaderTargetServiceID) + if serviceID != "" { + return p.getServiceIDFromAlias(serviceID), nil } // Fallback to using the HTTP request's host field's domain if the custom HTTP header is not set. From ae4255ec1da42906620274df3a976c6e19a240d6 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Fri, 13 Dec 2024 17:55:45 +0000 Subject: [PATCH 09/48] chore: add lua to list of HTTP filters in docs --- docusaurus/docs/develop/envoy/introduction.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index d3692c28..0fcf3056 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -119,8 +119,6 @@ graph TD GRPCServer <-.-> DataSource ``` - - ## 3. Envoy Proxy
@@ -156,6 +154,9 @@ The PATH Auth Server uses the following [Envoy HTTP filters](https://www.envoypr - **[jwt_authn](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter)**: Performs JWT verification and sets the `jwt-user-id` header. - **[ext_authz](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter)**: Performs authorization checks using the PATH Auth Server external authorization server. - **[ratelimit](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter)**: Performs rate limiting checks using the Rate Limiter service. +- **[lua (optional)](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter)**: Extracts the Service ID from the subdomain of the request's host field and attaches it to the request as the `target-service-id` header. + - Use only if you wish to specify the Service ID using the subdomain of the request's host field, + - eg. `host = "eth.path.grove.city" -> Header: "target-service-id: eth"`. ### 3.3. Request Lifecycle From ca0eb9825045137cad820a1cd81c28e2010ab739 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Mon, 16 Dec 2024 09:03:56 +0000 Subject: [PATCH 10/48] chore: remove debug log --- envoy/auth_server/auth/endpoint_id_extractor.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/envoy/auth_server/auth/endpoint_id_extractor.go b/envoy/auth_server/auth/endpoint_id_extractor.go index 6e9b3a19..281f5e7e 100644 --- a/envoy/auth_server/auth/endpoint_id_extractor.go +++ b/envoy/auth_server/auth/endpoint_id_extractor.go @@ -75,8 +75,6 @@ type HeaderExtractor struct{} func (h *HeaderExtractor) extractGatewayEndpointID(req *envoy_auth.AttributeContext_HttpRequest) (string, error) { headers := req.GetHeaders() - fmt.Println("headers", headers) - endpointID, ok := headers[reqHeaderEndpointID] if !ok { return "", fmt.Errorf("endpoint ID header not found") From 7ac3532131ba18a56c08114958fe8e8d7182deaf Mon Sep 17 00:00:00 2001 From: commoddity <47662958+commoddity@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:24:35 +0000 Subject: [PATCH 11/48] Apply suggestions from code review Co-authored-by: Daniel Olshansky --- Makefile | 14 +++++++------- docusaurus/docs/develop/envoy/introduction.md | 17 +++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index c0bd5c3a..787d9e24 100644 --- a/Makefile +++ b/Makefile @@ -262,17 +262,17 @@ docusaurus_start: ## Start docusaurus server .PHONY: test_request_with_url_path test_request_with_url_path: ## Test the auth_server URL path endpoint ID extractor (must have Envoy running) curl http://anvil.localhost:3001/v1/endpoint_3 \ - -X POST \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' .PHONY: test_request_with_header test_request_with_header: ## Test the auth_server header endpoint ID extractor (must have Envoy running) curl http://anvil.localhost:3001/v1 \ - -X POST \ - -H "Content-Type: application/json" \ - -H "x-endpoint-id: endpoint_3" \ - -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + -X POST \ + -H "Content-Type: application/json" \ + -H "x-endpoint-id: endpoint_3" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ############################### ### Makefile imports ### diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index d3f7f298..534f1e04 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -70,6 +70,7 @@ Specifically, this is split into two logical parts: :::tip A [Tiltfile](https://github.com/buildwithgrove/path/blob/main/Tiltfile) is provided to run all of these services locally. + ::: - **PATH Service**: The service that handles requests after they have been authorized. @@ -211,18 +212,14 @@ sequenceDiagram The Auth Server may extract the Gateway Endpoint ID from the request in one of two ways: -1. [URL Path](#221-url-path) -2. [Header](#222-header) - -This is determined by the `ENDPOINT_ID_EXTRACTOR` environment variable in the `auth_server/.env` file. +1. [URL Path](#221-url-path): e.g. ... +2. [Header](#222-header): Example or details ... -Valid options are: +This is determined by the `ENDPOINT_ID_EXTRACTOR` environment variable in the `auth_server/.env` file. One of: -- `url_path` +- `url_path` (default) - `header` -If the `ENDPOINT_ID_EXTRACTOR` environment variable is not set, the default `url_path` extractor is used. - :::warning Requests are rejected if either of the following are true: @@ -234,7 +231,7 @@ Requests are rejected if either of the following are true: Regardless of which extractor is used, the Gateway Endpoint ID will always be set in the `x-endpoint-id` header if the reuqest is forwarded to the PATH Service. ::: -### 4.1 URL Path +### 4.1 URL Path Endpoint ID Extractor When using the `url_path` extractor, the Gateway Endpoint ID must be specified in the URL path. @@ -251,7 +248,7 @@ curl http://anvil.localhost:3001/v1/endpoint_3 \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` -### 4.2 Header +### 4.2 Header Endpoint ID Extractor When using the `header` extractor, the Gateway Endpoint ID must be specified in the `x-endpoint-id` header. From 1699f448b55da2d4d8f3502dc54f30804f1d8fd7 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Mon, 16 Dec 2024 15:40:02 +0000 Subject: [PATCH 12/48] fix: implement review comments --- Makefile | 20 +-------- docusaurus/docs/develop/envoy/introduction.md | 8 ++-- makefiles/test_requests.mk | 44 +++++++++++++++++++ 3 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 makefiles/test_requests.mk diff --git a/Makefile b/Makefile index 787d9e24..1db013df 100644 --- a/Makefile +++ b/Makefile @@ -255,27 +255,9 @@ go_docs: ## Start Go documentation server docusaurus_start: ## Start docusaurus server cd docusaurus && npm i && npm run start -####################### -#### Test Requests #### -####################### - -.PHONY: test_request_with_url_path -test_request_with_url_path: ## Test the auth_server URL path endpoint ID extractor (must have Envoy running) - curl http://anvil.localhost:3001/v1/endpoint_3 \ - -X POST \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' - -.PHONY: test_request_with_header -test_request_with_header: ## Test the auth_server header endpoint ID extractor (must have Envoy running) - curl http://anvil.localhost:3001/v1 \ - -X POST \ - -H "Content-Type: application/json" \ - -H "x-endpoint-id: endpoint_3" \ - -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' - ############################### ### Makefile imports ### ############################### include ./makefiles/localnet.mk +include ./makefiles/test_requests.mk diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index 534f1e04..d17816c1 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -19,8 +19,8 @@ title: Introduction - [3.2. Envoy HTTP Filters](#32-envoy-http-filters) - [3.3. Request Lifecycle](#33-request-lifecycle) - [4. Specifying the Gateway Endpoint ID](#4-specifying-the-gateway-endpoint-id) - - [4.1 URL Path](#41-url-path) - - [4.2 Header](#42-header) + - [4.1 URL Path Endpoint ID Extractor](#41-url-path-endpoint-id-extractor) + - [4.2 Header Endpoint ID Extractor](#42-header-endpoint-id-extractor) - [5. Gateway Endpoint Authorization](#5-gateway-endpoint-authorization) - [5.1 JSON Web Token (JWT) Authorization](#51-json-web-token-jwt-authorization) - [5.2 API Key Authorization](#52-api-key-authorization) @@ -221,10 +221,12 @@ This is determined by the `ENDPOINT_ID_EXTRACTOR` environment variable in the `a - `header` :::warning + Requests are rejected if either of the following are true: - The `` is missing - ID is not present in the `Go External Authorization Server`'s `Gateway Endpoint Store` +- ::: :::info @@ -480,7 +482,7 @@ The custom implementation must use the methods defined in the `GatewayEndpoints` - `StreamAuthDataUpdates` :::tip -Forking the PADS repo is the easiest way to get started, though any gRPC server implementation that adheres to the `gateway_endpoint.proto` service definition should suffice. +If you wish to implement your own custom database driver, forking the PADS repo is the easiest way to get started, though any gRPC server implementation that adheres to the `gateway_endpoint.proto` service definition should suffice. ::: ## 6. Rate Limiter diff --git a/makefiles/test_requests.mk b/makefiles/test_requests.mk new file mode 100644 index 00000000..5583ef23 --- /dev/null +++ b/makefiles/test_requests.mk @@ -0,0 +1,44 @@ +####################### +#### Test Requests #### +####################### + +# This Makefile provides examples of the various ways to make requests to PATH: +# - Auth: static API key or no auth (JWT requires a non-expired JWT token, which cannot be statically set) +# - Service ID: passed as the subdomain or in the 'target-service-id' header +# - Endpoint ID: passed in the URL path or in the 'x-endpoint-id' header + +# For all of the below requests: +# - The full PATH stack including Envoy Proxy must be running +# - The 'anvil' service must be configured in the '.config.yaml' file. + +.PHONY: test_request_no_auth_url_path +test_request_no_auth_url_path: ## Test request with no auth, endpoint ID passed in the URL path and the service ID passed as the subdomain + curl http://anvil.localhost:3001/v1/endpoint_3_no_auth \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + +.PHONY: test_request_no_auth_header +test_request_no_auth_header: ## Test request with no auth, endpoint ID passed in the x-endpoint-id header and the service ID passed as the subdomain + curl http://anvil.localhost:3001/v1 \ + -X POST \ + -H "Content-Type: application/json" \ + -H "x-endpoint-id: endpoint_3_no_auth" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + +.PHONY: test_request_static_key_auth +test_request_static_key_auth: ## Test request with static key auth, endpoint ID passed in the x-endpoint-id header and the service ID passed as the subdomain + curl http://anvil.localhost:3001/v1 \ + -X POST \ + -H "Content-Type: application/json" \ + -H "x-endpoint-id: endpoint_1_static_key" \ + -H "authorization: api_key_1" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + +.PHONY: test_request_service_id_header +test_request_service_id_header: ## Test request with the service ID passed in the target-service-id header, no auth and the endpoint ID passed in the URL path + curl http://localhost:3001/v1/endpoint_3_no_auth \ + -X POST \ + -H "Content-Type: application/json" \ + -H "target-service-id: anvil" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' From f25f8aeeb4e9648677c6578e1b0933533ff8c838 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 12:07:33 +0000 Subject: [PATCH 13/48] chore: fix typo --- docusaurus/docs/develop/envoy/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index 368fa47e..8dd00dfb 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -234,7 +234,7 @@ Requests are rejected if either of the following are true: :::info -Regardless of which extractor is used, the Gateway Endpoint ID will always be set in the `endpoint-id` header if the reuqest is forwarded to the PATH Service. +Regardless of which extractor is used, the Gateway Endpoint ID will always be set in the `endpoint-id` header if the request is forwarded to the PATH Service. ::: From c1d94d45f0300212db65488d3bf6e85b1dfc5bf6 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 12:12:34 +0000 Subject: [PATCH 14/48] chore: remove refs to x- headers --- docusaurus/docs/develop/envoy/introduction.md | 46 +++++++++---------- makefiles/test_requests.mk | 10 ++-- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index 8dd00dfb..38d70e75 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -21,7 +21,6 @@ title: Introduction - [Specifying the Gateway Endpoint ID](#specifying-the-gateway-endpoint-id) - [URL Path Endpoint ID Extractor](#url-path-endpoint-id-extractor) - [Header Endpoint ID Extractor](#header-endpoint-id-extractor) - - [Example cURL Requests](#example-curl-requests) - [Gateway Endpoint Authorization](#gateway-endpoint-authorization) - [JSON Web Token (JWT) Authorization](#json-web-token-jwt-authorization) - [API Key Authorization](#api-key-authorization) @@ -249,7 +248,7 @@ https://./v1/ For example, if the `SERVICE_NAME` is `eth` and the `GATEWAY_ENDPOINT_ID` is `a1b2c3d4`: ``` -curl http://anvil.localhost:3001/v1/endpoint_3 \ +curl http://anvil.localhost:3001/v1/endpoint_3_no_auth \ -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' @@ -257,43 +256,40 @@ curl http://anvil.localhost:3001/v1/endpoint_3 \ ### Header Endpoint ID Extractor -When using the `header` extractor, the Gateway Endpoint ID must be specified in the `x-endpoint-id` header. +When using the `header` extractor, the Gateway Endpoint ID must be specified in the `endpoint-id` header. ``` -H "endpoint-id: " ``` -For example, if the `x-endpoint-id` header is set to `a1b2c3d4`: +For example, if the `endpoint-id` header is set to `a1b2c3d4`: ``` curl http://anvil.localhost:3001/v1 \ -X POST \ -H "Content-Type: application/json" \ - -H "endpoint-id: endpoint_3" \ + -H "endpoint-id: endpoint_3_no_auth" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` :::tip -Make targets are provided to send test requests with both the `url_path` and `header` extractors. -``` -make test_request_with_url_path -make test_request_with_header -``` - -:::info - -`endpoint_3` is the endpoint from the example `.gateway-endpoints.yaml` file that requires no authorization. +A variety of example cURL requests to the PATH service [may be found in the test_requests.mk file](https://github.com/buildwithgrove/path/blob/main/makefiles/test_requests.mk). -See the [Gateway Endpoint YAML File](#522-gateway-endpoint-yaml-file) section for more information on the `GatewayEndpoint` data structure. +_eg._ +```bash +## Test request with no auth, endpoint ID passed in the URL path and the service ID passed as the subdomain +make test_request_no_auth_url_path -::: +## Test request with no auth, endpoint ID passed in the endpoint-id header and the service ID passed as the subdomain +make test_request_no_auth_header +``` -### Example cURL Requests +:::info -:::tip +`endpoint_3_no_auth` is the endpoint from the example `.gateway-endpoints.yaml` file that requires no authorization. -A variety of example cURL requests to the PATH service [may be found in the test_requests.mk file](https://github.com/buildwithgrove/path/blob/main/makefiles/test_requests.mk). +See the [Gateway Endpoint YAML File](#gateway-endpoint-yaml-file) section for more information on the `GatewayEndpoint` data structure. ::: @@ -456,20 +452,20 @@ _`PADS` loads data from the Gateway Endpoints YAML file specified by the `YAML_F The yaml file below provides an example for a particular gateway operator where: -- `endpoint_1` is authorized with a static API Key -- `endpoint_2` is authorized using an auth-provider issued JWT for two users -- `endpoint_3` requires no authorization and has a rate limit set +- `endpoint_1_static_key` is authorized with a static API Key +- `endpoint_2_jwt` is authorized using an auth-provider issued JWT for two users +- `endpoint_3_no_auth` requires no authorization and has a rate limit set ```yaml endpoints: # 1. Example of a gateway endpoint using API Key Authorization - endpoint_1: + endpoint_1_static_key: auth: auth_type: "AUTH_TYPE_API_KEY" api_key: "api_key_1" # 2. Example of a gateway endpoint using JWT Authorization - endpoint_2: + endpoint_2_jwt: auth: auth_type: "AUTH_TYPE_JWT" jwt_authorized_users: @@ -477,7 +473,7 @@ endpoints: - "auth0|user_2" # 3. Example of a gateway endpoint with no authorization and rate limiting set - endpoint_3: + endpoint_3_no_auth: rate_limiting: throughput_limit: 30 capacity_limit: 100000 diff --git a/makefiles/test_requests.mk b/makefiles/test_requests.mk index 5583ef23..3048af7b 100644 --- a/makefiles/test_requests.mk +++ b/makefiles/test_requests.mk @@ -5,7 +5,7 @@ # This Makefile provides examples of the various ways to make requests to PATH: # - Auth: static API key or no auth (JWT requires a non-expired JWT token, which cannot be statically set) # - Service ID: passed as the subdomain or in the 'target-service-id' header -# - Endpoint ID: passed in the URL path or in the 'x-endpoint-id' header +# - Endpoint ID: passed in the URL path or in the 'endpoint-id' header # For all of the below requests: # - The full PATH stack including Envoy Proxy must be running @@ -19,19 +19,19 @@ test_request_no_auth_url_path: ## Test request with no auth, endpoint ID passed -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' .PHONY: test_request_no_auth_header -test_request_no_auth_header: ## Test request with no auth, endpoint ID passed in the x-endpoint-id header and the service ID passed as the subdomain +test_request_no_auth_header: ## Test request with no auth, endpoint ID passed in the endpoint-id header and the service ID passed as the subdomain curl http://anvil.localhost:3001/v1 \ -X POST \ -H "Content-Type: application/json" \ - -H "x-endpoint-id: endpoint_3_no_auth" \ + -H "endpoint-id: endpoint_3_no_auth" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' .PHONY: test_request_static_key_auth -test_request_static_key_auth: ## Test request with static key auth, endpoint ID passed in the x-endpoint-id header and the service ID passed as the subdomain +test_request_static_key_auth: ## Test request with static key auth, endpoint ID passed in the endpoint-id header and the service ID passed as the subdomain curl http://anvil.localhost:3001/v1 \ -X POST \ -H "Content-Type: application/json" \ - -H "x-endpoint-id: endpoint_1_static_key" \ + -H "endpoint-id: endpoint_1_static_key" \ -H "authorization: api_key_1" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' From 0d326b5c77dd4cd1fd5acd7e728a4a8b4c9bb331 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 12:14:12 +0000 Subject: [PATCH 15/48] chore: remove numbers from docusaurus internal links --- docusaurus/docs/develop/envoy/introduction.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index 38d70e75..14133e55 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -81,7 +81,7 @@ A [Tiltfile](https://github.com/buildwithgrove/path/blob/main/Tiltfile) is provi - **Redis**: A key-value store used by the rate limiter to share state and coordinate rate limiting across any number of PATH instances behind the same Envoy Proxy. - **Remote gRPC Server**: A server that provides the external authorization server with data on which endpoints are authorized to use the PATH service. - _PADS (PATH Auth Data Server) is provided as a functional implementation of the remote gRPC server that loads data from a YAML file or simple Postgres database._ - - _See [5.2.1. PATH Auth Data Server](#521-path-auth-data-server) for more information._ + - _See [PATH Auth Data Server](#path-auth-data-server) for more information._ ```mermaid graph TD @@ -214,8 +214,8 @@ sequenceDiagram The Auth Server may extract the Gateway Endpoint ID from the request in one of two ways: -1. [URL Path Endpoint ID Extractor](#41-url-path-endpoint-id-extractor) -2. [Header Endpoint ID Extractor](#42-header-endpoint-id-extractor) +1. [URL Path Endpoint ID Extractor](#url-path-endpoint-id-extractor) +2. [Header Endpoint ID Extractor](#header-endpoint-id-extractor) This is determined by the **`ENDPOINT_ID_EXTRACTOR`** environment variable in the `auth_server/.env` file. One of: @@ -301,9 +301,9 @@ The `Go External Authorization Server` evaluates whether incoming requests are a Three authorization types are supported: -1. [JSON Web Token (JWT) Authorization](#51-json-web-token-jwt-authorization) -2. [API Key Authorization](#52-api-key-authorization) -3. [No Authorization](#53-no-authorization) +1. [JSON Web Token (JWT) Authorization](#json-web-token-jwt-authorization) +2. [API Key Authorization](#api-key-authorization) +3. [No Authorization](#no-authorization) ### JSON Web Token (JWT) Authorization @@ -442,7 +442,7 @@ ghcr.io/buildwithgrove/path-auth-data-server:latest _This Docker image is loaded by default in the [Tiltfile](https://github.com/buildwithgrove/path/blob/main/Tiltfile) file at the root of the PATH repo._ -If the Gateway Operator wishes to implement a custom remote gRPC server, see the [Implementing a Custom Remote gRPC Server](#523-implementing-a-custom-remote-grpc-server) section. +If the Gateway Operator wishes to implement a custom remote gRPC server, see the [Implementing a Custom Remote gRPC Server](#implementing-a-custom-remote-grpc-server) section. #### Gateway Endpoint YAML File From 5c7043b7695e3f97e0940f13c5278d7a53cb213a Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 12:31:54 +0000 Subject: [PATCH 16/48] chore: modify script to handle setting of lua http filter --- docusaurus/docs/develop/envoy/introduction.md | 16 +-- envoy/envoy.template.yaml | 45 +++++---- ...ratelimit.yaml => ratelimit.template.yaml} | 0 envoy/scripts/copy_envoy_config.sh | 97 ++++++++++++++++--- 4 files changed, 119 insertions(+), 39 deletions(-) rename envoy/{ratelimit.yaml => ratelimit.template.yaml} (100%) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index 14133e55..65ebb596 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -138,11 +138,11 @@ Envoy acts as a gateway, handling incoming requests, performing auth checks, and ### Contents -- **ratelimit.yaml**: Configuration for the rate limiting service. +- **ratelimit.template.yaml**: A template configuration file for the rate limiting service. - **envoy.template.yaml**: A template configuration file for Envoy Proxy. - - Run `make copy_envoy_config` to create `envoy.yaml`. - - This will prompt you to enter your auth provider's domain and audience and will output the result to `envoy.yaml`. - - `envoy.yaml` is Git ignored as it contains sensitive information. + - Run `make copy_envoy_config` to create `.envoy.yaml`. + - This will prompt you to enter your auth provider's domain and audience and will output the result to `.envoy.yaml`. + - `.envoy.yaml` is Git ignored as it contains sensitive information. - **gateway-endpoints.example.yaml**: An example file containing data on which endpoints are authorized to use the PATH service. - ℹ️ **ONLY REQUIRED** if loading `GatewayEndpoint` data from a YAML file and used to load data in the `external authorization server` from the `remote gRPC server`. - Run `make copy_envoy_gateway_endpoints` to create `gateway-endpoints.yaml`. @@ -505,7 +505,7 @@ If you wish to implement your own custom database driver, forking the PADS repo 2. Envoy Proxy is configured to forward the `rl-endpoint-id` and `rl-throughput` headers to the rate limiter service as descriptors. - _envoy.yaml_ + _.envoy.yaml_ ```yaml rate_limits: @@ -518,9 +518,9 @@ If you wish to implement your own custom database driver, forking the PADS repo descriptor_key: "rl-throughput" ``` -3. Rate limiting is configured through the [`/envoy/ratelimit.yaml`](https://github.com/buildwithgrove/path/blob/main/envoy/ratelimit.yaml) file. +3. Rate limiting is configured through the `.ratelimit.yaml` file. - _ratelimit.yaml_ + _.ratelimit.yaml_ ```yaml domain: rl @@ -538,7 +538,7 @@ If you wish to implement your own custom database driver, forking the PADS repo The default throughput limit is **30 requests per second** for GatewayEndpoints with the `PLAN_FREE` plan type based on the `rl-endpoint-id` and `rl-throughput` descriptors. -_The rate limiting configuration may be configured to suit the needs of the Gateway Operator in the `ratelimit.yaml` file._ +_The rate limiting configuration may be configured to suit the needs of the Gateway Operator in the `.ratelimit.yaml` file._ ::: diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 6b93ab12..1d8269e3 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -90,35 +90,42 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog http_filters: + # Extracts the Service ID from the subdomain of the request's host field and attaches it + # to the request as the `target-service-id` header. + # This will require the subdomain to be specified in the request's host field; otherwise + # the request will be rejected. + # eg. host = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" + # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter + # + # DEV_NOTE: If the Gateway Operator wishes to specify the Service ID using the + # `target-service-id` header, this filter will be removed from the configuration file. + - name: envoy.filters.http.lua + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua + inline_code: | + function envoy_on_request(handle) + local host = handle:headers():get(":authority") + local subdomain = string.match(host, "^([^.]+)") + if subdomain ~= nil and subdomain ~= "" then + handle:headers():add("target-service-id", subdomain) + end + end # Removes the `jwt-user-id` header before forwarding the request to the external authorization filter. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_mutation_filter + # + # DEV_NOTE: If the Gateway Operator does not wish to use JWT authorization, + # this filter will be removed from the configuration file. - name: envoy.filters.http.header_mutation typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.header_mutation.v3.HeaderMutation mutations: request_mutations: - remove: jwt-user-id - # DEV_NOTE: uncomment the 'envoy.filters.http.lua' filter section if you wish for - # users to specify the target service ID using the subdomain of the request's host field. - # This will require the subdomain to be specified in the request's host field; otherwise - # the request will be rejected. eg. host = "eth.path.grove.city" -> Header: "target-service-id: eth" - # - # Extracts the Service ID from the subdomain of the request's host field and attaches it - # to the request as the `target-service-id` header. - # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter - # - name: envoy.filters.http.lua - # typed_config: - # "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua - # inline_code: | - # function envoy_on_request(handle) - # local host = handle:headers():get(":authority") - # local subdomain = string.match(host, "^([^.]+)") - # if subdomain ~= nil and subdomain ~= "" then - # handle:headers():add("target-service-id", subdomain) - # end - # end # Verifies JWT tokens and sets the `jwt-user-id` header based on the token claims. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter + # + # DEV_NOTE: If the Gateway Operator does not wish to use JWT authorization, + # this filter will be removed from the configuration file. - name: envoy.filters.http.jwt_authn typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication diff --git a/envoy/ratelimit.yaml b/envoy/ratelimit.template.yaml similarity index 100% rename from envoy/ratelimit.yaml rename to envoy/ratelimit.template.yaml diff --git a/envoy/scripts/copy_envoy_config.sh b/envoy/scripts/copy_envoy_config.sh index b844313e..a23aa626 100755 --- a/envoy/scripts/copy_envoy_config.sh +++ b/envoy/scripts/copy_envoy_config.sh @@ -6,8 +6,16 @@ # Get the directory of the script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Source file names +ENVOY_TEMPLATE_FILE_NAME="envoy.template.yaml" +ENVOY_RATELIMIT_TEMPLATE_FILE_NAME="ratelimit.template.yaml" + +# Destination file names +ENVOY_FILE_NAME=".envoy.yaml" +ENVOY_RATELIMIT_FILE_NAME=".ratelimit.yaml" + # Define the absolute path for envoy.yaml -ENVOY_CONFIG_PATH="$SCRIPT_DIR/../../local/path/envoy/.envoy.yaml" +ENVOY_CONFIG_PATH="$SCRIPT_DIR/../../local/path/envoy/$ENVOY_FILE_NAME" # Check if envoy.yaml exists and throw an error if it does if [ -f "$ENVOY_CONFIG_PATH" ]; then @@ -15,20 +23,85 @@ if [ -f "$ENVOY_CONFIG_PATH" ]; then exit 1 fi -# Prompt for AUTH_DOMAIN -read -p "Enter AUTH_DOMAIN (eg. 'auth.example.com'): " AUTH_DOMAIN +# Function to prompt the user for OAuth usage +prompt_oauth_usage() { + while true; do + echo "Configure JWT Authentication:" + echo "1. Enable JWT Auth (requires an OAuth provider like Auth0, along with domain and audience details)" + echo "2. Disable JWT Auth" + echo "Select an option (1 or 2): " + read -p "> " USE_OAUTH + if [[ "$USE_OAUTH" == "1" ]]; then + return 0 + elif [[ "$USE_OAUTH" == "2" ]]; then + return 1 + else + echo "Invalid selection. Please enter '1' or '2'." + fi + done +} + +# Function to prompt the user for Service ID specification method +prompt_service_id_method() { + while true; do + echo "Specify how to determine the Service ID in the request:" + echo "1. As the 'target-service-id' header" + echo " e.g., Header: 'target-service-id: anvil' -> Service ID: 'anvil'" + echo "2. As the URL subdomain" + echo " e.g., http://anvil.path.grove.city/v1 -> Service ID: 'anvil'" + echo "Select an option (1 or 2): " + read -p "> " SERVICE_ID_METHOD + if [[ "$SERVICE_ID_METHOD" == "1" ]]; then + echo "Service ID will be determined from the 'target-service-id' header." + return 0 + elif [[ "$SERVICE_ID_METHOD" == "2" ]]; then + echo "Service ID will be determined from the URL subdomain." + return 1 + else + echo "Invalid selection. Please enter '1' or '2'." + fi + done +} + +# Prompt the user if they wish to use an OAuth provider +if prompt_oauth_usage; then + # Prompt for AUTH_DOMAIN + echo "Enter AUTH_DOMAIN: This is the domain of your OAuth provider, where the authorization server is hosted." + echo "Example: 'auth.example.com'" + read -p "> " AUTH_DOMAIN + + # Prompt for AUTH_AUDIENCE + echo "Enter AUTH_AUDIENCE: This is the intended audience for the token, typically the identifier of the API or service that will consume the token." + echo "Example: 'https://auth.example.com/oauth/token'" + read -p "> " AUTH_AUDIENCE -# Prompt for AUTH_AUDIENCE -read -p "Enter AUTH_AUDIENCE (eg. 'https://auth.example.com/oauth/token'): " AUTH_AUDIENCE + # Substitute sensitive variables manually using bash parameter expansion + sed -e "s|\${AUTH_DOMAIN}|$AUTH_DOMAIN|g" \ + -e "s|\${AUTH_AUDIENCE}|$AUTH_AUDIENCE|g" \ + "$SCRIPT_DIR/../$ENVOY_TEMPLATE_FILE_NAME" > "$ENVOY_CONFIG_PATH" +else + # Just copy the file without substitution if the user does not wish to use JWT authorization + cp "$SCRIPT_DIR/../$ENVOY_TEMPLATE_FILE_NAME" "$ENVOY_CONFIG_PATH" -# Substitute sensitive variables manually using bash parameter expansion -sed -e "s|\${AUTH_DOMAIN}|$AUTH_DOMAIN|g" \ - -e "s|\${AUTH_AUDIENCE}|$AUTH_AUDIENCE|g" \ - "$SCRIPT_DIR/../envoy.template.yaml" > "$ENVOY_CONFIG_PATH" + # Use yq to remove specific YAML blocks related to JWT authentication + yq eval 'del(.static_resources.clusters[] | select(.name == "auth_jwks_cluster"))' -i "$ENVOY_CONFIG_PATH" + yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.jwt_authn"))' -i "$ENVOY_CONFIG_PATH" + yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.header_mutation"))' -i "$ENVOY_CONFIG_PATH" +fi + +# Prompt the user for Service ID specification method +prompt_service_id_method -echo "envoy.yaml has been created at $ENVOY_CONFIG_PATH" +# If the user selects the 'target-service-id' header, remove the Lua filter +if [[ "$SERVICE_ID_METHOD" == "1" ]]; then + yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.lua"))' -i "$ENVOY_CONFIG_PATH" +fi + +echo "$ENVOY_FILE_NAME has been created at $ENVOY_CONFIG_PATH" # Define the absolute path for ratelimit.yaml -RATELIMIT_CONFIG_PATH="$SCRIPT_DIR/../../local/path/envoy/.ratelimit.yaml" +RATELIMIT_CONFIG_PATH="$SCRIPT_DIR/../../local/path/envoy/$ENVOY_RATELIMIT_FILE_NAME" + +cp "$SCRIPT_DIR/../$ENVOY_RATELIMIT_TEMPLATE_FILE_NAME" "$RATELIMIT_CONFIG_PATH" -cp "$SCRIPT_DIR/../ratelimit.yaml" "$RATELIMIT_CONFIG_PATH" \ No newline at end of file +echo "$ENVOY_RATELIMIT_FILE_NAME has been created at $RATELIMIT_CONFIG_PATH" From c1a31f9dd38f53ee335ba244fa50804ed6ba49f9 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 12:44:15 +0000 Subject: [PATCH 17/48] chore: update docusarus docs about service id specification method --- docusaurus/docs/develop/envoy/introduction.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index 65ebb596..d98e35d0 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -25,6 +25,9 @@ title: Introduction - [JSON Web Token (JWT) Authorization](#json-web-token-jwt-authorization) - [API Key Authorization](#api-key-authorization) - [No Authorization](#no-authorization) +- [Service ID Specification](#service-id-specification) + - [Target Service ID Header](#target-service-id-header) + - [URL Subdomain](#url-subdomain) - [External Authorization Server](#external-authorization-server) - [External Auth Service Sequence Diagram](#external-auth-service-sequence-diagram) - [External Auth Service Environment Variables](#external-auth-service-environment-variables) @@ -310,6 +313,11 @@ Three authorization types are supported: For GatewayEndpoints with the `AuthType` field set to `JWT_AUTH`, a valid JWT issued by the auth provider specified in the `envoy.yaml` file is required to access the PATH service. :::tip + +The `make init_envoy` command will prompt you about whether you wish to use JWT authorization. + +If you do wish to use it, you will be asked to enter your auth provider's domain and audience. + Auth0 is an example of a JWT issuer that can be used with PATH. Their docs page on JWTs gives a good overview of the JWT format and how to issue JWTs to your users: @@ -334,7 +342,9 @@ jwt-user-id: auth0|a12b3c4d5e6f7g8h9 ``` :::info + For more information, see the [Envoy JWT Authn Docs](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter) + ::: ### API Key Authorization @@ -355,6 +365,50 @@ For GatewayEndpoints with the `AuthType` field set to `NO_AUTH`, no authorizatio All requests for GatewayEndpoints with the `AuthType` field set to `NO_AUTH` will be authorized by the `Go External Authorization Server`. +## Service ID Specification + +The `target-service-id` header is used to specify the Service ID in the request. + +There are two methods for specifying this header in the request: + +1. [Target Service ID Header](#target-service-id-header) +2. [URL Subdomain](#url-subdomain) +3. +:::tip + +The `make init_envoy` command will prompt you about which service ID specification method to use. + +::: + +### Target Service ID Header + +The `target-service-id` header is set directly on the request. + +_Example request:_ +```bash +curl http://localhost:3001/v1 \ + -X POST \ + -H "Content-Type: application/json" \ + -H "target-service-id: anvil" \ + -H "endpoint-id: endpoint_3_no_auth" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' +``` + +### URL Subdomain + +The `target-service-id` header is set by the `lua` HTTP filter in the Envoy configuration file from the subdomain of the request's host field. + +eg. `host = "anvil.path.grove.city" -> Header: "target-service-id: anvil"` + +_Example request:_ +```bash +curl http://anvil.localhost:3001/v1 \ + -X POST \ + -H "Content-Type: application/json" \ + -H "endpoint-id: endpoint_3_no_auth" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' +``` + ## External Authorization Server :::info From cd61b2f08b53b8fe33817242b772e28771311cba Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 13:04:21 +0000 Subject: [PATCH 18/48] chore: improve init scripts --- Makefile | 28 +--- envoy/scripts/copy_envoy_config.sh | 143 ++++++++++--------- envoy/scripts/copy_gateway_endpoints_yaml.sh | 21 ++- 3 files changed, 101 insertions(+), 91 deletions(-) diff --git a/Makefile b/Makefile index 1db013df..d06f1aa2 100644 --- a/Makefile +++ b/Makefile @@ -204,33 +204,13 @@ init_envoy: copy_envoy_config copy_gateway_endpoints ## Runs copy_envoy_config a .PHONY: copy_envoy_config copy_envoy_config: ## Substitutes the sensitive 0Auth environment variables in the template envoy configuration yaml file and outputs the result to .envoy.yaml - @if [ ! -f ./local/path/envoy/envoy.yaml ]; then \ - mkdir -p local/path/envoy; \ - ./envoy/scripts/copy_envoy_config.sh; \ - echo "###########################################################"; \ - echo "### Created ./local/path/envoy/envoy.yaml ###"; \ - echo "### README: Please ensure the configuration is correct. ###"; \ - echo "###########################################################"; \ - else \ - echo "######################################################################"; \ - echo "### ./local/path/envoy/envoy.yaml already exists, not overwriting. ###"; \ - echo "######################################################################"; \ - fi + @mkdir -p local/path/envoy; + @./envoy/scripts/copy_envoy_config.sh; .PHONY: copy_gateway_endpoints copy_gateway_endpoints: ## Copies the example gateway endpoints YAML file from the PADS repo to ./local/path/envoy/.gateway-endpoints.yaml - @if [ ! -f ./local/path/envoy/gateway-endpoints.yaml ]; then \ - mkdir -p local/path/envoy; \ - ./envoy/scripts/copy_gateway_endpoints_yaml.sh; \ - echo "###########################################################"; \ - echo "### Created ./local/path/envoy/gateway-endpoints.yaml ###"; \ - echo "### README: Please update this file with your own data. ###"; \ - echo "###########################################################"; \ - else \ - echo "##################################################################################"; \ - echo "### ./local/path/envoy/gateway-endpoints.yaml already exists, not overwriting. ###"; \ - echo "##################################################################################"; \ - fi + @mkdir -p local/path/envoy; + @./envoy/scripts/copy_gateway_endpoints_yaml.sh; ############################### ### Generation Make Targets ### diff --git a/envoy/scripts/copy_envoy_config.sh b/envoy/scripts/copy_envoy_config.sh index a23aa626..acb30cd7 100755 --- a/envoy/scripts/copy_envoy_config.sh +++ b/envoy/scripts/copy_envoy_config.sh @@ -14,29 +14,82 @@ ENVOY_RATELIMIT_TEMPLATE_FILE_NAME="ratelimit.template.yaml" ENVOY_FILE_NAME=".envoy.yaml" ENVOY_RATELIMIT_FILE_NAME=".ratelimit.yaml" -# Define the absolute path for envoy.yaml -ENVOY_CONFIG_PATH="$SCRIPT_DIR/../../local/path/envoy/$ENVOY_FILE_NAME" +# Define the absolute paths +ENVOY_CONFIG_PATH=$(realpath "$SCRIPT_DIR/../../local/path/envoy/$ENVOY_FILE_NAME") +RATELIMIT_CONFIG_PATH=$(realpath "$SCRIPT_DIR/../../local/path/envoy/$ENVOY_RATELIMIT_FILE_NAME") + +# Function to handle envoy.yaml creation +create_envoy_config() { + # Check if envoy.yaml exists + if [ -f "$ENVOY_CONFIG_PATH" ]; then + echo "💡 $ENVOY_CONFIG_PATH already exists, not overwriting." + else + # Prompt the user if they wish to use an OAuth provider + if prompt_oauth_usage; then + # Prompt for AUTH_DOMAIN + echo "🔑 Enter AUTH_DOMAIN: This is the domain of your OAuth provider, where the authorization server is hosted." + echo " Example: 'auth.example.com'" + read -p "> " AUTH_DOMAIN + + # Prompt for AUTH_AUDIENCE + echo "🎯 Enter AUTH_AUDIENCE: This is the intended audience for the token, typically the identifier of the API or service that will consume the token." + echo " Example: 'https://auth.example.com/oauth/token'" + read -p "> " AUTH_AUDIENCE + + # Substitute sensitive variables manually using bash parameter expansion + sed -e "s|\${AUTH_DOMAIN}|$AUTH_DOMAIN|g" \ + -e "s|\${AUTH_AUDIENCE}|$AUTH_AUDIENCE|g" \ + "$SCRIPT_DIR/../$ENVOY_TEMPLATE_FILE_NAME" > "$ENVOY_CONFIG_PATH" + + echo "🔑 JWT Authorization is enabled" + else + # Just copy the file without substitution if the user does not wish to use JWT authorization + cp "$SCRIPT_DIR/../$ENVOY_TEMPLATE_FILE_NAME" "$ENVOY_CONFIG_PATH" + + # Use yq to remove specific YAML blocks related to JWT authentication + yq eval 'del(.static_resources.clusters[] | select(.name == "auth_jwks_cluster"))' -i "$ENVOY_CONFIG_PATH" + yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.jwt_authn"))' -i "$ENVOY_CONFIG_PATH" + yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.header_mutation"))' -i "$ENVOY_CONFIG_PATH" + + echo "🔑 JWT Authorization is disabled" + fi + + # Prompt the user for Service ID specification method + prompt_service_id_method + + # If the user selects the 'target-service-id' header, remove the Lua filter + if [[ "$SERVICE_ID_METHOD" == "1" ]]; then + yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.lua"))' -i "$ENVOY_CONFIG_PATH" + fi + + echo "✅ $ENVOY_FILE_NAME has been created at $ENVOY_CONFIG_PATH" + fi +} -# Check if envoy.yaml exists and throw an error if it does -if [ -f "$ENVOY_CONFIG_PATH" ]; then - echo "Error: $ENVOY_CONFIG_PATH already exists." - exit 1 -fi +# Function to handle ratelimit.yaml creation +create_ratelimit_config() { + # Check if ratelimit.yaml exists + if [ -f "$RATELIMIT_CONFIG_PATH" ]; then + echo "💡 $RATELIMIT_CONFIG_PATH already exists, not overwriting." + else + cp "$SCRIPT_DIR/../$ENVOY_RATELIMIT_TEMPLATE_FILE_NAME" "$RATELIMIT_CONFIG_PATH" + echo "✅ $ENVOY_RATELIMIT_FILE_NAME has been created at $RATELIMIT_CONFIG_PATH" + fi +} # Function to prompt the user for OAuth usage prompt_oauth_usage() { while true; do - echo "Configure JWT Authentication:" - echo "1. Enable JWT Auth (requires an OAuth provider like Auth0, along with domain and audience details)" - echo "2. Disable JWT Auth" - echo "Select an option (1 or 2): " - read -p "> " USE_OAUTH + echo "🔧 Configure JWT Authentication:" + echo " 1️⃣ Enable JWT Auth (requires an OAuth provider like Auth0, along with domain and audience details)" + echo " 2️⃣ Disable JWT Auth" + read -p "👉 Select an option (1 or 2): " USE_OAUTH if [[ "$USE_OAUTH" == "1" ]]; then return 0 elif [[ "$USE_OAUTH" == "2" ]]; then return 1 else - echo "Invalid selection. Please enter '1' or '2'." + echo "❌ Invalid selection. Please enter '1' or '2'." fi done } @@ -44,64 +97,24 @@ prompt_oauth_usage() { # Function to prompt the user for Service ID specification method prompt_service_id_method() { while true; do - echo "Specify how to determine the Service ID in the request:" - echo "1. As the 'target-service-id' header" - echo " e.g., Header: 'target-service-id: anvil' -> Service ID: 'anvil'" - echo "2. As the URL subdomain" - echo " e.g., http://anvil.path.grove.city/v1 -> Service ID: 'anvil'" - echo "Select an option (1 or 2): " - read -p "> " SERVICE_ID_METHOD + echo "🔧 Configure Service ID Specification Method:" + echo " 1️⃣ As the 'target-service-id' header" + echo " e.g., Header: 'target-service-id: anvil' -> Service ID: 'anvil'" + echo " 2️⃣ As the URL subdomain" + echo " e.g., http://anvil.path.grove.city/v1 -> Service ID: 'anvil'" + read -p "👉 Select an option (1 or 2): " SERVICE_ID_METHOD if [[ "$SERVICE_ID_METHOD" == "1" ]]; then - echo "Service ID will be determined from the 'target-service-id' header." + echo "ℹ️ Service ID will be determined from the 'target-service-id' header." return 0 elif [[ "$SERVICE_ID_METHOD" == "2" ]]; then - echo "Service ID will be determined from the URL subdomain." + echo "ℹ️ Service ID will be determined from the URL subdomain." return 1 else - echo "Invalid selection. Please enter '1' or '2'." + echo "❌ Invalid selection. Please enter '1' or '2'." fi done } -# Prompt the user if they wish to use an OAuth provider -if prompt_oauth_usage; then - # Prompt for AUTH_DOMAIN - echo "Enter AUTH_DOMAIN: This is the domain of your OAuth provider, where the authorization server is hosted." - echo "Example: 'auth.example.com'" - read -p "> " AUTH_DOMAIN - - # Prompt for AUTH_AUDIENCE - echo "Enter AUTH_AUDIENCE: This is the intended audience for the token, typically the identifier of the API or service that will consume the token." - echo "Example: 'https://auth.example.com/oauth/token'" - read -p "> " AUTH_AUDIENCE - - # Substitute sensitive variables manually using bash parameter expansion - sed -e "s|\${AUTH_DOMAIN}|$AUTH_DOMAIN|g" \ - -e "s|\${AUTH_AUDIENCE}|$AUTH_AUDIENCE|g" \ - "$SCRIPT_DIR/../$ENVOY_TEMPLATE_FILE_NAME" > "$ENVOY_CONFIG_PATH" -else - # Just copy the file without substitution if the user does not wish to use JWT authorization - cp "$SCRIPT_DIR/../$ENVOY_TEMPLATE_FILE_NAME" "$ENVOY_CONFIG_PATH" - - # Use yq to remove specific YAML blocks related to JWT authentication - yq eval 'del(.static_resources.clusters[] | select(.name == "auth_jwks_cluster"))' -i "$ENVOY_CONFIG_PATH" - yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.jwt_authn"))' -i "$ENVOY_CONFIG_PATH" - yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.header_mutation"))' -i "$ENVOY_CONFIG_PATH" -fi - -# Prompt the user for Service ID specification method -prompt_service_id_method - -# If the user selects the 'target-service-id' header, remove the Lua filter -if [[ "$SERVICE_ID_METHOD" == "1" ]]; then - yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.lua"))' -i "$ENVOY_CONFIG_PATH" -fi - -echo "$ENVOY_FILE_NAME has been created at $ENVOY_CONFIG_PATH" - -# Define the absolute path for ratelimit.yaml -RATELIMIT_CONFIG_PATH="$SCRIPT_DIR/../../local/path/envoy/$ENVOY_RATELIMIT_FILE_NAME" - -cp "$SCRIPT_DIR/../$ENVOY_RATELIMIT_TEMPLATE_FILE_NAME" "$RATELIMIT_CONFIG_PATH" - -echo "$ENVOY_RATELIMIT_FILE_NAME has been created at $RATELIMIT_CONFIG_PATH" +# Execute the functions +create_envoy_config +create_ratelimit_config diff --git a/envoy/scripts/copy_gateway_endpoints_yaml.sh b/envoy/scripts/copy_gateway_endpoints_yaml.sh index 8e94d460..6e1aa7d8 100755 --- a/envoy/scripts/copy_gateway_endpoints_yaml.sh +++ b/envoy/scripts/copy_gateway_endpoints_yaml.sh @@ -1,12 +1,29 @@ #!/bin/bash +# URL of the gateway-endpoints.example.yaml file in the PADS repo URL="https://raw.githubusercontent.com/buildwithgrove/path-auth-data-server/refs/heads/main/yaml/testdata/gateway-endpoints.example.yaml" +# Get the directory of the script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Destination file path +DESTINATION=$(realpath "$SCRIPT_DIR/../../local/path/envoy/.gateway-endpoints.yaml") + +# Check if the gateway-endpoints.yaml file already exists +if [ -f "$DESTINATION" ]; then + echo "💡 $DESTINATION already exists, not overwriting." + exit 0 +fi + +# Download the file using wget or PowerShell if command -v wget &> /dev/null; then - wget -O ./local/path/envoy/.gateway-endpoints.yaml "$URL" + wget -O "$DESTINATION" "$URL" elif command -v powershell &> /dev/null; then - powershell -Command "Invoke-WebRequest -Uri '$URL' -OutFile './local/path/envoy/.gateway-endpoints.yaml'" + powershell -Command "Invoke-WebRequest -Uri '$URL' -OutFile '$DESTINATION'" else echo "Please install wget or use PowerShell to run this script." exit 1 fi + +echo "✅ $DESTINATION has been created" +echo "📄 README: Please update this file with your own data." From 1698a793f5a4f85a0b47f592743a174e7d8048bc Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 13:17:35 +0000 Subject: [PATCH 19/48] chore: add example request for all headers --- local/kubernetes/envoy-ext-authz.yaml | 2 +- makefiles/test_requests.mk | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/local/kubernetes/envoy-ext-authz.yaml b/local/kubernetes/envoy-ext-authz.yaml index 7ac7907a..ee78f8a4 100644 --- a/local/kubernetes/envoy-ext-authz.yaml +++ b/local/kubernetes/envoy-ext-authz.yaml @@ -29,7 +29,7 @@ spec: # Options are "url_path" or "header". # Default is "url_path" if not set. - name: ENDPOINT_ID_EXTRACTOR - value: "url_path" + value: "header" ports: - containerPort: 10003 --- diff --git a/makefiles/test_requests.mk b/makefiles/test_requests.mk index 3048af7b..2f9878a0 100644 --- a/makefiles/test_requests.mk +++ b/makefiles/test_requests.mk @@ -11,18 +11,19 @@ # - The full PATH stack including Envoy Proxy must be running # - The 'anvil' service must be configured in the '.config.yaml' file. +# These requests use the example data defined in the 'gateway-endpoints.example.yaml' file in the PADS repo. +# See: https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml + .PHONY: test_request_no_auth_url_path test_request_no_auth_url_path: ## Test request with no auth, endpoint ID passed in the URL path and the service ID passed as the subdomain curl http://anvil.localhost:3001/v1/endpoint_3_no_auth \ -X POST \ - -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' .PHONY: test_request_no_auth_header test_request_no_auth_header: ## Test request with no auth, endpoint ID passed in the endpoint-id header and the service ID passed as the subdomain curl http://anvil.localhost:3001/v1 \ -X POST \ - -H "Content-Type: application/json" \ -H "endpoint-id: endpoint_3_no_auth" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' @@ -30,7 +31,6 @@ test_request_no_auth_header: ## Test request with no auth, endpoint ID passed in test_request_static_key_auth: ## Test request with static key auth, endpoint ID passed in the endpoint-id header and the service ID passed as the subdomain curl http://anvil.localhost:3001/v1 \ -X POST \ - -H "Content-Type: application/json" \ -H "endpoint-id: endpoint_1_static_key" \ -H "authorization: api_key_1" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' @@ -39,6 +39,14 @@ test_request_static_key_auth: ## Test request with static key auth, endpoint ID test_request_service_id_header: ## Test request with the service ID passed in the target-service-id header, no auth and the endpoint ID passed in the URL path curl http://localhost:3001/v1/endpoint_3_no_auth \ -X POST \ - -H "Content-Type: application/json" \ + -H "target-service-id: anvil" \ + -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' + +.PHONY: test_request_all_headers +test_request_all_headers: ## Test request with all possible values passed as headers: service ID, endpoint ID and authorization + curl http://localhost:3001/v1 \ + -X POST \ + -H "endpoint-id: endpoint_1_static_key" \ + -H "authorization: api_key_1" \ -H "target-service-id: anvil" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' From d8a45954cba5ab5a1738bbb14391e39715fe4cc0 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 13:27:02 +0000 Subject: [PATCH 20/48] chore: set default in ext_authz k8s file to url_path --- local/kubernetes/envoy-ext-authz.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/local/kubernetes/envoy-ext-authz.yaml b/local/kubernetes/envoy-ext-authz.yaml index ee78f8a4..1634ded6 100644 --- a/local/kubernetes/envoy-ext-authz.yaml +++ b/local/kubernetes/envoy-ext-authz.yaml @@ -15,6 +15,7 @@ spec: containers: - name: ext-authz image: ext-authz:latest + # TODO_MVP(@commoddity): get these values from PATH's .config.yaml file. env: # REQUIRED: The host and port for the remote gRPC server connection # that provides the GatewayEndpoint data for the auth server. From 0b15e1dabad6024451ecf097b75d7d2b44d4fa17 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 13:27:02 +0000 Subject: [PATCH 21/48] chore: set default in ext_authz k8s file to url_path --- local/kubernetes/envoy-ext-authz.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/local/kubernetes/envoy-ext-authz.yaml b/local/kubernetes/envoy-ext-authz.yaml index ee78f8a4..2479628d 100644 --- a/local/kubernetes/envoy-ext-authz.yaml +++ b/local/kubernetes/envoy-ext-authz.yaml @@ -15,6 +15,7 @@ spec: containers: - name: ext-authz image: ext-authz:latest + # TODO_MVP(@commoddity): get these values from PATH's .config.yaml file. env: # REQUIRED: The host and port for the remote gRPC server connection # that provides the GatewayEndpoint data for the auth server. @@ -29,7 +30,7 @@ spec: # Options are "url_path" or "header". # Default is "url_path" if not set. - name: ENDPOINT_ID_EXTRACTOR - value: "header" + value: "url_path" ports: - containerPort: 10003 --- From 94e016aef696d26686a4cf754e57da73153bde9d Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 20:02:47 +0000 Subject: [PATCH 22/48] feat: add allowed-services file for handling which services are allowed by PATH --- .gitignore | 1 + Makefile | 2 +- Tiltfile | 13 ++- cmd/main.go | 2 +- config/config.go | 48 ---------- config/config.schema.yaml | 5 +- config/config_test.go | 87 +------------------ config/examples/config.morse_example.yaml | 2 - config/examples/config.shannon_example.yaml | 10 --- docusaurus/docs/develop/envoy/introduction.md | 52 +++++++++-- e2e/morse_relay_test.go | 33 ++++--- e2e/shannon_relay_test.go | 6 +- envoy/allowed-services.template.lua | 16 ++++ envoy/envoy.template.yaml | 50 ++++++++--- envoy/scripts/copy_envoy_config.sh | 44 ++++------ local/kubernetes/envoy-proxy.yaml | 6 ++ request/parser.go | 45 ++-------- 17 files changed, 160 insertions(+), 262 deletions(-) create mode 100644 envoy/allowed-services.template.lua diff --git a/.gitignore b/.gitignore index 13801d5d..29446559 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ go.work.sum # Config .*.yaml +.*.lua .config.yaml .config.*.yaml !.config.example.yaml diff --git a/Makefile b/Makefile index d06f1aa2..0d0bbac1 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ path_down: localnet_down ## Tears down local Tilt development environment which ######################### .PHONY: test_all ## Run all tests -test_all: test_unit test_auth_plugin test_e2e_shannon_relay test_e2e_morse_relay +test_all: test_unit test_auth_server test_e2e_shannon_relay test_e2e_morse_relay .PHONY: test_unit test_unit: ## Run all unit tests diff --git a/Tiltfile b/Tiltfile index 09d75fef..c6ba32e0 100644 --- a/Tiltfile +++ b/Tiltfile @@ -119,7 +119,14 @@ if MODE == "path_with_auth": # Import Envoy Auth configuration file into Kubernetes ConfigMaps configmap_create( - "envoy-config", from_file="./local/path/envoy/.envoy.yaml", watch=True + "envoy-config", + from_file="./local/path/envoy/.envoy.yaml", + watch=True, + ) + configmap_create( + "allowed-services", + from_file="./local/path/envoy/.allowed-services.lua", + watch=True, ) configmap_create( "gateway-endpoints", @@ -127,7 +134,9 @@ if MODE == "path_with_auth": watch=True, ) configmap_create( - "ratelimit-config", from_file="./local/path/envoy/.ratelimit.yaml", watch=True + "ratelimit-config", + from_file="./local/path/envoy/.ratelimit.yaml", + watch=True, ) # 1. Build the External Authorization Server image from envoy/auth_server/Dockerfile diff --git a/cmd/main.go b/cmd/main.go index 1e51b84e..44b6d2f1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -58,7 +58,7 @@ func main() { log.Fatalf("failed to setup endpoint hydrator: %v", err) } - requestParser, err := request.NewParser(config, gatewayQoSInstances, logger) + requestParser, err := request.NewParser(gatewayQoSInstances, logger) if err != nil { log.Fatalf("failed to create request parser: %v", err) } diff --git a/config/config.go b/config/config.go index 9c873059..5320cd87 100644 --- a/config/config.go +++ b/config/config.go @@ -10,7 +10,6 @@ import ( "github.com/buildwithgrove/path/config/morse" "github.com/buildwithgrove/path/config/shannon" - "github.com/buildwithgrove/path/config/utils" "github.com/buildwithgrove/path/protocol" ) @@ -29,12 +28,8 @@ type ( Router RouterConfig `yaml:"router_config"` HydratorConfig EndpointHydratorConfig `yaml:"hydrator_config"` MessagingConfig MessagingConfig `yaml:"messaging_config"` - - // A map from human readable aliases (e.g. eth-mainnet) to service ID (e.g. 0021) - serviceAliases map[string]protocol.ServiceID } ServiceConfig struct { - Alias string `yaml:"alias"` RequestTimeout time.Duration `yaml:"request_timeout"` } ) @@ -53,9 +48,6 @@ func LoadGatewayConfigFromYAML(path string) (GatewayConfig, error) { } // hydrate required fields and set defaults for optional fields - if err := config.hydrateServiceAliases(); err != nil { - return GatewayConfig{}, err - } config.hydrateRouterConfig() return config, config.validate() @@ -75,17 +67,6 @@ func (c GatewayConfig) GetRouterConfig() RouterConfig { return c.Router } -// GetServiceIDFromAlias retrieves the ServiceID associated with a given service alias. -// -// This method allows for the use of a user-friendly string service alias in the -// URL subdomain, enabling more user-friendly URLs. For example, instead of -// using a ServiceID like "F00C", an alias such as "eth" can be used, -// resulting in a URL like "eth.rpc.gateway.io" instead of "F00C.rpc.gateway.io". -func (c GatewayConfig) GetServiceIDFromAlias(alias string) (protocol.ServiceID, bool) { - serviceID, ok := c.serviceAliases[alias] - return serviceID, ok -} - // GetEnabledServiceIDs() returns the list of enabled service IDs. func (c GatewayConfig) GetEnabledServiceIDs() []protocol.ServiceID { var enabledServices []protocol.ServiceID @@ -97,21 +78,6 @@ func (c GatewayConfig) GetEnabledServiceIDs() []protocol.ServiceID { /* --------------------------------- Gateway Config Hydration Helpers -------------------------------- */ -func (c *GatewayConfig) hydrateServiceAliases() error { - if c.serviceAliases == nil { - c.serviceAliases = make(map[string]protocol.ServiceID) - } - for serviceID, service := range c.Services { - if service.Alias != "" { - if _, ok := c.serviceAliases[service.Alias]; ok { - return fmt.Errorf("duplicate service alias: %s", service.Alias) - } - c.serviceAliases[service.Alias] = serviceID - } - } - return nil -} - func (c *GatewayConfig) hydrateRouterConfig() { c.Router.hydrateRouterDefaults() } @@ -125,7 +91,6 @@ func (c GatewayConfig) validate() error { if err := c.validateServiceConfig(); err != nil { return err } - return nil } @@ -148,18 +113,5 @@ func (c GatewayConfig) validateServiceConfig() error { if len(c.Services) == 0 { return fmt.Errorf("at least one service must be configured") } - - for _, service := range c.Services { - if service.Alias != "" { - if !utils.IsValidSubdomain(service.Alias) { - return fmt.Errorf("invalid service alias %s: must be a valid URL subdomain", service.Alias) - } - } - if err := c.validateProtocolConfig(); err != nil { - return err - } - - } - return nil } diff --git a/config/config.schema.yaml b/config/config.schema.yaml index b918deb7..193a5ec9 100644 --- a/config/config.schema.yaml +++ b/config/config.schema.yaml @@ -182,13 +182,10 @@ properties: additionalProperties: false patternProperties: "^[a-zA-Z0-9]+$": - description: "Configuration for a service, containing its unique service ID and string alias." + description: "Configuration for a service, containing its unique service ID and other optional configuration." type: object additionalProperties: false properties: - alias: - type: string - description: "Alias for the service." request_timeout: type: string description: "Timeout duration for service requests." diff --git a/config/config_test.go b/config/config_test.go index d793a2ab..dc567667 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -46,7 +46,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { }, Services: map[protocol.ServiceID]ServiceConfig{ "F00C": { - Alias: "eth", RequestTimeout: 3_000 * time.Millisecond, }, "0021": { @@ -63,9 +62,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { HydratorConfig: EndpointHydratorConfig{ ServiceIDs: []protocol.ServiceID{"F00C"}, }, - serviceAliases: map[string]protocol.ServiceID{ - "eth": "F00C", - }, }, wantErr: false, }, @@ -93,10 +89,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { "anvil": { RequestTimeout: 3_000 * time.Millisecond, }, - "F00C": { - Alias: "eth", - RequestTimeout: 3_000 * time.Millisecond, - }, }, Router: RouterConfig{ Port: defaultPort, @@ -105,9 +97,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { WriteTimeout: defaultWriteTimeout, IdleTimeout: defaultIdleTimeout, }, - serviceAliases: map[string]protocol.ServiceID{ - "eth": "F00C", - }, }, wantErr: false, }, @@ -150,17 +139,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { `, wantErr: true, }, - { - name: "should return error for invalid service alias", - filePath: "invalid_service_alias.yaml", - yamlData: ` - morse_config: - serviceAliases: - invalid_alias!@#: - id: "0001" - `, - wantErr: true, - }, { name: "should return error for invalid client public key", filePath: "invalid_client_public_key.yaml", @@ -194,12 +172,7 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { morse_config: services: 0001: - id: "0001" - config: - alias: "pokt-mainnet" - fallback_url: "invalid-url" - service_type_override: "REST" - request_timeout_seconds: 30 + request_timeout: 30 `, wantErr: true, }, @@ -213,8 +186,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { http_config: retries: 3 timeout: 5000 - request_config: - retries: 3 relay_signing_key: "gateway-private-key" signed_aats: f9076ec39b2a495883eb59740d566d5fa2e2b222: @@ -236,17 +207,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { yamlData: "invalid_yaml: [", wantErr: true, }, - { - name: "should return error for duplicate service alias", - filePath: "duplicate_service_alias.yaml", - yamlData: ` - morse_config: - serviceAliases: - eth: "0021" - eth: "0022" - `, - wantErr: true, - }, } for _, test := range tests { @@ -270,50 +230,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { } } -func Test_GetServiceIDFromAlias(t *testing.T) { - test := []struct { - name string - config GatewayConfig - alias string - want protocol.ServiceID - ok bool - }{ - { - name: "should return service ID for existing alias", - config: GatewayConfig{ - serviceAliases: map[string]protocol.ServiceID{ - "eth-mainnet": "0021", - }, - }, - alias: "eth-mainnet", - want: "0021", - ok: true, - }, - { - name: "should return false for non-existing alias", - config: GatewayConfig{ - serviceAliases: map[string]protocol.ServiceID{ - "eth-mainnet": "0021", - }, - }, - alias: "btc-mainnet", - want: "", - ok: false, - }, - } - - for _, test := range test { - t.Run(test.name, func(t *testing.T) { - c := require.New(t) - got, ok := test.config.GetServiceIDFromAlias(test.alias) - c.Equal(test.ok, ok) - if ok { - c.Equal(test.want, got) - } - }) - } -} - func compareConfigs(c *require.Assertions, want, got GatewayConfig) { c.Equal(want.Router, got.Router) if want.MorseConfig != nil { @@ -323,7 +239,6 @@ func compareConfigs(c *require.Assertions, want, got GatewayConfig) { if want.ShannonConfig != nil { c.Equal(want.ShannonConfig, got.ShannonConfig) } - c.Equal(want.serviceAliases, got.serviceAliases) } func compareMorseFullNodeConfig(c *require.Assertions, want, got morseprotocol.FullNodeConfig) { diff --git a/config/examples/config.morse_example.yaml b/config/examples/config.morse_example.yaml index 80afc612..5c55bd9e 100644 --- a/config/examples/config.morse_example.yaml +++ b/config/examples/config.morse_example.yaml @@ -35,8 +35,6 @@ morse_config: # All fields are optional but the id is required. services: "F00C": - # do not change service alias: it is used in the Morse E2E tests to identify the target service. - alias: "eth" request_timeout: "3000ms" # "0021" is an example of a service with no further configuration. diff --git a/config/examples/config.shannon_example.yaml b/config/examples/config.shannon_example.yaml index bd260a4f..93897213 100644 --- a/config/examples/config.shannon_example.yaml +++ b/config/examples/config.shannon_example.yaml @@ -41,20 +41,10 @@ services: # "anvil" is a test service alias used in the Shannon E2E tests. # If this config is used for Shannon E2E tests, do not remove or change this service. # This is used for testing a dedicated Ethereum endpoint that is not of any public network. - # Note that this service has no alias, meaning it can be reached only by its service ID. - # eg. https://anvil.path.grove.city "anvil": {} - # "F00C" is the service ID for the Ethereum MainNet Archival. - # Note that this service has an alias, meaning it can be reached - # both by its service ID and by its alias. - # eg. https://F00C.path.grove.city and https://eth.path.grove.city. - "F00C": - alias: "eth" - # To enable endpoint hydrator to run QoS checks against # service endpoints, add the following to the config: # hydrator_config: # service_ids: # - "anvil" -# - "F00C" diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index d98e35d0..019db405 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -26,6 +26,7 @@ title: Introduction - [API Key Authorization](#api-key-authorization) - [No Authorization](#no-authorization) - [Service ID Specification](#service-id-specification) + - [Allowed Services File](#allowed-services-file) - [Target Service ID Header](#target-service-id-header) - [URL Subdomain](#url-subdomain) - [External Authorization Server](#external-authorization-server) @@ -54,10 +55,13 @@ title: Introduction 2. Run `make init_envoy` to create all the required config files - - `envoy.yaml` is created with your auth provider's domain and audience. - - `auth_server/.env` is created with the host and port of the provided remote gRPC server. - - `gateway-endpoints.yaml` is created from the example file in the [PADS Repository](https://github.com/buildwithgrove/path-auth-data-server/tree/main/yaml/testdata). + - `.envoy.yaml` is created with your auth provider's domain and audience. + - `.allowed-services.lua` is created with the service IDs allowed by the PATH instance. + - ℹ️ _Please update `allowed-services.lua` with the service IDs allowed by your PATH instance._ + - For more details, see the [Allowed Services Map](#allowed-services-map) section. + - `.gateway-endpoints.yaml` is created from the example file in the [PADS Repository](https://github.com/buildwithgrove/path-auth-data-server/tree/main/yaml/testdata). - ℹ️ _Please update `gateway-endpoints.yaml` with your own data._ + - For more details, see the [Gateway Endpoint YAML File](#gateway-endpoint-yaml-file) section. 3. Run `make path_up` to start the services with all auth and rate limiting dependencies. @@ -373,18 +377,36 @@ There are two methods for specifying this header in the request: 1. [Target Service ID Header](#target-service-id-header) 2. [URL Subdomain](#url-subdomain) -3. -:::tip -The `make init_envoy` command will prompt you about which service ID specification method to use. +### Allowed Services File + + +The file `local/path/envoy/.allowed-services.lua` defines the mapping of service IDs to the service IDs used by the PATH service. + +All service IDs (and optional service aliases) used by the PATH service must be defined in this file. + +:::info + +_Example `.allowed-services.lua` file:_ + +```lua +return { + F00C = "F00C", -- Ethereum Service (Authoritative ID) + eth = "F00C", -- Ethereum Service (Alias) + anvil = "anvil", -- Anvil Service (Authoritative ID) +} +``` ::: ### Target Service ID Header -The `target-service-id` header is set directly on the request. +The service ID (or a configured alias) may be specified in the `target-service-id` header. + +:::info _Example request:_ + ```bash curl http://localhost:3001/v1 \ -X POST \ @@ -394,12 +416,16 @@ curl http://localhost:3001/v1 \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` +::: + ### URL Subdomain -The `target-service-id` header is set by the `lua` HTTP filter in the Envoy configuration file from the subdomain of the request's host field. +The service ID (or a configured alias) may be specified in the URL subdomain. eg. `host = "anvil.path.grove.city" -> Header: "target-service-id: anvil"` +:::info + _Example request:_ ```bash curl http://anvil.localhost:3001/v1 \ @@ -409,6 +435,8 @@ curl http://anvil.localhost:3001/v1 \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` +::: + ## External Authorization Server :::info @@ -500,8 +528,9 @@ If the Gateway Operator wishes to implement a custom remote gRPC server, see the #### Gateway Endpoint YAML File -_`PADS` loads data from the Gateway Endpoints YAML file specified by the `YAML_FILEPATH` environment variable._ +_`PADS` loads data from the Gateway Endpoints YAML file specified by the `YAML_FILEPATH` environment variable._\ +:::info [An example `gateway-endpoints.yaml` file may be seen in the PADS repo](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml). The yaml file below provides an example for a particular gateway operator where: @@ -535,9 +564,12 @@ endpoints: ``` :::tip + The PADS repo also provides a [YAML schema for the `gateway-endpoints.yaml` file](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/gateway-endpoints.schema.yaml), which can be used to validate the configuration. + ::: + #### Implementing a Custom Remote gRPC Server If the Gateway operator wishes to implement a custom remote gRPC server, the implementation must import the Go `github.com/buildwithgrove/path/envoy/auth_server/proto` package, which is autogenerated from the [`gateway_endpoint.proto`](https://github.com/buildwithgrove/path/blob/main/envoy/auth_server/proto/gateway_endpoint.proto) file. @@ -548,7 +580,9 @@ The custom implementation must use the methods defined in the `GatewayEndpoints` - `StreamAuthDataUpdates` :::tip + If you wish to implement your own custom database driver, forking the PADS repo is the easiest way to get started, though any gRPC server implementation that adheres to the `gateway_endpoint.proto` service definition should suffice. + ::: ## Rate Limiter diff --git a/e2e/morse_relay_test.go b/e2e/morse_relay_test.go index 51f0fc89..bb4e364c 100644 --- a/e2e/morse_relay_test.go +++ b/e2e/morse_relay_test.go @@ -24,26 +24,25 @@ func Test_MorseRelay(t *testing.T) { defer teardownFn() tests := []struct { - name string - reqMethod string - serviceID string - serviceAlias string - relayID string - body string + name string + reqMethod string + serviceID string + relayID string + body string }{ { - name: "should successfully relay eth_chainId for eth (F00C)", - reqMethod: http.MethodPost, - serviceAlias: "eth", - relayID: "1201", - body: `{"jsonrpc": "2.0", "id": "1201", "method": "eth_chainId"}`, + name: "should successfully relay eth_chainId for eth (F00C)", + reqMethod: http.MethodPost, + serviceID: "F00C", + relayID: "1201", + body: `{"jsonrpc": "2.0", "id": "1201", "method": "eth_chainId"}`, }, { - name: "should successfully relay eth_blockNumber for eth (F00C)", - reqMethod: http.MethodPost, - serviceAlias: "eth", - relayID: "1202", - body: `{"jsonrpc": "2.0", "id": "1202", "method": "eth_blockNumber"}`, + name: "should successfully relay eth_blockNumber for eth (F00C)", + reqMethod: http.MethodPost, + serviceID: "F00C", + relayID: "1202", + body: `{"jsonrpc": "2.0", "id": "1202", "method": "eth_blockNumber"}`, }, // TODO_UPNEXT(@adshmh): add more test cases with valid and invalid jsonrpc request payloads. @@ -60,7 +59,7 @@ func Test_MorseRelay(t *testing.T) { c := require.New(t) // eg. fullURL = "http://test-service.localdev.me:55006/v1" - fullURL := fmt.Sprintf("http://%s.%s:%s%s", test.serviceAlias, localdevMe, pathContainerPort, reqPath) + fullURL := fmt.Sprintf("http://%s.%s:%s%s", test.serviceID, localdevMe, pathContainerPort, reqPath) client := &http.Client{} diff --git a/e2e/shannon_relay_test.go b/e2e/shannon_relay_test.go index 0b6077a2..c0e67e8c 100644 --- a/e2e/shannon_relay_test.go +++ b/e2e/shannon_relay_test.go @@ -28,7 +28,6 @@ func Test_ShannonRelay(t *testing.T) { tests := []struct { name string reqMethod string - serviceID string relayID string body string }{ @@ -56,8 +55,8 @@ func Test_ShannonRelay(t *testing.T) { t.Run(test.name, func(t *testing.T) { c := require.New(t) - // eg. fullURL = "http://anvil.localdev.me:55006/v1/abcdef12" - fullURL := fmt.Sprintf("http://%s.%s:%s%s", serviceID, localdevMe, pathContainerPort, reqPath) + // eg. fullURL = "http://localdev.me:55006/v1/abcdef12" + fullURL := fmt.Sprintf("http://%s:%s%s", localdevMe, pathContainerPort, reqPath) client := &http.Client{} @@ -65,6 +64,7 @@ func Test_ShannonRelay(t *testing.T) { req, err := http.NewRequest(test.reqMethod, fullURL, bytes.NewBuffer([]byte(test.body))) c.NoError(err) req.Header.Set("Content-Type", "application/json") + req.Header.Set("target-service-id", serviceID) var success bool var allErrors []error diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua new file mode 100644 index 00000000..70176710 --- /dev/null +++ b/envoy/allowed-services.template.lua @@ -0,0 +1,16 @@ +-- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. +-- +-- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. +-- +-- eg. the alias "eth = F00C" enables the URL "http://eth.path.grove.city" to be routed to the service with the ID "F00C". +-- +-- For the service to utilize PATH's Quality of Service (QoS) features, the service ID value must match the values defined in +-- PATH's `qos` module. TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module. +return { + -- Morse Service IDs + F00C = "F00C", -- Ethereum Service (Authoritative ID) + eth = "F00C", -- Ethereum Service (Alias) + + -- Shannon Service IDs + anvil = "anvil", -- Anvil Service (Authoritative ID) + } diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 1d8269e3..49eb5d42 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -90,15 +90,20 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog http_filters: - # Extracts the Service ID from the subdomain of the request's host field and attaches it - # to the request as the `target-service-id` header. - # This will require the subdomain to be specified in the request's host field; otherwise - # the request will be rejected. - # eg. host = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" - # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter + # Extracts the Service ID from either the subdomain of the request's host field or the + # `target-service-id` header and attaches it to the request as the `target-service-id` header. + # + # This will require the service ID to be specified in the request's host field or a valid + # `target-service-id` header; otherwise, the request will be rejected. + # + # eg 1. host = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" + # eg 2. header = "target-service-id: anvil" will be transformed to Header: "target-service-id: anvil" + # + # IMPORTANT: The `.allowed-services.lua` file must contain all service IDs allowed by the PATH instance. + # Only requests for services defined in `.allowed-services.lua` will be forwarded to PATH; + # otherwise, the request will be rejected. # - # DEV_NOTE: If the Gateway Operator wishes to specify the Service ID using the - # `target-service-id` header, this filter will be removed from the configuration file. + # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter - name: envoy.filters.http.lua typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua @@ -106,8 +111,33 @@ static_resources: function envoy_on_request(handle) local host = handle:headers():get(":authority") local subdomain = string.match(host, "^([^.]+)") - if subdomain ~= nil and subdomain ~= "" then - handle:headers():add("target-service-id", subdomain) + local target_service_id_header = handle:headers():get("target-service-id") + + -- Load the mapping of subdomains to service IDs from an external file + local subdomain_to_service_id = dofile("/etc/envoy/.allowed-services.lua") + + -- Function to resolve service ID from a given key + local function resolve_service_id(key) + return subdomain_to_service_id[key] + end + + -- Attempt to resolve service ID from subdomain + local service_id = resolve_service_id(subdomain) + + -- If not found, attempt to resolve from "target-service-id" header + if not service_id and target_service_id_header then + service_id = resolve_service_id(target_service_id_header) + end + + if service_id then + -- Update the "target-service-id" header with the resolved service ID + handle:headers():replace("target-service-id", service_id) + else + -- Reject the request if the service ID is not found in both subdomain and header + handle:respond( + {[":status"] = "404"}, + "Not Found: No valid service ID found for subdomain or 'target-service-id' header" + ) end end # Removes the `jwt-user-id` header before forwarding the request to the external authorization filter. diff --git a/envoy/scripts/copy_envoy_config.sh b/envoy/scripts/copy_envoy_config.sh index acb30cd7..81ba1a38 100755 --- a/envoy/scripts/copy_envoy_config.sh +++ b/envoy/scripts/copy_envoy_config.sh @@ -9,14 +9,17 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Source file names ENVOY_TEMPLATE_FILE_NAME="envoy.template.yaml" ENVOY_RATELIMIT_TEMPLATE_FILE_NAME="ratelimit.template.yaml" +ALLOWED_SERVICES_TEMPLATE_FILE_NAME="allowed-services.template.lua" # Destination file names ENVOY_FILE_NAME=".envoy.yaml" ENVOY_RATELIMIT_FILE_NAME=".ratelimit.yaml" +ALLOWED_SERVICES_FILE_NAME=".allowed-services.lua" # Define the absolute paths ENVOY_CONFIG_PATH=$(realpath "$SCRIPT_DIR/../../local/path/envoy/$ENVOY_FILE_NAME") RATELIMIT_CONFIG_PATH=$(realpath "$SCRIPT_DIR/../../local/path/envoy/$ENVOY_RATELIMIT_FILE_NAME") +ALLOWED_SERVICES_PATH=$(realpath "$SCRIPT_DIR/../../local/path/envoy/$ALLOWED_SERVICES_FILE_NAME") # Function to handle envoy.yaml creation create_envoy_config() { @@ -54,14 +57,6 @@ create_envoy_config() { echo "🔑 JWT Authorization is disabled" fi - # Prompt the user for Service ID specification method - prompt_service_id_method - - # If the user selects the 'target-service-id' header, remove the Lua filter - if [[ "$SERVICE_ID_METHOD" == "1" ]]; then - yq eval 'del(.static_resources.listeners[].filter_chains[].filters[].typed_config.http_filters[] | select(.name == "envoy.filters.http.lua"))' -i "$ENVOY_CONFIG_PATH" - fi - echo "✅ $ENVOY_FILE_NAME has been created at $ENVOY_CONFIG_PATH" fi } @@ -77,6 +72,17 @@ create_ratelimit_config() { fi } +# Function to handle allowed-services.lua creation +create_allowed_services_config() { + # Check if allowed-services.lua exists + if [ -f "$ALLOWED_SERVICES_PATH" ]; then + echo "💡 $ALLOWED_SERVICES_PATH already exists, not overwriting." + else + cp "$SCRIPT_DIR/../$ALLOWED_SERVICES_TEMPLATE_FILE_NAME" "$ALLOWED_SERVICES_PATH" + echo "✅ $ALLOWED_SERVICES_FILE_NAME has been created at $ALLOWED_SERVICES_PATH" + fi +} + # Function to prompt the user for OAuth usage prompt_oauth_usage() { while true; do @@ -94,27 +100,7 @@ prompt_oauth_usage() { done } -# Function to prompt the user for Service ID specification method -prompt_service_id_method() { - while true; do - echo "🔧 Configure Service ID Specification Method:" - echo " 1️⃣ As the 'target-service-id' header" - echo " e.g., Header: 'target-service-id: anvil' -> Service ID: 'anvil'" - echo " 2️⃣ As the URL subdomain" - echo " e.g., http://anvil.path.grove.city/v1 -> Service ID: 'anvil'" - read -p "👉 Select an option (1 or 2): " SERVICE_ID_METHOD - if [[ "$SERVICE_ID_METHOD" == "1" ]]; then - echo "ℹ️ Service ID will be determined from the 'target-service-id' header." - return 0 - elif [[ "$SERVICE_ID_METHOD" == "2" ]]; then - echo "ℹ️ Service ID will be determined from the URL subdomain." - return 1 - else - echo "❌ Invalid selection. Please enter '1' or '2'." - fi - done -} - # Execute the functions create_envoy_config create_ratelimit_config +create_allowed_services_config diff --git a/local/kubernetes/envoy-proxy.yaml b/local/kubernetes/envoy-proxy.yaml index 8afb1ebe..63c4ed1f 100644 --- a/local/kubernetes/envoy-proxy.yaml +++ b/local/kubernetes/envoy-proxy.yaml @@ -23,12 +23,18 @@ spec: - name: envoy-config mountPath: /etc/envoy/.envoy.yaml subPath: .envoy.yaml + - name: allowed-services + mountPath: /etc/envoy/.allowed-services.lua + subPath: .allowed-services.lua ports: - containerPort: 3001 volumes: - name: envoy-config configMap: name: envoy-config + - name: allowed-services + configMap: + name: allowed-services --- apiVersion: v1 kind: Service diff --git a/request/parser.go b/request/parser.go index b8cb7abb..8090de74 100644 --- a/request/parser.go +++ b/request/parser.go @@ -14,7 +14,6 @@ import ( "errors" "fmt" "net/http" - "strings" "github.com/pokt-network/poktroll/pkg/polylog" @@ -29,18 +28,13 @@ const HTTPHeaderTargetServiceID = "target-service-id" type ( Parser struct { - Backend Backend QoSServices map[protocol.ServiceID]gateway.QoSService Logger polylog.Logger } - Backend interface { - GetServiceIDFromAlias(string) (protocol.ServiceID, bool) - } ) -func NewParser(backend Backend, enabledServices map[protocol.ServiceID]gateway.QoSService, logger polylog.Logger) (*Parser, error) { +func NewParser(enabledServices map[protocol.ServiceID]gateway.QoSService, logger polylog.Logger) (*Parser, error) { return &Parser{ - Backend: backend, QoSServices: enabledServices, Logger: logger, }, nil @@ -70,41 +64,12 @@ func (p *Parser) GetHTTPErrorResponse(ctx context.Context, err error) gateway.HT return &parserErrorResponse{err: err.Error(), code: http.StatusNotFound} } -// TODO_TECHDEBT(@commoddity): Remove the ability to set the target service ID using the subdomain and -// instead enforce the use of the HTTP Header 'target-service-id'. Envoy Proxy can handle setting the -// header from the subdomain to continue supporting URLs like 'eth.path.grove.city'. -// -// getServiceID extracts the target service ID from the supplied HTTP request. -// As of now, it supports two options for specifying the target service ID, in order of priority: -// -// 1. The value of the HTTP Header target-service-id, if defined. -// e.g. `target-service-id: eth` is interpreted as `eth` target service ID. -// -// 2. The subdomain of the HTTP request's Host field. -// eg. host = "eth.gateway.pokt.network" -> serviceID = "eth" +// getServiceID extracts the target service ID from the HTTP request's headers. func (p *Parser) getServiceID(req *http.Request) (protocol.ServiceID, error) { // Prefer the custom HTTP Header for specification of the Target Service ID - serviceID := req.Header.Get(HTTPHeaderTargetServiceID) - if serviceID != "" { - return p.getServiceIDFromAlias(serviceID), nil - } - - // Fallback to using the HTTP request's host field's domain if the custom HTTP header is not set. - hostParts := strings.Split(req.Host, ".") - if len(hostParts) < 2 { - return "", errNoServiceIDProvided - } - - subdomain := hostParts[0] - return p.getServiceIDFromAlias(subdomain), nil -} - -// TODO_TECHDEBT(@adshmh): consider removing the alias concept altogether: it looks like a DNS/Load Balancer level concept rather than a gateway feature. -// getServiceIDFromAlias returns the service ID for the supplied alias. The serviceAlias is returned as-is if no matching service IDs are found. -func (p *Parser) getServiceIDFromAlias(serviceAlias string) protocol.ServiceID { - if serviceIDFromAlias, ok := p.Backend.GetServiceIDFromAlias(serviceAlias); ok { - return serviceIDFromAlias + if serviceID := req.Header.Get(HTTPHeaderTargetServiceID); serviceID != "" { + return protocol.ServiceID(serviceID), nil } - return protocol.ServiceID(serviceAlias) + return "", errNoServiceIDProvided } From ba159fdb85ff1031d83b9255ea1381e2a1b9f1fe Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 20:47:41 +0000 Subject: [PATCH 23/48] chore: add morse chains and simple aliases to allowed services template file --- envoy/allowed-services.template.lua | 145 ++++++++++++++++++++++++++-- envoy/envoy.template.yaml | 5 +- 2 files changed, 139 insertions(+), 11 deletions(-) diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua index 70176710..bdbd5f1f 100644 --- a/envoy/allowed-services.template.lua +++ b/envoy/allowed-services.template.lua @@ -1,16 +1,143 @@ -- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. +-- The service IDs configured here are used in the `envoy.filters.http.lua` HTTP filter defined in `.envoy.yaml` config file. -- -- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. -- -- eg. the alias "eth = F00C" enables the URL "http://eth.path.grove.city" to be routed to the service with the ID "F00C". -- --- For the service to utilize PATH's Quality of Service (QoS) features, the service ID value must match the values defined in --- PATH's `qos` module. TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module. +-- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. +-- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. return { - -- Morse Service IDs - F00C = "F00C", -- Ethereum Service (Authoritative ID) - eth = "F00C", -- Ethereum Service (Alias) - - -- Shannon Service IDs - anvil = "anvil", -- Anvil Service (Authoritative ID) - } + -- 1. Shannon Service IDs + anvil = "anvil", -- Anvil (Authoritative ID) + + -- 2. Morse Service IDs + F000 = "F000", -- Pocket (Authoritative ID) + pocket = "F000", -- Pocket (Alias) + + F001 = "F001", -- Arbitrum One (Authoritative ID) + arbitrum-one = "F001", -- Arbitrum One (Alias) + + F002 = "F002", -- Arbitrum Sepolia Testnet (Authoritative ID) + arbitrum-sepolia-testnet = "F002", -- Arbitrum Sepolia Testnet (Alias) + + F003 = "F003", -- AVAX (Authoritative ID) + avax = "F003", -- AVAX (Alias) + + F004 = "F004", -- AVAX-DFK (Authoritative ID) + avax-dfk = "F004", -- AVAX-DFK (Alias) + + F005 = "F005", -- Base (Authoritative ID) + base = "F005", -- Base (Alias) + + F006 = "F006", -- Base Testnet (Authoritative ID) + base-testnet = "F006", -- Base Testnet (Alias) + + F008 = "F008", -- Blast (Authoritative ID) + blast = "F008", -- Blast (Alias) + + F009 = "F009", -- Binance Smart Chain (Authoritative ID) + bsc = "F009", -- Binance Smart Chain (Alias) + + F00A = "F00A", -- Boba (Authoritative ID) + boba = "F00A", -- Boba (Alias) + + F00B = "F00B", -- Celo (Authoritative ID) + celo = "F00B", -- Celo (Alias) + + F00C = "F00C", -- Ethereum (Authoritative ID) + eth = "F00C", -- Ethereum (Alias) + + F00D = "F00D", -- Ethereum Holesky Testnet (Authoritative ID) + eth-holesky-testnet = "F00D", -- Ethereum Holesky Testnet (Alias) + + F00E = "F00E", -- Ethereum Sepolia Testnet (Authoritative ID) + sepolia = "F00E", -- Ethereum Sepolia Testnet (Alias) + + F00F = "F00F", -- Evmos (Authoritative ID) + evmos = "F00F", -- Evmos (Alias) + + F010 = "F010", -- Fantom (Authoritative ID) + fantom = "F010", -- Fantom (Alias) + + F011 = "F011", -- Fraxtal (Authoritative ID) + fraxtal = "F011", -- Fraxtal (Alias) + + F012 = "F012", -- Fuse (Authoritative ID) + fuse = "F012", -- Fuse (Alias) + + F013 = "F013", -- Gnosis (Authoritative ID) + gnosis = "F013", -- Gnosis (Alias) + + F014 = "F014", -- Harmony (Authoritative ID) + harmony = "F014", -- Harmony (Alias) + + F015 = "F015", -- Iotex (Authoritative ID) + iotex = "F015", -- Iotex (Alias) + + F016 = "F016", -- Kaia (Authoritative ID) + kaia = "F016", -- Kaia (Alias) + + F017 = "F017", -- Kava (Authoritative ID) + kava = "F017", -- Kava (Alias) + + F018 = "F018", -- Metis (Authoritative ID) + metis = "F018", -- Metis (Alias) + + F019 = "F019", -- Moonbeam (Authoritative ID) + moonbeam = "F019", -- Moonbeam (Alias) + + F01A = "F01A", -- Moonriver (Authoritative ID) + moonriver = "F01A", -- Moonriver (Alias) + + F01B = "F01B", -- Near (Authoritative ID) + near = "F01B", -- Near (Alias) + + F01C = "F01C", -- Oasys (Authoritative ID) + oasys = "F01C", -- Oasys (Alias) + + F01D = "F01D", -- Optimism (Authoritative ID) + optimism = "F01D", -- Optimism (Alias) + + F01E = "F01E", -- Optimism Sepolia Testnet (Authoritative ID) + optimism-sepolia-testnet = "F01E", -- Optimism Sepolia Testnet (Alias) + + F01F = "F01F", -- Opbnb (Authoritative ID) + opbnb = "F01F", -- Opbnb (Alias) + + F020 = "F020", -- Osmosis (Authoritative ID) + osmosis = "F020", -- Osmosis (Alias) + + F021 = "F021", -- Polygon (Authoritative ID) + polygon = "F021", -- Polygon (Alias) + + F022 = "F022", -- Polygon Amoy Testnet (Authoritative ID) + polygon-amoy-testnet = "F022", -- Polygon Amoy Testnet (Alias) + + F023 = "F023", -- Radix (Authoritative ID) + radix = "F023", -- Radix (Alias) + + F024 = "F024", -- Scroll (Authoritative ID) + scroll = "F024", -- Scroll (Alias) + + F025 = "F025", -- Solana (Authoritative ID) + solana = "F025", -- Solana (Alias) + + F026 = "F026", -- Sui (Authoritative ID) + sui = "F026", -- Sui (Alias) + + F027 = "F027", -- Taiko (Authoritative ID) + taiko = "F027", -- Taiko (Alias) + + F028 = "F028", -- Taiko Hekla Testnet (Authoritative ID) + taiko-hekla-testnet = "F028", -- Taiko Hekla Testnet (Alias) + + F029 = "F029", -- Polygon Zkevm (Authoritative ID) + polygon-zkevm = "F029", -- Polygon Zkevm (Alias) + + F02A = "F02A", -- Zklink Nova (Authoritative ID) + zklink-nova = "F02A", -- Zklink Nova (Alias) + + F02B = "F02B", -- Zksync Era (Authoritative ID) + zksync-era = "F02B", -- Zksync Era (Alias) +} diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 49eb5d42..aa29b18c 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -93,11 +93,12 @@ static_resources: # Extracts the Service ID from either the subdomain of the request's host field or the # `target-service-id` header and attaches it to the request as the `target-service-id` header. # - # This will require the service ID to be specified in the request's host field or a valid + # This will require the service ID to be specified in the request's subdomain or a valid # `target-service-id` header; otherwise, the request will be rejected. # - # eg 1. host = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" + # eg 1. subdomain = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" # eg 2. header = "target-service-id: anvil" will be transformed to Header: "target-service-id: anvil" + # eg 3. header (aliased) = "target-service-id: eth" will be transformed to Header: "target-service-id: F00C" # # IMPORTANT: The `.allowed-services.lua` file must contain all service IDs allowed by the PATH instance. # Only requests for services defined in `.allowed-services.lua` will be forwarded to PATH; From 878fa1b280edb6830b8396d8d7e6a6f1a108d3af Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 20:47:41 +0000 Subject: [PATCH 24/48] chore: add morse chains and simple aliases to allowed services template file --- envoy/allowed-services.template.lua | 148 ++++++++++++++++++++++++++-- envoy/envoy.template.yaml | 5 +- 2 files changed, 141 insertions(+), 12 deletions(-) diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua index 70176710..82f8ee52 100644 --- a/envoy/allowed-services.template.lua +++ b/envoy/allowed-services.template.lua @@ -1,16 +1,144 @@ -- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. +-- The service IDs configured here are used in the `envoy.filters.http.lua` HTTP filter defined in `.envoy.yaml` config file. -- -- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. -- --- eg. the alias "eth = F00C" enables the URL "http://eth.path.grove.city" to be routed to the service with the ID "F00C". +-- eg 1. the service ID "F000 = F000" enables Envoy to forward requests with the subdomain "F000.path.grove.city" to PATH with the service ID "F000". +-- eg 2. the alias "pocket = F000" enables Envoy to forward requests with the subdomain "pocket.path.grove.city" to PATH with the service ID "F000". -- --- For the service to utilize PATH's Quality of Service (QoS) features, the service ID value must match the values defined in --- PATH's `qos` module. TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module. +-- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. +-- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. return { - -- Morse Service IDs - F00C = "F00C", -- Ethereum Service (Authoritative ID) - eth = "F00C", -- Ethereum Service (Alias) - - -- Shannon Service IDs - anvil = "anvil", -- Anvil Service (Authoritative ID) - } + -- 1. Shannon Service IDs + anvil = "anvil", -- Anvil (Authoritative ID) + + -- 2. Morse Service IDs + F000 = "F000", -- Pocket (Authoritative ID) + pocket = "F000", -- Pocket (Alias) + + F001 = "F001", -- Arbitrum One (Authoritative ID) + arbitrum-one = "F001", -- Arbitrum One (Alias) + + F002 = "F002", -- Arbitrum Sepolia Testnet (Authoritative ID) + arbitrum-sepolia-testnet = "F002", -- Arbitrum Sepolia Testnet (Alias) + + F003 = "F003", -- AVAX (Authoritative ID) + avax = "F003", -- AVAX (Alias) + + F004 = "F004", -- AVAX-DFK (Authoritative ID) + avax-dfk = "F004", -- AVAX-DFK (Alias) + + F005 = "F005", -- Base (Authoritative ID) + base = "F005", -- Base (Alias) + + F006 = "F006", -- Base Testnet (Authoritative ID) + base-testnet = "F006", -- Base Testnet (Alias) + + F008 = "F008", -- Blast (Authoritative ID) + blast = "F008", -- Blast (Alias) + + F009 = "F009", -- Binance Smart Chain (Authoritative ID) + bsc = "F009", -- Binance Smart Chain (Alias) + + F00A = "F00A", -- Boba (Authoritative ID) + boba = "F00A", -- Boba (Alias) + + F00B = "F00B", -- Celo (Authoritative ID) + celo = "F00B", -- Celo (Alias) + + F00C = "F00C", -- Ethereum (Authoritative ID) + eth = "F00C", -- Ethereum (Alias) + + F00D = "F00D", -- Ethereum Holesky Testnet (Authoritative ID) + eth-holesky-testnet = "F00D", -- Ethereum Holesky Testnet (Alias) + + F00E = "F00E", -- Ethereum Sepolia Testnet (Authoritative ID) + sepolia = "F00E", -- Ethereum Sepolia Testnet (Alias) + + F00F = "F00F", -- Evmos (Authoritative ID) + evmos = "F00F", -- Evmos (Alias) + + F010 = "F010", -- Fantom (Authoritative ID) + fantom = "F010", -- Fantom (Alias) + + F011 = "F011", -- Fraxtal (Authoritative ID) + fraxtal = "F011", -- Fraxtal (Alias) + + F012 = "F012", -- Fuse (Authoritative ID) + fuse = "F012", -- Fuse (Alias) + + F013 = "F013", -- Gnosis (Authoritative ID) + gnosis = "F013", -- Gnosis (Alias) + + F014 = "F014", -- Harmony (Authoritative ID) + harmony = "F014", -- Harmony (Alias) + + F015 = "F015", -- Iotex (Authoritative ID) + iotex = "F015", -- Iotex (Alias) + + F016 = "F016", -- Kaia (Authoritative ID) + kaia = "F016", -- Kaia (Alias) + + F017 = "F017", -- Kava (Authoritative ID) + kava = "F017", -- Kava (Alias) + + F018 = "F018", -- Metis (Authoritative ID) + metis = "F018", -- Metis (Alias) + + F019 = "F019", -- Moonbeam (Authoritative ID) + moonbeam = "F019", -- Moonbeam (Alias) + + F01A = "F01A", -- Moonriver (Authoritative ID) + moonriver = "F01A", -- Moonriver (Alias) + + F01B = "F01B", -- Near (Authoritative ID) + near = "F01B", -- Near (Alias) + + F01C = "F01C", -- Oasys (Authoritative ID) + oasys = "F01C", -- Oasys (Alias) + + F01D = "F01D", -- Optimism (Authoritative ID) + optimism = "F01D", -- Optimism (Alias) + + F01E = "F01E", -- Optimism Sepolia Testnet (Authoritative ID) + optimism-sepolia-testnet = "F01E", -- Optimism Sepolia Testnet (Alias) + + F01F = "F01F", -- Opbnb (Authoritative ID) + opbnb = "F01F", -- Opbnb (Alias) + + F020 = "F020", -- Osmosis (Authoritative ID) + osmosis = "F020", -- Osmosis (Alias) + + F021 = "F021", -- Polygon (Authoritative ID) + polygon = "F021", -- Polygon (Alias) + + F022 = "F022", -- Polygon Amoy Testnet (Authoritative ID) + polygon-amoy-testnet = "F022", -- Polygon Amoy Testnet (Alias) + + F023 = "F023", -- Radix (Authoritative ID) + radix = "F023", -- Radix (Alias) + + F024 = "F024", -- Scroll (Authoritative ID) + scroll = "F024", -- Scroll (Alias) + + F025 = "F025", -- Solana (Authoritative ID) + solana = "F025", -- Solana (Alias) + + F026 = "F026", -- Sui (Authoritative ID) + sui = "F026", -- Sui (Alias) + + F027 = "F027", -- Taiko (Authoritative ID) + taiko = "F027", -- Taiko (Alias) + + F028 = "F028", -- Taiko Hekla Testnet (Authoritative ID) + taiko-hekla-testnet = "F028", -- Taiko Hekla Testnet (Alias) + + F029 = "F029", -- Polygon Zkevm (Authoritative ID) + polygon-zkevm = "F029", -- Polygon Zkevm (Alias) + + F02A = "F02A", -- Zklink Nova (Authoritative ID) + zklink-nova = "F02A", -- Zklink Nova (Alias) + + F02B = "F02B", -- Zksync Era (Authoritative ID) + zksync-era = "F02B", -- Zksync Era (Alias) +} diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 49eb5d42..aa29b18c 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -93,11 +93,12 @@ static_resources: # Extracts the Service ID from either the subdomain of the request's host field or the # `target-service-id` header and attaches it to the request as the `target-service-id` header. # - # This will require the service ID to be specified in the request's host field or a valid + # This will require the service ID to be specified in the request's subdomain or a valid # `target-service-id` header; otherwise, the request will be rejected. # - # eg 1. host = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" + # eg 1. subdomain = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" # eg 2. header = "target-service-id: anvil" will be transformed to Header: "target-service-id: anvil" + # eg 3. header (aliased) = "target-service-id: eth" will be transformed to Header: "target-service-id: F00C" # # IMPORTANT: The `.allowed-services.lua` file must contain all service IDs allowed by the PATH instance. # Only requests for services defined in `.allowed-services.lua` will be forwarded to PATH; From d5e6a1c38329401c2c9a28789d7da588a52f973c Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 21:05:01 +0000 Subject: [PATCH 25/48] fix: syntax in .lua template file --- envoy/allowed-services.template.lua | 184 ++++++++++++++-------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua index 04451bdd..c545f450 100644 --- a/envoy/allowed-services.template.lua +++ b/envoy/allowed-services.template.lua @@ -4,142 +4,142 @@ -- -- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. -- --- eg 1. the service ID "F000 = F000" enables Envoy to forward requests with the subdomain "F000.path.grove.city" to PATH with the service ID "F000". --- eg 2. the alias "pocket = F000" enables Envoy to forward requests with the subdomain "pocket.path.grove.city" to PATH with the service ID "F000". +-- eg 1. the service ID "F000" = "F000" enables Envoy to forward requests with the subdomain "F000.path.grove.city" to PATH with the service ID "F000". +-- eg 2. the alias "pocket" = "F000" enables Envoy to forward requests with the subdomain "pocket.path.grove.city" to PATH with the service ID "F000". -- --- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. +-- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. -- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. return { - -- 1. Shannon Service IDs - anvil = "anvil", -- Anvil (Authoritative ID) + -- 1. Shannon Service IDs + "anvil" == "anvil", -- Anvil (Authoritative ID) - -- 2. Morse Service IDs - F000 = "F000", -- Pocket (Authoritative ID) - pocket = "F000", -- Pocket (Alias) + -- 2. Morse Service IDs + "F000" == "F000", -- Pocket (Authoritative ID) + "pocket" == "F000", -- Pocket (Alias) - F001 = "F001", -- Arbitrum One (Authoritative ID) - arbitrum-one = "F001", -- Arbitrum One (Alias) + "F001" == "F001", -- Arbitrum One (Authoritative ID) + "arbitrum-one" == "F001", -- Arbitrum One (Alias) - F002 = "F002", -- Arbitrum Sepolia Testnet (Authoritative ID) - arbitrum-sepolia-testnet = "F002", -- Arbitrum Sepolia Testnet (Alias) + "F002" == "F002", -- Arbitrum Sepolia Testnet (Authoritative ID) + "arbitrum-sepolia-testnet" == "F002", -- Arbitrum Sepolia Testnet (Alias) - F003 = "F003", -- AVAX (Authoritative ID) - avax = "F003", -- AVAX (Alias) + "F003" == "F003", -- AVAX (Authoritative ID) + "avax" == "F003", -- AVAX (Alias) - F004 = "F004", -- AVAX-DFK (Authoritative ID) - avax-dfk = "F004", -- AVAX-DFK (Alias) + "F004" == "F004", -- AVAX-DFK (Authoritative ID) + "avax-dfk" == "F004", -- AVAX-DFK (Alias) - F005 = "F005", -- Base (Authoritative ID) - base = "F005", -- Base (Alias) + "F005" == "F005", -- Base (Authoritative ID) + "base" == "F005", -- Base (Alias) - F006 = "F006", -- Base Testnet (Authoritative ID) - base-testnet = "F006", -- Base Testnet (Alias) + "F006" == "F006", -- Base Testnet (Authoritative ID) + "base-testnet" == "F006", -- Base Testnet (Alias) - F008 = "F008", -- Blast (Authoritative ID) - blast = "F008", -- Blast (Alias) + "F008" == "F008", -- Blast (Authoritative ID) + "blast" == "F008", -- Blast (Alias) - F009 = "F009", -- Binance Smart Chain (Authoritative ID) - bsc = "F009", -- Binance Smart Chain (Alias) + "F009" == "F009", -- Binance Smart Chain (Authoritative ID) + "bsc" == "F009", -- Binance Smart Chain (Alias) - F00A = "F00A", -- Boba (Authoritative ID) - boba = "F00A", -- Boba (Alias) + "F00A" == "F00A", -- Boba (Authoritative ID) + "boba" == "F00A", -- Boba (Alias) - F00B = "F00B", -- Celo (Authoritative ID) - celo = "F00B", -- Celo (Alias) + "F00B" == "F00B", -- Celo (Authoritative ID) + "celo" == "F00B", -- Celo (Alias) - F00C = "F00C", -- Ethereum (Authoritative ID) - eth = "F00C", -- Ethereum (Alias) + "F00C" == "F00C", -- Ethereum (Authoritative ID) + "eth" == "F00C", -- Ethereum (Alias) - F00D = "F00D", -- Ethereum Holesky Testnet (Authoritative ID) - eth-holesky-testnet = "F00D", -- Ethereum Holesky Testnet (Alias) + "F00D" == "F00D", -- Ethereum Holesky Testnet (Authoritative ID) + "eth-holesky-testnet" == "F00D", -- Ethereum Holesky Testnet (Alias) - F00E = "F00E", -- Ethereum Sepolia Testnet (Authoritative ID) - sepolia = "F00E", -- Ethereum Sepolia Testnet (Alias) + "F00E" == "F00E", -- Ethereum Sepolia Testnet (Authoritative ID) + "sepolia" == "F00E", -- Ethereum Sepolia Testnet (Alias) - F00F = "F00F", -- Evmos (Authoritative ID) - evmos = "F00F", -- Evmos (Alias) + "F00F" == "F00F", -- Evmos (Authoritative ID) + "evmos" == "F00F", -- Evmos (Alias) - F010 = "F010", -- Fantom (Authoritative ID) - fantom = "F010", -- Fantom (Alias) + "F010" == "F010", -- Fantom (Authoritative ID) + "fantom" == "F010", -- Fantom (Alias) - F011 = "F011", -- Fraxtal (Authoritative ID) - fraxtal = "F011", -- Fraxtal (Alias) + "F011" == "F011", -- Fraxtal (Authoritative ID) + "fraxtal" == "F011", -- Fraxtal (Alias) - F012 = "F012", -- Fuse (Authoritative ID) - fuse = "F012", -- Fuse (Alias) + "F012" == "F012", -- Fuse (Authoritative ID) + "fuse" == "F012", -- Fuse (Alias) - F013 = "F013", -- Gnosis (Authoritative ID) - gnosis = "F013", -- Gnosis (Alias) + "F013" == "F013", -- Gnosis (Authoritative ID) + "gnosis" == "F013", -- Gnosis (Alias) - F014 = "F014", -- Harmony (Authoritative ID) - harmony = "F014", -- Harmony (Alias) + "F014" == "F014", -- Harmony (Authoritative ID) + "harmony" == "F014", -- Harmony (Alias) - F015 = "F015", -- Iotex (Authoritative ID) - iotex = "F015", -- Iotex (Alias) + "F015" == "F015", -- Iotex (Authoritative ID) + "iotex" == "F015", -- Iotex (Alias) - F016 = "F016", -- Kaia (Authoritative ID) - kaia = "F016", -- Kaia (Alias) + "F016" == "F016", -- Kaia (Authoritative ID) + "kaia" == "F016", -- Kaia (Alias) - F017 = "F017", -- Kava (Authoritative ID) - kava = "F017", -- Kava (Alias) + "F017" == "F017", -- Kava (Authoritative ID) + "kava" == "F017", -- Kava (Alias) - F018 = "F018", -- Metis (Authoritative ID) - metis = "F018", -- Metis (Alias) + "F018" == "F018", -- Metis (Authoritative ID) + "metis" == "F018", -- Metis (Alias) - F019 = "F019", -- Moonbeam (Authoritative ID) - moonbeam = "F019", -- Moonbeam (Alias) + "F019" == "F019", -- Moonbeam (Authoritative ID) + "moonbeam" == "F019", -- Moonbeam (Alias) - F01A = "F01A", -- Moonriver (Authoritative ID) - moonriver = "F01A", -- Moonriver (Alias) + "F01A" == "F01A", -- Moonriver (Authoritative ID) + "moonriver" == "F01A", -- Moonriver (Alias) - F01B = "F01B", -- Near (Authoritative ID) - near = "F01B", -- Near (Alias) + "F01B" == "F01B", -- Near (Authoritative ID) + "near" == "F01B", -- Near (Alias) - F01C = "F01C", -- Oasys (Authoritative ID) - oasys = "F01C", -- Oasys (Alias) + "F01C" == "F01C", -- Oasys (Authoritative ID) + "oasys" == "F01C", -- Oasys (Alias) - F01D = "F01D", -- Optimism (Authoritative ID) - optimism = "F01D", -- Optimism (Alias) + "F01D" == "F01D", -- Optimism (Authoritative ID) + "optimism" == "F01D", -- Optimism (Alias) - F01E = "F01E", -- Optimism Sepolia Testnet (Authoritative ID) - optimism-sepolia-testnet = "F01E", -- Optimism Sepolia Testnet (Alias) + "F01E" == "F01E", -- Optimism Sepolia Testnet (Authoritative ID) + "optimism-sepolia-testnet" == "F01E", -- Optimism Sepolia Testnet (Alias) - F01F = "F01F", -- Opbnb (Authoritative ID) - opbnb = "F01F", -- Opbnb (Alias) + "F01F" == "F01F", -- Opbnb (Authoritative ID) + "opbnb" == "F01F", -- Opbnb (Alias) - F020 = "F020", -- Osmosis (Authoritative ID) - osmosis = "F020", -- Osmosis (Alias) + "F020" == "F020", -- Osmosis (Authoritative ID) + "osmosis" == "F020", -- Osmosis (Alias) - F021 = "F021", -- Polygon (Authoritative ID) - polygon = "F021", -- Polygon (Alias) + "F021" == "F021", -- Polygon (Authoritative ID) + "polygon" == "F021", -- Polygon (Alias) - F022 = "F022", -- Polygon Amoy Testnet (Authoritative ID) - polygon-amoy-testnet = "F022", -- Polygon Amoy Testnet (Alias) + "F022" == "F022", -- Polygon Amoy Testnet (Authoritative ID) + "polygon-amoy-testnet" == "F022", -- Polygon Amoy Testnet (Alias) - F023 = "F023", -- Radix (Authoritative ID) - radix = "F023", -- Radix (Alias) + "F023" == "F023", -- Radix (Authoritative ID) + "radix" == "F023", -- Radix (Alias) - F024 = "F024", -- Scroll (Authoritative ID) - scroll = "F024", -- Scroll (Alias) + "F024" == "F024", -- Scroll (Authoritative ID) + "scroll" == "F024", -- Scroll (Alias) - F025 = "F025", -- Solana (Authoritative ID) - solana = "F025", -- Solana (Alias) + "F025" == "F025", -- Solana (Authoritative ID) + "solana" == "F025", -- Solana (Alias) - F026 = "F026", -- Sui (Authoritative ID) - sui = "F026", -- Sui (Alias) + "F026" == "F026", -- Sui (Authoritative ID) + "sui" == "F026", -- Sui (Alias) - F027 = "F027", -- Taiko (Authoritative ID) - taiko = "F027", -- Taiko (Alias) + "F027" == "F027", -- Taiko (Authoritative ID) + "taiko" == "F027", -- Taiko (Alias) - F028 = "F028", -- Taiko Hekla Testnet (Authoritative ID) - taiko-hekla-testnet = "F028", -- Taiko Hekla Testnet (Alias) + "F028" == "F028", -- Taiko Hekla Testnet (Authoritative ID) + "taiko-hekla-testnet" == "F028", -- Taiko Hekla Testnet (Alias) - F029 = "F029", -- Polygon Zkevm (Authoritative ID) - polygon-zkevm = "F029", -- Polygon Zkevm (Alias) + "F029" == "F029", -- Polygon Zkevm (Authoritative ID) + "polygon-zkevm" == "F029", -- Polygon Zkevm (Alias) - F02A = "F02A", -- Zklink Nova (Authoritative ID) - zklink-nova = "F02A", -- Zklink Nova (Alias) + "F02A" == "F02A", -- Zklink Nova (Authoritative ID) + "zklink-nova" == "F02A", -- Zklink Nova (Alias) - F02B = "F02B", -- Zksync Era (Authoritative ID) - zksync-era = "F02B", -- Zksync Era (Alias) + "F02B" == "F02B", -- Zksync Era (Authoritative ID) + "zksync-era" == "F02B", -- Zksync Era (Alias) } From 1b41827f2e1aaa7055f15f180c341d795ee0aeda Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 21:05:01 +0000 Subject: [PATCH 26/48] fix: syntax in .lua template file --- envoy/allowed-services.template.lua | 184 ++++++++++++++-------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua index 04451bdd..ec818197 100644 --- a/envoy/allowed-services.template.lua +++ b/envoy/allowed-services.template.lua @@ -4,142 +4,142 @@ -- -- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. -- --- eg 1. the service ID "F000 = F000" enables Envoy to forward requests with the subdomain "F000.path.grove.city" to PATH with the service ID "F000". --- eg 2. the alias "pocket = F000" enables Envoy to forward requests with the subdomain "pocket.path.grove.city" to PATH with the service ID "F000". +-- eg 1. the service ID ["F000"] = "F000" enables Envoy to forward requests with the subdomain "F000.path.grove.city" to PATH with the service ID "F000". +-- eg 2. the alias ["pocket"] = "F000" enables Envoy to forward requests with the subdomain "pocket.path.grove.city" to PATH with the service ID "F000". -- --- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. +-- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. -- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. return { - -- 1. Shannon Service IDs - anvil = "anvil", -- Anvil (Authoritative ID) + -- 1. Shannon Service IDs + ["anvil"] = "anvil", -- Anvil (Authoritative ID) - -- 2. Morse Service IDs - F000 = "F000", -- Pocket (Authoritative ID) - pocket = "F000", -- Pocket (Alias) + -- 2. Morse Service IDs + ["F000"] = "F000", -- Pocket (Authoritative ID) + ["pocket"] = "F000", -- Pocket (Alias) - F001 = "F001", -- Arbitrum One (Authoritative ID) - arbitrum-one = "F001", -- Arbitrum One (Alias) + ["F001"] = "F001", -- Arbitrum One (Authoritative ID) + ["arbitrum-one"] = "F001", -- Arbitrum One (Alias) - F002 = "F002", -- Arbitrum Sepolia Testnet (Authoritative ID) - arbitrum-sepolia-testnet = "F002", -- Arbitrum Sepolia Testnet (Alias) + ["F002"] = "F002", -- Arbitrum Sepolia Testnet (Authoritative ID) + ["arbitrum-sepolia-testnet"] = "F002", -- Arbitrum Sepolia Testnet (Alias) - F003 = "F003", -- AVAX (Authoritative ID) - avax = "F003", -- AVAX (Alias) + ["F003"] = "F003", -- AVAX (Authoritative ID) + ["avax"] = "F003", -- AVAX (Alias) - F004 = "F004", -- AVAX-DFK (Authoritative ID) - avax-dfk = "F004", -- AVAX-DFK (Alias) + ["F004"] = "F004", -- AVAX-DFK (Authoritative ID) + ["avax-dfk"] = "F004", -- AVAX-DFK (Alias) - F005 = "F005", -- Base (Authoritative ID) - base = "F005", -- Base (Alias) + ["F005"] = "F005", -- Base (Authoritative ID) + ["base"] = "F005", -- Base (Alias) - F006 = "F006", -- Base Testnet (Authoritative ID) - base-testnet = "F006", -- Base Testnet (Alias) + ["F006"] = "F006", -- Base Testnet (Authoritative ID) + ["base-testnet"] = "F006", -- Base Testnet (Alias) - F008 = "F008", -- Blast (Authoritative ID) - blast = "F008", -- Blast (Alias) + ["F008"] = "F008", -- Blast (Authoritative ID) + ["blast"] = "F008", -- Blast (Alias) - F009 = "F009", -- Binance Smart Chain (Authoritative ID) - bsc = "F009", -- Binance Smart Chain (Alias) + ["F009"] = "F009", -- Binance Smart Chain (Authoritative ID) + ["bsc"] = "F009", -- Binance Smart Chain (Alias) - F00A = "F00A", -- Boba (Authoritative ID) - boba = "F00A", -- Boba (Alias) + ["F00A"] = "F00A", -- Boba (Authoritative ID) + ["boba"] = "F00A", -- Boba (Alias) - F00B = "F00B", -- Celo (Authoritative ID) - celo = "F00B", -- Celo (Alias) + ["F00B"] = "F00B", -- Celo (Authoritative ID) + ["celo"] = "F00B", -- Celo (Alias) - F00C = "F00C", -- Ethereum (Authoritative ID) - eth = "F00C", -- Ethereum (Alias) + ["F00C"] = "F00C", -- Ethereum (Authoritative ID) + ["eth"] = "F00C", -- Ethereum (Alias) - F00D = "F00D", -- Ethereum Holesky Testnet (Authoritative ID) - eth-holesky-testnet = "F00D", -- Ethereum Holesky Testnet (Alias) + ["F00D"] = "F00D", -- Ethereum Holesky Testnet (Authoritative ID) + ["eth-holesky-testnet"] = "F00D", -- Ethereum Holesky Testnet (Alias) - F00E = "F00E", -- Ethereum Sepolia Testnet (Authoritative ID) - sepolia = "F00E", -- Ethereum Sepolia Testnet (Alias) + ["F00E"] = "F00E", -- Ethereum Sepolia Testnet (Authoritative ID) + ["sepolia"] = "F00E", -- Ethereum Sepolia Testnet (Alias) - F00F = "F00F", -- Evmos (Authoritative ID) - evmos = "F00F", -- Evmos (Alias) + ["F00F"] = "F00F", -- Evmos (Authoritative ID) + ["evmos"] = "F00F", -- Evmos (Alias) - F010 = "F010", -- Fantom (Authoritative ID) - fantom = "F010", -- Fantom (Alias) + ["F010"] = "F010", -- Fantom (Authoritative ID) + ["fantom"] = "F010", -- Fantom (Alias) - F011 = "F011", -- Fraxtal (Authoritative ID) - fraxtal = "F011", -- Fraxtal (Alias) + ["F011"] = "F011", -- Fraxtal (Authoritative ID) + ["fraxtal"] = "F011", -- Fraxtal (Alias) - F012 = "F012", -- Fuse (Authoritative ID) - fuse = "F012", -- Fuse (Alias) + ["F012"] = "F012", -- Fuse (Authoritative ID) + ["fuse"] = "F012", -- Fuse (Alias) - F013 = "F013", -- Gnosis (Authoritative ID) - gnosis = "F013", -- Gnosis (Alias) + ["F013"] = "F013", -- Gnosis (Authoritative ID) + ["gnosis"] = "F013", -- Gnosis (Alias) - F014 = "F014", -- Harmony (Authoritative ID) - harmony = "F014", -- Harmony (Alias) + ["F014"] = "F014", -- Harmony (Authoritative ID) + ["harmony"] = "F014", -- Harmony (Alias) - F015 = "F015", -- Iotex (Authoritative ID) - iotex = "F015", -- Iotex (Alias) + ["F015"] = "F015", -- Iotex (Authoritative ID) + ["iotex"] = "F015", -- Iotex (Alias) - F016 = "F016", -- Kaia (Authoritative ID) - kaia = "F016", -- Kaia (Alias) + ["F016"] = "F016", -- Kaia (Authoritative ID) + ["kaia"] = "F016", -- Kaia (Alias) - F017 = "F017", -- Kava (Authoritative ID) - kava = "F017", -- Kava (Alias) + ["F017"] = "F017", -- Kava (Authoritative ID) + ["kava"] = "F017", -- Kava (Alias) - F018 = "F018", -- Metis (Authoritative ID) - metis = "F018", -- Metis (Alias) + ["F018"] = "F018", -- Metis (Authoritative ID) + ["metis"] = "F018", -- Metis (Alias) - F019 = "F019", -- Moonbeam (Authoritative ID) - moonbeam = "F019", -- Moonbeam (Alias) + ["F019"] = "F019", -- Moonbeam (Authoritative ID) + ["moonbeam"] = "F019", -- Moonbeam (Alias) - F01A = "F01A", -- Moonriver (Authoritative ID) - moonriver = "F01A", -- Moonriver (Alias) + ["F01A"] = "F01A", -- Moonriver (Authoritative ID) + ["moonriver"] = "F01A", -- Moonriver (Alias) - F01B = "F01B", -- Near (Authoritative ID) - near = "F01B", -- Near (Alias) + ["F01B"] = "F01B", -- Near (Authoritative ID) + ["near"] = "F01B", -- Near (Alias) - F01C = "F01C", -- Oasys (Authoritative ID) - oasys = "F01C", -- Oasys (Alias) + ["F01C"] = "F01C", -- Oasys (Authoritative ID) + ["oasys"] = "F01C", -- Oasys (Alias) - F01D = "F01D", -- Optimism (Authoritative ID) - optimism = "F01D", -- Optimism (Alias) + ["F01D"] = "F01D", -- Optimism (Authoritative ID) + ["optimism"] = "F01D", -- Optimism (Alias) - F01E = "F01E", -- Optimism Sepolia Testnet (Authoritative ID) - optimism-sepolia-testnet = "F01E", -- Optimism Sepolia Testnet (Alias) + ["F01E"] = "F01E", -- Optimism Sepolia Testnet (Authoritative ID) + ["optimism-sepolia-testnet"] = "F01E", -- Optimism Sepolia Testnet (Alias) - F01F = "F01F", -- Opbnb (Authoritative ID) - opbnb = "F01F", -- Opbnb (Alias) + ["F01F"] = "F01F", -- Opbnb (Authoritative ID) + ["opbnb"] = "F01F", -- Opbnb (Alias) - F020 = "F020", -- Osmosis (Authoritative ID) - osmosis = "F020", -- Osmosis (Alias) + ["F020"] = "F020", -- Osmosis (Authoritative ID) + ["osmosis"] = "F020", -- Osmosis (Alias) - F021 = "F021", -- Polygon (Authoritative ID) - polygon = "F021", -- Polygon (Alias) + ["F021"] = "F021", -- Polygon (Authoritative ID) + ["polygon"] = "F021", -- Polygon (Alias) - F022 = "F022", -- Polygon Amoy Testnet (Authoritative ID) - polygon-amoy-testnet = "F022", -- Polygon Amoy Testnet (Alias) + ["F022"] = "F022", -- Polygon Amoy Testnet (Authoritative ID) + ["polygon-amoy-testnet"] = "F022", -- Polygon Amoy Testnet (Alias) - F023 = "F023", -- Radix (Authoritative ID) - radix = "F023", -- Radix (Alias) + ["F023"] = "F023", -- Radix (Authoritative ID) + ["radix"] = "F023", -- Radix (Alias) - F024 = "F024", -- Scroll (Authoritative ID) - scroll = "F024", -- Scroll (Alias) + ["F024"] = "F024", -- Scroll (Authoritative ID) + ["scroll"] = "F024", -- Scroll (Alias) - F025 = "F025", -- Solana (Authoritative ID) - solana = "F025", -- Solana (Alias) + ["F025"] = "F025", -- Solana (Authoritative ID) + ["solana"] = "F025", -- Solana (Alias) - F026 = "F026", -- Sui (Authoritative ID) - sui = "F026", -- Sui (Alias) + ["F026"] = "F026", -- Sui (Authoritative ID) + ["sui"] = "F026", -- Sui (Alias) - F027 = "F027", -- Taiko (Authoritative ID) - taiko = "F027", -- Taiko (Alias) + ["F027"] = "F027", -- Taiko (Authoritative ID) + ["taiko"] = "F027", -- Taiko (Alias) - F028 = "F028", -- Taiko Hekla Testnet (Authoritative ID) - taiko-hekla-testnet = "F028", -- Taiko Hekla Testnet (Alias) + ["F028"] = "F028", -- Taiko Hekla Testnet (Authoritative ID) + ["taiko-hekla-testnet"] = "F028", -- Taiko Hekla Testnet (Alias) - F029 = "F029", -- Polygon Zkevm (Authoritative ID) - polygon-zkevm = "F029", -- Polygon Zkevm (Alias) + ["F029"] = "F029", -- Polygon Zkevm (Authoritative ID) + ["polygon-zkevm"] = "F029", -- Polygon Zkevm (Alias) - F02A = "F02A", -- Zklink Nova (Authoritative ID) - zklink-nova = "F02A", -- Zklink Nova (Alias) + ["F02A"] = "F02A", -- Zklink Nova (Authoritative ID) + ["zklink-nova"] = "F02A", -- Zklink Nova (Alias) - F02B = "F02B", -- Zksync Era (Authoritative ID) - zksync-era = "F02B", -- Zksync Era (Alias) + ["F02B"] = "F02B", -- Zksync Era (Authoritative ID) + ["zksync-era"] = "F02B", -- Zksync Era (Alias) } From a6277d6e1ba3dbefc2904fb1b857f827374d782a Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 21:18:33 +0000 Subject: [PATCH 27/48] fix: use global variable in lua filter --- envoy/envoy.template.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index aa29b18c..5cc387e5 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -110,16 +110,19 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inline_code: | function envoy_on_request(handle) + -- Check if the subdomain_to_service_id is already loaded + if not _G.subdomain_to_service_id then + -- Load the mapping of subdomains to service IDs from an external file + _G.subdomain_to_service_id = dofile("/etc/envoy/.allowed-services.lua") + end + local host = handle:headers():get(":authority") local subdomain = string.match(host, "^([^.]+)") local target_service_id_header = handle:headers():get("target-service-id") - -- Load the mapping of subdomains to service IDs from an external file - local subdomain_to_service_id = dofile("/etc/envoy/.allowed-services.lua") - -- Function to resolve service ID from a given key local function resolve_service_id(key) - return subdomain_to_service_id[key] + return _G.subdomain_to_service_id[key] end -- Attempt to resolve service ID from subdomain From ca0ea8c6d79d847473c36ce157a5db6a49d5f334 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Tue, 17 Dec 2024 21:26:42 +0000 Subject: [PATCH 28/48] fix: morse e2e test --- config/config_test.go | 3 --- config/examples/config.morse_example.yaml | 8 +++----- config/examples/config.shannon_example.yaml | 2 +- e2e/morse_relay_test.go | 5 +++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index dc567667..2a5add47 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -48,9 +48,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { "F00C": { RequestTimeout: 3_000 * time.Millisecond, }, - "0021": { - RequestTimeout: 3_000 * time.Millisecond, - }, }, Router: RouterConfig{ Port: defaultPort, diff --git a/config/examples/config.morse_example.yaml b/config/examples/config.morse_example.yaml index 5c55bd9e..b1bdcadd 100644 --- a/config/examples/config.morse_example.yaml +++ b/config/examples/config.morse_example.yaml @@ -34,11 +34,9 @@ morse_config: # services is required. At least one service must be configured with a valid id. # All fields are optional but the id is required. services: - "F00C": - request_timeout: "3000ms" - - # "0021" is an example of a service with no further configuration. - "0021": {} + # "F00C" is a service ID used in the Morse E2E tests; it resolves to the "eth" service. + # If this config is used for Morse E2E tests, do not remove or change this service. + "F00C": {} # Enable endpoint hydrator to run QoS checks against endpoints of service F00C hydrator_config: diff --git a/config/examples/config.shannon_example.yaml b/config/examples/config.shannon_example.yaml index 93897213..0d7a6731 100644 --- a/config/examples/config.shannon_example.yaml +++ b/config/examples/config.shannon_example.yaml @@ -38,7 +38,7 @@ shannon_config: - 40af4e7e1b311c76a573610fe115cd2adf1eeade709cd77ca31ad4472509d388 services: - # "anvil" is a test service alias used in the Shannon E2E tests. + # "anvil" is a test service ID used in the Shannon E2E tests. # If this config is used for Shannon E2E tests, do not remove or change this service. # This is used for testing a dedicated Ethereum endpoint that is not of any public network. "anvil": {} diff --git a/e2e/morse_relay_test.go b/e2e/morse_relay_test.go index bb4e364c..cdd7d76c 100644 --- a/e2e/morse_relay_test.go +++ b/e2e/morse_relay_test.go @@ -58,8 +58,8 @@ func Test_MorseRelay(t *testing.T) { t.Run(test.name, func(t *testing.T) { c := require.New(t) - // eg. fullURL = "http://test-service.localdev.me:55006/v1" - fullURL := fmt.Sprintf("http://%s.%s:%s%s", test.serviceID, localdevMe, pathContainerPort, reqPath) + // eg. fullURL = "http://localdev.me:55006/v1" + fullURL := fmt.Sprintf("http://%s:%s%s", localdevMe, pathContainerPort, reqPath) client := &http.Client{} @@ -67,6 +67,7 @@ func Test_MorseRelay(t *testing.T) { req, err := http.NewRequest(test.reqMethod, fullURL, bytes.NewBuffer([]byte(test.body))) c.NoError(err) req.Header.Set("Content-Type", "application/json") + req.Header.Set("target-service-id", test.serviceID) var success bool var allErrors []error From acdeda1bc7c150d6b0760019f4b1a15029a12024 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Tue, 17 Dec 2024 16:30:51 -0800 Subject: [PATCH 29/48] Followup to #103 --- config/service_alias.go | 107 +++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/config/service_alias.go b/config/service_alias.go index 3d9bdfdc..ef77a714 100644 --- a/config/service_alias.go +++ b/config/service_alias.go @@ -30,63 +30,82 @@ func init() { for k, v := range legacyMorseQoSTypes { ServiceQoSTypes[k] = v } + for k, v := range testQoSTypes { + ServiceQoSTypes[k] = v + } } +// Shannon service IDs. +// As of 12/2024, these service IDs are on Beta TestNet and intended to be moved +// over to MainNet once the network is ready. var shannonQoSTypes = map[protocol.ServiceID]ServiceQoSType{ - // TODO_IMPROVE Add all non-EVM Morse Services and requisite initialization - - // Shannon Service IDs - "anvil": ServiceIDEVM, // ETH Local (development/testing) - - // TODO_IMPROVE(@commoddity): Use actual service IDs for Solana and POKT. + // Solana Service IDs "solana": ServiceIDSolana, + // EVM Service IDs + "eth": ServiceIDEVM, + + // POKT Service IDs "pokt": ServiceIDPOKT, "morse": ServiceIDPOKT, +} + +// Shannon test service IDs. +// As of 12/2024, these service IDs are on Beta TestNet and primarily used +// for E2E testing. They may or may not be moved over to MainNet once the network. +var testQoSTypes = map[protocol.ServiceID]ServiceQoSType{ + // Shannon Service IDs + "anvil": ServiceIDEVM, // ETH Local (development/testing) // Gateway E2E service ID is used only for running PATH's Morse and Shannon E2E tests. "gatewaye2e": ServiceIDE2E, } -// All Morse EVM Services as of 12/17/2024 (#103) // TODO_TECHDEBT(@fredteumer): Revisit and consider removing these once #105 is complete. +// Service IDs transferred from Morse to Shannon for backwards compatibility. var legacyMorseQoSTypes = map[protocol.ServiceID]ServiceQoSType{ - "F001": ServiceIDEVM, // Arbitrum One - "F002": ServiceIDEVM, // Arbitrum Sepolia Testnet - "F003": ServiceIDEVM, // Avalanche - "F004": ServiceIDEVM, // Avalanche-DFK - "F005": ServiceIDEVM, // Base - "F006": ServiceIDEVM, // Base Sepolia Testnet - "F008": ServiceIDEVM, // Blast - "F009": ServiceIDEVM, // BNB Smart Chain - "F00A": ServiceIDEVM, // Boba - "F00B": ServiceIDEVM, // Celo - "F00C": ServiceIDEVM, // Ethereum - "F00D": ServiceIDEVM, // Ethereum Holesky Testnet - "F00E": ServiceIDEVM, // Ethereum Sepolia Testnet - "F00F": ServiceIDEVM, // Evmos - "F010": ServiceIDEVM, // Fantom - "F011": ServiceIDEVM, // Fraxtal - "F012": ServiceIDEVM, // Fuse - "F013": ServiceIDEVM, // Gnosis - "F014": ServiceIDEVM, // Harmony-0 - "F015": ServiceIDEVM, // IoTeX - "F016": ServiceIDEVM, // Kaia - "F017": ServiceIDEVM, // Kava - "F018": ServiceIDEVM, // Metis - "F019": ServiceIDEVM, // Moonbeam - "F01A": ServiceIDEVM, // Moonriver - "F01C": ServiceIDEVM, // Oasys - "F01D": ServiceIDEVM, // Optimism - "F01E": ServiceIDEVM, // Optimism Sepolia Testnet - "F01F": ServiceIDEVM, // opBNB - "F021": ServiceIDEVM, // Polygon - "F022": ServiceIDEVM, // Polygon Amoy Testnet - "F024": ServiceIDEVM, // Scroll + // Deprecated Morse EVM chains + "0021": ServiceIDEVM, // ETH Mainnet + + // All Morse EVM F-Chain Services as of 12/17/2024 (#103) + "F001": ServiceIDEVM, // Arbitrum One + "F002": ServiceIDEVM, // Arbitrum Sepolia Testnet + "F003": ServiceIDEVM, // Avalanche + "F004": ServiceIDEVM, // Avalanche-DFK + "F005": ServiceIDEVM, // Base + "F006": ServiceIDEVM, // Base Sepolia Testnet + "F008": ServiceIDEVM, // Blast + "F009": ServiceIDEVM, // BNB Smart Chain + "F00A": ServiceIDEVM, // Boba + "F00B": ServiceIDEVM, // Celo + "F00C": ServiceIDEVM, // Ethereum + "F00D": ServiceIDEVM, // Ethereum Holesky Testnet + "F00E": ServiceIDEVM, // Ethereum Sepolia Testnet + "F00F": ServiceIDEVM, // Evmos + "F010": ServiceIDEVM, // Fantom + "F011": ServiceIDEVM, // Fraxtal + "F012": ServiceIDEVM, // Fuse + "F013": ServiceIDEVM, // Gnosis + "F014": ServiceIDEVM, // Harmony-0 + "F015": ServiceIDEVM, // IoTeX + "F016": ServiceIDEVM, // Kaia + "F017": ServiceIDEVM, // Kava + "F018": ServiceIDEVM, // Metis + "F019": ServiceIDEVM, // Moonbeam + "F01A": ServiceIDEVM, // Moonriver + "F01C": ServiceIDEVM, // Oasys + "F01D": ServiceIDEVM, // Optimism + "F01E": ServiceIDEVM, // Optimism Sepolia Testnet + "F01F": ServiceIDEVM, // opBNB + "F021": ServiceIDEVM, // Polygon + "F022": ServiceIDEVM, // Polygon Amoy Testnet + "F024": ServiceIDEVM, // Scroll + "F027": ServiceIDEVM, // Taiko + "F028": ServiceIDEVM, // Taiko Hekla Testnet + "F029": ServiceIDEVM, // Polygon zkEVM + "F02A": ServiceIDEVM, // zkLink + "F02B": ServiceIDEVM, // zkSync + + // Solana F-Chain Service IDs as of 12/2024 (#103) "F025": ServiceIDSolana, // Solana - "F027": ServiceIDEVM, // Taiko - "F028": ServiceIDEVM, // Taiko Hekla Testnet - "F029": ServiceIDEVM, // Polygon zkEVM - "F02A": ServiceIDEVM, // zkLink - "F02B": ServiceIDEVM, // zkSync } From 309fa7271f7565ce092fd5bd7978fd026f8d08f1 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Tue, 17 Dec 2024 16:52:41 -0800 Subject: [PATCH 30/48] Adding some helpers for makefile test helpers --- Makefile | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 1db013df..7a985ddf 100644 --- a/Makefile +++ b/Makefile @@ -78,24 +78,26 @@ test_auth_server: ## Run the auth server tests (cd envoy/auth_server && go test ./... -count=1) .PHONY: test_e2e_shannon_relay_iterate -test_e2e_shannon_relay_iterate: ## Instructions on how to iterate locally for an E2E shannon relay +test_e2e_shannon_relay_iterate: ## Iterate on E2E shannon relay tests @echo "go build -o bin/path ./cmd" @echo "# Update ./bin/config/.config.yaml" @echo "./bin/path" @echo "curl http://anvil.localhost:3000/v1/abcd1234 -X POST -H \"Content-Type: application/json\" -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_blockNumber\"}'" .PHONY: test_e2e_shannon_relay -test_e2e_shannon_relay: ## Run an E2E shannon relay test (iterate) - @echo "README: If you are iterating on E2E tests, stop this and run the following for instructions instead: 'make test_e2e_shannon_relay_iterate'." +test_e2e_shannon_relay: shannon_e2e_config_warning ## Run an E2E Shannon relay test + @echo "###############################################################################################################################################################" + @echo "### README: If you are intending to iterate on E2E tests, stop this and run the following for instructions instead: 'make test_e2e_shannon_relay_iterate'. ###" + @echo "###############################################################################################################################################################" go test -v ./e2e/... -tags=e2e -count=1 -run Test_ShannonRelay .PHONY: test_e2e_morse_relay -test_e2e_morse_relay: ## Run an E2E Morse relay test +test_e2e_morse_relay: morse_e2e_config_warning ## Run an E2E Morse relay test go test -v ./e2e/... -tags=e2e -count=1 -run Test_MorseRelay -################################ -### Copy Config Make Targets ### -################################ +################################### +### Shannon Config Make Targets ### +################################### # TODO_MVP(@commoddity): Consolidate the copy_*_config targets into fewer targets once # the config files are consolidated as well. @@ -117,6 +119,15 @@ copy_shannon_config: ## copies the example shannon configuration yaml file to .c echo "##################################################################"; \ fi +.PHONY: shannon_e2e_config_warning +shannon_e2e_config_warning: ## Prints a warning if the shannon E2E config is not populated + @if [ ! -f ./e2e/.shannon.config.yaml ]; then \ + echo "#########################################################################"; \ + echo "### Shannon E2E config not found, run: 'make copy_shannon_e2e_config' ###"; \ + echo "#########################################################################"; \ + exit; \ + fi + .PHONY: copy_shannon_e2e_config copy_shannon_e2e_config: ## copies the example Shannon test configuration yaml file to .gitignored .shannon.config.yaml file @if [ ! -f ./e2e/.shannon.config.yaml ]; then \ @@ -148,6 +159,10 @@ config_shannon_localnet: ## Create a localnet config file to serve as a Shannon echo "#########################################################################"; \ fi +################################### +### Morse Config Make Targets ### +################################### + .PHONY: copy_morse_config copy_morse_config: ## copies the example morse configuration yaml file to .config.yaml file @if [ ! -f ./bin/config/.config.yaml ]; then \ @@ -164,6 +179,15 @@ copy_morse_config: ## copies the example morse configuration yaml file to .confi echo "##################################################################"; \ fi +.PHONY: morse_e2e_config_warning +morse_e2e_config_warning: ## Prints a warning if the shannon E2E config is not populated + @if [ ! -f ./e2e/.morse.config.yaml ]; then \ + echo "#####################################################################"; \ + echo "### Morse E2E config not found, run: 'make copy_morse_e2e_config' ###"; \ + echo "#####################################################################"; \ + exit; \ + fi + .PHONY: copy_morse_e2e_config copy_morse_e2e_config: ## copies the example Morse test configuration yaml file to .gitignored ..morse.config.yaml file. @if [ ! -f ./e2e/.morse.config.yaml ]; then \ From 4d9d6e9a7fa897d3b45e5e8b471bcb99555a426e Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Tue, 17 Dec 2024 18:10:53 -0800 Subject: [PATCH 31/48] Link to 1Password UUIDs --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 7a985ddf..bea9a424 100644 --- a/Makefile +++ b/Makefile @@ -128,6 +128,7 @@ shannon_e2e_config_warning: ## Prints a warning if the shannon E2E config is not exit; \ fi +# If you are a Grove employee, search for this UUID in 1Password: 47k7kidj3y6nd3cghlakg76nlm .PHONY: copy_shannon_e2e_config copy_shannon_e2e_config: ## copies the example Shannon test configuration yaml file to .gitignored .shannon.config.yaml file @if [ ! -f ./e2e/.shannon.config.yaml ]; then \ @@ -188,6 +189,7 @@ morse_e2e_config_warning: ## Prints a warning if the shannon E2E config is not p exit; \ fi +# If you are a Grove employee, search for this UUID in 1Password: un76qmz6xunx43icttjmagzlri .PHONY: copy_morse_e2e_config copy_morse_e2e_config: ## copies the example Morse test configuration yaml file to .gitignored ..morse.config.yaml file. @if [ ! -f ./e2e/.morse.config.yaml ]; then \ From 94eeac28cd22b0972544e7f8b8353b83521688fa Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Tue, 17 Dec 2024 19:12:45 -0800 Subject: [PATCH 32/48] Remove gatewaye2e --- config/service_alias.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/config/service_alias.go b/config/service_alias.go index ef77a714..e77d08b0 100644 --- a/config/service_alias.go +++ b/config/service_alias.go @@ -12,10 +12,9 @@ type ServiceQoSType string const ( // TODO_IMPROVE(@commoddity): consider using protocol scope for the service IDs. - ServiceIDEVM ServiceQoSType = "evm" // ServiceIDEVM represents the EVM service type, containing all EVM-based blockchains. - ServiceIDSolana ServiceQoSType = "solana" // ServiceIDSolana represents the Solana blockchain service type. - ServiceIDPOKT ServiceQoSType = "pokt" // ServiceIDPOKT represents the POKT blockchain service type. - ServiceIDE2E ServiceQoSType = "gatewaye2e" // ServiceIDE2E represents the service created for running PATH gateway's E2E tests. + ServiceIDEVM ServiceQoSType = "evm" // ServiceIDEVM represents the EVM service type, containing all EVM-based blockchains. + ServiceIDSolana ServiceQoSType = "solana" // ServiceIDSolana represents the Solana blockchain service type. + ServiceIDPOKT ServiceQoSType = "pokt" // ServiceIDPOKT represents the POKT blockchain service type. ) // The ServiceQoSTypes map associates each supported service ID with a specific @@ -56,9 +55,6 @@ var shannonQoSTypes = map[protocol.ServiceID]ServiceQoSType{ var testQoSTypes = map[protocol.ServiceID]ServiceQoSType{ // Shannon Service IDs "anvil": ServiceIDEVM, // ETH Local (development/testing) - - // Gateway E2E service ID is used only for running PATH's Morse and Shannon E2E tests. - "gatewaye2e": ServiceIDE2E, } // TODO_TECHDEBT(@fredteumer): Revisit and consider removing these once #105 is complete. From 309b40e9e1ddea02dbfd5ac1422f31e27353b2aa Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Tue, 17 Dec 2024 19:42:29 -0800 Subject: [PATCH 33/48] Remove ServiceIDE2E --- cmd/qos.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cmd/qos.go b/cmd/qos.go index 9713f038..fe4c9e53 100644 --- a/cmd/qos.go +++ b/cmd/qos.go @@ -74,18 +74,6 @@ func getServiceQoSInstances( case config.ServiceIDPOKT: // TODO_TECHDEBT: add pokt qos service here - case config.ServiceIDE2E: - evmEndpointStore := &evm.EndpointStore{ - Config: evm.EndpointStoreConfig{ - // TODO_MVP(@adshmh): Read the chain ID from the configuration. - ChainID: "0x1", - }, - Logger: logger, - } - if _, ok := gatewayServiceIDsIdx[serviceID]; ok { - gatewayQoSService[serviceID] = evm.NewServiceQoS(evmEndpointStore, logger) - } - default: return nil, nil, fmt.Errorf("error building QoS instances: service ID %q not supported by PATH", serviceID) } From 3c6890f0d3f1f45cb15dd12f7d70b975c42a1a0f Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 11:04:42 +0000 Subject: [PATCH 34/48] chore: add envoy config doc --- .../docs/operate/configs/_category_.json | 8 + .../docs/operate/configs/envoy_config.md | 212 ++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 docusaurus/docs/operate/configs/_category_.json create mode 100644 docusaurus/docs/operate/configs/envoy_config.md diff --git a/docusaurus/docs/operate/configs/_category_.json b/docusaurus/docs/operate/configs/_category_.json new file mode 100644 index 00000000..e89e5390 --- /dev/null +++ b/docusaurus/docs/operate/configs/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "PATH Operator Guide", + "position": 3, + "link": { + "type": "generated-index", + "description": "Documentation for PATH operators." + } +} \ No newline at end of file diff --git a/docusaurus/docs/operate/configs/envoy_config.md b/docusaurus/docs/operate/configs/envoy_config.md new file mode 100644 index 00000000..aab209ea --- /dev/null +++ b/docusaurus/docs/operate/configs/envoy_config.md @@ -0,0 +1,212 @@ +--- +title: Envoy Proxy config +sidebar_position: 4 +--- + +# Envoy Proxy config + +This document describes the configuration options for PATH's Envoy Proxy. + +In PATH, Envoy Proxy is responsible for: + +- Defining allowed services +- Request authorization +- Rate limiting + +:::info + +There are a total of four files used to configure Envoy Proxy in PATH: + +**Envoy Config Files** + +1. `.allowed-services.lua` +2. `.envoy.yaml` +3. `.ratelimit.yaml` + + The templates used to generate these Envoy config files [may be found here](https://github.com/buildwithgrove/path/tree/main/envoy). + +**Gateway Endpoints File** + +4. `.gateway-endpoints.yaml` + + The example `.gateway-endpoints.yaml` file is located in the [PADS repo](https://github.com/buildwithgrove/path-auth-data-service/tree/main/envoy/gateway-endpoints.yaml). + +::: + +- [Initialization](#initialization) +- [Allowed Services - `.allowed-services.lua`](#allowed-services---allowed-serviceslua) + - [File Format](#file-format) + - [Terminology](#terminology) +- [Envoy Proxy Configuration - `.envoy.yaml`](#envoy-proxy-configuration---envoyyaml) +- [Ratelimit Configuration - `.ratelimit.yaml`](#ratelimit-configuration---ratelimityaml) + - [File Format](#file-format-1) +- [Gateway Endpoints Data - `.gateway-endpoints.yaml`](#gateway-endpoints-data---gateway-endpointsyaml) + - [File Format](#file-format-2) + + +## Initialization + +The required Envoy configuration files may be generated from their templates by running the make target: + +```bash +make init_envoy +``` + +This will generate the following files in the `local/path/envoy` directory: + +- `.allowed-services.lua` +- `.envoy.yaml` +- `.ratelimit.yaml` +- `.gateway-endpoints.yaml` + +:::warning + +All of these files are git ignored from the PATH repo as they are specific to each PATH instance and may contain sensitive information. + +::: + +## Allowed Services - `.allowed-services.lua` + +The `.allowed-services.lua` file is used to define the allowed services for the Envoy Proxy. + +Once created in `local/path/envoy`, the `.allowed-services.lua` file is mounted as a file in the Envoy Proxy container at `/etc/envoy/.allowed-services.lua`. + +### File Format + +_`.allowed-services.lua` format:_ +```lua +return { + -- 1. Shannon Service IDs + ["anvil"] = "anvil", -- Anvil (Authoritative ID) + + -- 2. Morse Service IDs + ["F000"] = "F000", -- Pocket (Authoritative ID) + ["pocket"] = "F000", -- Pocket (Alias) +} +``` + +The key may either be the **authoritative service ID** or an **alias**. The value must be the **authoritative service ID**. + +- [`.allowed-services.lua` template file](https://github.com/buildwithgrove/path/tree/main/envoy/allowed-services.template.lua). + +:::warning + +All service IDs allowed by the PATH instance must be defined in the `.allowed-services.lua` file. + +**Requests for services not defined in this file will be rejected.** + +::: + +### Terminology + +- **Authoritative ID**: The service ID that that PATH uses to identify a service. +- **Alias**: A string that resolves to a service ID, which is useful for creating human-readable subdomains for services. + + **Regardless of the method used to pass the service ID or alias to Envoy Proxy, the Envoy Proxy's Lua filter will forward requests to PATH with the `authoritative ID` set in the `target-service-id` header.** + +## Envoy Proxy Configuration - `.envoy.yaml` + +The `.envoy.yaml` file is used to configure the Envoy Proxy. + +Once created in `local/path/envoy`, the `.envoy.yaml` file is mounted as a file in the Envoy Proxy container at `/etc/envoy/.envoy.yaml`. + +:::warning + +Once configured using the prompts in the `make init_envoy` target, the `.envoy.yaml` file does not require further modification. + +**It is not recommended to modify the `.envoy.yaml` file directly unless you know what you are doing.** + +::: + +:::tip + +[For more information on Envoy Proxy configuration file, see the Envoy Proxy documentation.](https://www.envoyproxy.io/docs/envoy/latest/configuration/overview/examples#static) + +::: + +## Ratelimit Configuration - `.ratelimit.yaml` + +The `.ratelimit.yaml` file is used to configure the Ratelimit service. + +Once created in `local/path/envoy`, the `.ratelimit.yaml` file is mounted as a file in the Envoy Proxy container at `/etc/envoy/.ratelimit.yaml`. + +**To make changes to the Ratelimit service, modify the `.ratelimit.yaml` file.** + +### File Format + +_`.ratelimit.yaml` format:_ +```yaml +--- +domain: rl +descriptors: + - key: rl-endpoint-id + descriptors: + - key: rl-throughput + value: "30" + rate_limit: + unit: second + requests_per_unit: 30 +``` + +- [`.ratelimit.yaml` template file](https://github.com/buildwithgrove/path/tree/main/envoy/ratelimit.template.yaml). + +To add new throughput limits, add a new descriptor array item under the `descriptors` key. + +:::tip + +[For more information on Rate Limit descriptors, see the documentation in the Envoy Rate Limit repository.](https://github.com/envoyproxy/ratelimit?tab=readme-ov-file#definitions) + +::: + + +## Gateway Endpoints Data - `.gateway-endpoints.yaml` + +:::info + +A `GatewayEndpoint` is how PATH defines a single endpoint that is authorized to use the PATH service. + +It is used to define the **authorization method**, **rate limits**, and **metadata** for an endpoint. + +For more information, see the [Gateway Endpoint section in the Envoy docs](../../develop/envoy/introduction.md#gateway-endpoint-authorization). + +::: + +The `.gateway-endpoints.yaml` file is used to define the Gateway Endpoints that are authorized to make requests to the PATH instance. + +Once created in `local/path/envoy`, the `.gateway-endpoints.yaml` file is mounted as a file in the [PATH Auth Data Server (PADS)](https://github.com/buildwithgrove/path-auth-data-server) container at `/app/.gateway-endpoints.yaml`. + +:::tip + +This YAML file is provided as an easy default way to define Gateway Endpoints to get started with PATH. For more complex use cases, you may wish to use a database as the data source for Gateway Endpoints. + +**For more information on how to use a database as the data source for Gateway Endpoints, [see the PATH Auth Data Server (PADS) section of the Envoy docs](../../develop/envoy/introduction.md#path-auth-data-server).** + +::: + + +### File Format + +```yaml +endpoints: + endpoint_1_static_key: + auth: + api_key: "api_key_1" + + endpoint_2_jwt: + auth: + jwt_authorized_users: + - "auth0|user_1" + - "auth0|user_2" + + endpoint_3_no_auth: + rate_limiting: + throughput_limit: 30 + capacity_limit: 100000 + capacity_limit_period: "CAPACITY_LIMIT_PERIOD_MONTHLY" +``` + +- [`.gateway-endpoints.yaml` example file](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml). + +- [Gateway Endpoint YAML File Schema](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/gateway-endpoints.schema.yaml). + +To define the Gateway Endpoints that are authorized to use the PATH service, edit the `.gateway-endpoints.yaml` file. From a8a439103a0ff1f48db4bddab5603a9c4cf7244c Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 14:30:21 +0000 Subject: [PATCH 35/48] fix: link in docs --- docusaurus/docs/operate/configs/envoy_config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus/docs/operate/configs/envoy_config.md b/docusaurus/docs/operate/configs/envoy_config.md index c366d69b..0e3200b9 100644 --- a/docusaurus/docs/operate/configs/envoy_config.md +++ b/docusaurus/docs/operate/configs/envoy_config.md @@ -36,7 +36,7 @@ There are a total of four files used to configure Envoy Proxy in PATH: 4. `.gateway-endpoints.yaml` - The example `.gateway-endpoints.yaml` file is located in the [PADS repo](https://github.com/buildwithgrove/path-auth-data-service/tree/main/envoy/gateway-endpoints.yaml). + The example `.gateway-endpoints.yaml` file is located in the [PADS repo](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml). ::: From cb5b24f0f9e842eed0eb03a51900a6b14f06a76c Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 15:45:21 +0000 Subject: [PATCH 36/48] fix: small changes to fix morse E2E test --- config/config_test.go | 16 +- config/examples/config.morse_example.yaml | 11 +- config/examples/config.shannon_example.yaml | 11 +- e2e/morse_relay_test.go | 43 +++-- e2e/shannon_relay_test.go | 18 +- local/path/envoy/.allowed-services.lua | 199 ++++++++++++++++++++ 6 files changed, 241 insertions(+), 57 deletions(-) create mode 100644 local/path/envoy/.allowed-services.lua diff --git a/config/config_test.go b/config/config_test.go index d793a2ab..5afe30e3 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -46,10 +46,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { }, Services: map[protocol.ServiceID]ServiceConfig{ "F00C": { - Alias: "eth", - RequestTimeout: 3_000 * time.Millisecond, - }, - "0021": { RequestTimeout: 3_000 * time.Millisecond, }, }, @@ -63,9 +59,7 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { HydratorConfig: EndpointHydratorConfig{ ServiceIDs: []protocol.ServiceID{"F00C"}, }, - serviceAliases: map[string]protocol.ServiceID{ - "eth": "F00C", - }, + serviceAliases: map[string]protocol.ServiceID{}, }, wantErr: false, }, @@ -93,10 +87,6 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { "anvil": { RequestTimeout: 3_000 * time.Millisecond, }, - "F00C": { - Alias: "eth", - RequestTimeout: 3_000 * time.Millisecond, - }, }, Router: RouterConfig{ Port: defaultPort, @@ -105,9 +95,7 @@ func Test_LoadGatewayConfigFromYAML(t *testing.T) { WriteTimeout: defaultWriteTimeout, IdleTimeout: defaultIdleTimeout, }, - serviceAliases: map[string]protocol.ServiceID{ - "eth": "F00C", - }, + serviceAliases: map[string]protocol.ServiceID{}, }, wantErr: false, }, diff --git a/config/examples/config.morse_example.yaml b/config/examples/config.morse_example.yaml index 80afc612..42776b25 100644 --- a/config/examples/config.morse_example.yaml +++ b/config/examples/config.morse_example.yaml @@ -32,15 +32,10 @@ morse_config: application_signature: "40af4e7e1b311c76a573610fe115cd2adf1eeade709cd77ca31ad4472509d38840af4e7e1b311c76a573610fe115cd2adf1eeade709cd77ca31ad4472509d388" # services is required. At least one service must be configured with a valid id. -# All fields are optional but the id is required. services: - "F00C": - # do not change service alias: it is used in the Morse E2E tests to identify the target service. - alias: "eth" - request_timeout: "3000ms" - - # "0021" is an example of a service with no further configuration. - "0021": {} + # "F00C" is the service ID for the full-chain Ethereum network. + # If this config is used for Morse E2E tests, do not remove or change this service. + "F00C": {} # Enable endpoint hydrator to run QoS checks against endpoints of service F00C hydrator_config: diff --git a/config/examples/config.shannon_example.yaml b/config/examples/config.shannon_example.yaml index bd260a4f..8731a859 100644 --- a/config/examples/config.shannon_example.yaml +++ b/config/examples/config.shannon_example.yaml @@ -39,19 +39,10 @@ shannon_config: services: # "anvil" is a test service alias used in the Shannon E2E tests. - # If this config is used for Shannon E2E tests, do not remove or change this service. # This is used for testing a dedicated Ethereum endpoint that is not of any public network. - # Note that this service has no alias, meaning it can be reached only by its service ID. - # eg. https://anvil.path.grove.city + # If this config is used for Shannon E2E tests, do not remove or change this service. "anvil": {} - # "F00C" is the service ID for the Ethereum MainNet Archival. - # Note that this service has an alias, meaning it can be reached - # both by its service ID and by its alias. - # eg. https://F00C.path.grove.city and https://eth.path.grove.city. - "F00C": - alias: "eth" - # To enable endpoint hydrator to run QoS checks against # service endpoints, add the following to the config: # hydrator_config: diff --git a/e2e/morse_relay_test.go b/e2e/morse_relay_test.go index 51f0fc89..a9e94163 100644 --- a/e2e/morse_relay_test.go +++ b/e2e/morse_relay_test.go @@ -9,6 +9,7 @@ import ( "net/http" "testing" + "github.com/buildwithgrove/path/protocol" "github.com/stretchr/testify/require" ) @@ -24,26 +25,28 @@ func Test_MorseRelay(t *testing.T) { defer teardownFn() tests := []struct { - name string - reqMethod string - serviceID string - serviceAlias string - relayID string - body string + name string + reqMethod string + serviceID protocol.ServiceID + reqPath string + relayID string + body string }{ { - name: "should successfully relay eth_chainId for eth (F00C)", - reqMethod: http.MethodPost, - serviceAlias: "eth", - relayID: "1201", - body: `{"jsonrpc": "2.0", "id": "1201", "method": "eth_chainId"}`, + name: "should successfully relay eth_chainId for eth (F00C)", + reqMethod: http.MethodPost, + serviceID: "F00C", + reqPath: "/v1/abcdef12", + relayID: "1201", + body: `{"jsonrpc": "2.0", "id": "1201", "method": "eth_chainId"}`, }, { - name: "should successfully relay eth_blockNumber for eth (F00C)", - reqMethod: http.MethodPost, - serviceAlias: "eth", - relayID: "1202", - body: `{"jsonrpc": "2.0", "id": "1202", "method": "eth_blockNumber"}`, + name: "should successfully relay eth_blockNumber for eth (F00C)", + reqMethod: http.MethodPost, + serviceID: "F00C", + reqPath: "/v1/abcdef12", + relayID: "1202", + body: `{"jsonrpc": "2.0", "id": "1202", "method": "eth_blockNumber"}`, }, // TODO_UPNEXT(@adshmh): add more test cases with valid and invalid jsonrpc request payloads. @@ -54,13 +57,12 @@ func Test_MorseRelay(t *testing.T) { // 4. Invalid generic request } - reqPath := "/v1/abcdef12" for _, test := range tests { t.Run(test.name, func(t *testing.T) { c := require.New(t) - // eg. fullURL = "http://test-service.localdev.me:55006/v1" - fullURL := fmt.Sprintf("http://%s.%s:%s%s", test.serviceAlias, localdevMe, pathContainerPort, reqPath) + // eg. fullURL = "http://localdev.me:55006/v1/abcdef12" + fullURL := fmt.Sprintf("http://%s:%s%s", localdevMe, pathContainerPort, test.reqPath) client := &http.Client{} @@ -69,6 +71,9 @@ func Test_MorseRelay(t *testing.T) { c.NoError(err) req.Header.Set("Content-Type", "application/json") + // Assign the target service ID to the request header. + req.Header.Set("target-service-id", string(test.serviceID)) + var success bool var allErrors []error for i := 0; i < 10; i++ { diff --git a/e2e/shannon_relay_test.go b/e2e/shannon_relay_test.go index 0b6077a2..ca95542f 100644 --- a/e2e/shannon_relay_test.go +++ b/e2e/shannon_relay_test.go @@ -11,6 +11,7 @@ import ( "net/http" "testing" + "github.com/buildwithgrove/path/protocol" "github.com/stretchr/testify/require" ) @@ -28,7 +29,8 @@ func Test_ShannonRelay(t *testing.T) { tests := []struct { name string reqMethod string - serviceID string + serviceID protocol.ServiceID + reqPath string relayID string body string }{ @@ -37,27 +39,28 @@ func Test_ShannonRelay(t *testing.T) { // single endpoint, maintained by Grove. name: "should successfully relay eth_blockNumber for anvil", reqMethod: http.MethodPost, + serviceID: "anvil", + reqPath: "/v1/abcdef12", relayID: "1001", body: `{"jsonrpc": "2.0", "id": "1001", "method": "eth_blockNumber"}`, }, { name: "should successfully relay eth_chainId for anvil", reqMethod: http.MethodPost, + serviceID: "anvil", + reqPath: "/v1/abcdef12", relayID: "1002", body: `{"jsonrpc": "2.0", "id": "1002", "method": "eth_chainId"}`, }, // TODO_UPNEXT(@adshmh): add more test cases with valid and invalid jsonrpc request payloads. } - reqPath := "/v1/abcdef12" - serviceID := "anvil" - for _, test := range tests { t.Run(test.name, func(t *testing.T) { c := require.New(t) - // eg. fullURL = "http://anvil.localdev.me:55006/v1/abcdef12" - fullURL := fmt.Sprintf("http://%s.%s:%s%s", serviceID, localdevMe, pathContainerPort, reqPath) + // eg. fullURL = "http://localdev.me:55006/v1/abcdef12" + fullURL := fmt.Sprintf("http://%s:%s%s", localdevMe, pathContainerPort, test.reqPath) client := &http.Client{} @@ -66,6 +69,9 @@ func Test_ShannonRelay(t *testing.T) { c.NoError(err) req.Header.Set("Content-Type", "application/json") + // Assign the target service ID to the request header. + req.Header.Set("target-service-id", string(test.serviceID)) + var success bool var allErrors []error for i := 0; i < 10; i++ { diff --git a/local/path/envoy/.allowed-services.lua b/local/path/envoy/.allowed-services.lua new file mode 100644 index 00000000..2857667e --- /dev/null +++ b/local/path/envoy/.allowed-services.lua @@ -0,0 +1,199 @@ +-- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. +-- +-- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. +-- +-- eg. the alias "eth" = F00C" enables the URL "http://eth.path.grove.city" to be routed to the service with the ID "F00C". +-- +-- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. +-- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. +return { + -- 1. Shannon Service IDs + ["anvil"] = "anvil", -- Anvil (Authoritative ID) + + -- 2. Morse Service IDs + ["F000"] = "F000", -- Pocket (Authoritative ID) + ["mainnet"] = "F000", -- Pocket (Alias) + ["mainnet-archival"] = "F000", -- Pocket (Alias) + ["pocket"] = "F000", -- Pocket (Alias) + ["pokt-archival"] = "F000", -- Pocket (Alias) + + ["F001"] = "F001", -- Arbitrum-one (Authoritative ID) + ["arbitrum-one"] = "F001", -- Arbitrum-one (Alias) + + ["F002"] = "F002", -- Arbitrum-sepolia-testnet (Authoritative ID) + ["arbitrum-sepolia-archival"] = "F002", -- Arbitrum-sepolia-testnet (Alias) + ["arbitrum-sepolia-testnet"] = "F002", -- Arbitrum-sepolia-testnet (Alias) + + ["F003"] = "F003", -- Avax (Authoritative ID) + ["avax"] = "F003", -- Avax (Alias) + ["avax-archival"] = "F003", -- Avax (Alias) + ["avax-mainnet"] = "F003", -- Avax (Alias) + + ["F004"] = "F004", -- Avax-dfk (Authoritative ID) + ["avax-dfk"] = "F004", -- Avax-dfk (Alias) + + ["F005"] = "F005", -- Base (Authoritative ID) + ["base"] = "F005", -- Base (Alias) + ["base-mainnet"] = "F005", -- Base (Alias) + ["base-mainnet-archival"] = "F005", -- Base (Alias) + + ["F006"] = "F006", -- Base-testnet (Authoritative ID) + ["base-testnet"] = "F006", -- Base-testnet (Alias) + ["base-testnet-archival"] = "F006", -- Base-testnet (Alias) + + ["F008"] = "F008", -- Blast (Authoritative ID) + ["blast"] = "F008", -- Blast (Alias) + ["blast-archival"] = "F008", -- Blast (Alias) + ["blast-mainnet"] = "F008", -- Blast (Alias) + + ["F009"] = "F009", -- Bsc (Authoritative ID) + ["bsc"] = "F009", -- Bsc (Alias) + ["bsc-archival"] = "F009", -- Bsc (Alias) + ["bsc-mainnet"] = "F009", -- Bsc (Alias) + + ["F00A"] = "F00A", -- Boba (Authoritative ID) + ["boba"] = "F00A", -- Boba (Alias) + ["boba-mainnet"] = "F00A", -- Boba (Alias) + + ["F00B"] = "F00B", -- Celo (Authoritative ID) + ["celo"] = "F00B", -- Celo (Alias) + ["celo-mainnet"] = "F00B", -- Celo (Alias) + + ["F00C"] = "F00C", -- Eth (Authoritative ID) + ["eth"] = "F00C", -- Eth (Alias) + ["eth-archival"] = "F00C", -- Eth (Alias) + ["eth-mainnet"] = "F00C", -- Eth (Alias) + ["eth-trace"] = "F00C", -- Eth (Alias) + + ["F00D"] = "F00D", -- Eth-holesky-testnet (Authoritative ID) + ["eth-holesky-testnet"] = "F00D", -- Eth-holesky-testnet (Alias) + ["holesky-fullnode-testnet"] = "F00D", -- Eth-holesky-testnet (Alias) + + ["F00E"] = "F00E", -- Eth-sepolia-testnet (Authoritative ID) + ["eth-sepolia-testnet"] = "F00E", -- Eth-sepolia-testnet (Alias) + ["sepolia"] = "F00E", -- Eth-sepolia-testnet (Alias) + ["sepolia-archival"] = "F00E", -- Eth-sepolia-testnet (Alias) + + ["F00F"] = "F00F", -- Evmos (Authoritative ID) + ["evmos"] = "F00F", -- Evmos (Alias) + ["evmos-mainnet"] = "F00F", -- Evmos (Alias) + + ["F010"] = "F010", -- Fantom (Authoritative ID) + ["fantom"] = "F010", -- Fantom (Alias) + ["fantom-mainnet"] = "F010", -- Fantom (Alias) + + ["F011"] = "F011", -- Fraxtal (Authoritative ID) + ["fraxtal"] = "F011", -- Fraxtal (Alias) + ["fraxtal-archival"] = "F011", -- Fraxtal (Alias) + + ["F012"] = "F012", -- Fuse (Authoritative ID) + ["fuse"] = "F012", -- Fuse (Alias) + ["fuse-archival"] = "F012", -- Fuse (Alias) + ["fuse-mainnet"] = "F012", -- Fuse (Alias) + + ["F013"] = "F013", -- Gnosis (Authoritative ID) + ["gnosis"] = "F013", -- Gnosis (Alias) + ["gnosischain-archival"] = "F013", -- Gnosis (Alias) + ["gnosischain-mainnet"] = "F013", -- Gnosis (Alias) + ["poa-xdai"] = "F013", -- Gnosis (Alias) + + ["F014"] = "F014", -- Harmony (Authoritative ID) + ["harmony"] = "F014", -- Harmony (Alias) + ["harmony-0"] = "F014", -- Harmony (Alias) + + ["F015"] = "F015", -- Iotex (Authoritative ID) + ["iotex"] = "F015", -- Iotex (Alias) + ["iotex-mainnet"] = "F015", -- Iotex (Alias) + + ["F016"] = "F016", -- Kaia (Authoritative ID) + ["kaia"] = "F016", -- Kaia (Alias) + ["kaia-mainnet"] = "F016", -- Kaia (Alias) + ["klaytn-mainnet"] = "F016", -- Kaia (Alias) + + ["F017"] = "F017", -- Kava (Authoritative ID) + ["kava"] = "F017", -- Kava (Alias) + ["kava-mainnet"] = "F017", -- Kava (Alias) + ["kava-mainnet-archival"] = "F017", -- Kava (Alias) + + ["F018"] = "F018", -- Metis (Authoritative ID) + ["metis"] = "F018", -- Metis (Alias) + ["metis-mainnet"] = "F018", -- Metis (Alias) + + ["F019"] = "F019", -- Moonbeam (Authoritative ID) + ["moonbeam"] = "F019", -- Moonbeam (Alias) + ["moonbeam-mainnet"] = "F019", -- Moonbeam (Alias) + + ["F01A"] = "F01A", -- Moonriver (Authoritative ID) + ["moonriver"] = "F01A", -- Moonriver (Alias) + ["moonriver-mainnet"] = "F01A", -- Moonriver (Alias) + + ["F01B"] = "F01B", -- Near (Authoritative ID) + ["near"] = "F01B", -- Near (Alias) + ["near-mainnet"] = "F01B", -- Near (Alias) + + ["F01C"] = "F01C", -- Oasys (Authoritative ID) + ["oasys"] = "F01C", -- Oasys (Alias) + ["oasys-mainnet"] = "F01C", -- Oasys (Alias) + ["oasys-mainnet-archival"] = "F01C", -- Oasys (Alias) + + ["F01D"] = "F01D", -- Optimism (Authoritative ID) + ["optimism"] = "F01D", -- Optimism (Alias) + ["optimism-archival"] = "F01D", -- Optimism (Alias) + ["optimism-mainnet"] = "F01D", -- Optimism (Alias) + + ["F01E"] = "F01E", -- Optimism-sepolia-testnet (Authoritative ID) + ["optimism-sepolia-archival"] = "F01E", -- Optimism-sepolia-testnet (Alias) + ["optimism-sepolia-testnet"] = "F01E", -- Optimism-sepolia-testnet (Alias) + + ["F01F"] = "F01F", -- Opbnb (Authoritative ID) + ["opbnb"] = "F01F", -- Opbnb (Alias) + ["opbnb-archival"] = "F01F", -- Opbnb (Alias) + + ["F020"] = "F020", -- Osmosis (Authoritative ID) + ["osmosis"] = "F020", -- Osmosis (Alias) + ["osmosis-mainnet"] = "F020", -- Osmosis (Alias) + + ["F021"] = "F021", -- Polygon (Authoritative ID) + ["poly-archival"] = "F021", -- Polygon (Alias) + ["polygon"] = "F021", -- Polygon (Alias) + ["poly-mainnet"] = "F021", -- Polygon (Alias) + + ["F022"] = "F022", -- Polygon-amoy-testnet (Authoritative ID) + ["amoy-testnet-archival"] = "F022", -- Polygon-amoy-testnet (Alias) + ["polygon-amoy-testnet"] = "F022", -- Polygon-amoy-testnet (Alias) + + ["F023"] = "F023", -- Radix (Authoritative ID) + ["radix"] = "F023", -- Radix (Alias) + ["radix-mainnet"] = "F023", -- Radix (Alias) + + ["F024"] = "F024", -- Scroll (Authoritative ID) + ["scroll"] = "F024", -- Scroll (Alias) + + ["F025"] = "F025", -- Solana (Authoritative ID) + ["solana"] = "F025", -- Solana (Alias) + ["solana-mainnet"] = "F025", -- Solana (Alias) + ["solana-mainnet-custom"] = "F025", -- Solana (Alias) + + ["F026"] = "F026", -- Sui (Authoritative ID) + ["sui"] = "F026", -- Sui (Alias) + ["sui-mainnet"] = "F026", -- Sui (Alias) + + ["F027"] = "F027", -- Taiko (Authoritative ID) + ["taiko"] = "F027", -- Taiko (Alias) + + ["F028"] = "F028", -- Taiko-hekla-testnet (Authoritative ID) + ["taiko-hekla-testnet"] = "F028", -- Taiko-hekla-testnet (Alias) + + ["F029"] = "F029", -- Polygon-zkevm (Authoritative ID) + ["polygon-zkevm"] = "F029", -- Polygon-zkevm (Alias) + ["polygon-zkevm-mainnet"] = "F029", -- Polygon-zkevm (Alias) + ["zkevm-polygon-mainnet"] = "F029", -- Polygon-zkevm (Alias) + + ["F02A"] = "F02A", -- Zklink-nova (Authoritative ID) + ["zklink-nova"] = "F02A", -- Zklink-nova (Alias) + ["zklink-nova-archival"] = "F02A", -- Zklink-nova (Alias) + + ["F02B"] = "F02B", -- Zksync-era (Authoritative ID) + ["zksync-era"] = "F02B", -- Zksync-era (Alias) + ["zksync-era-mainnet"] = "F02B", -- Zksync-era (Alias) +} From 381270b6f9a24602b8aae85b7fe198ecd1af2e15 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 15:50:05 +0000 Subject: [PATCH 37/48] Remove .allowed-service.lua from the repository --- .gitignore | 1 + local/path/envoy/.allowed-services.lua | 199 ------------------------- 2 files changed, 1 insertion(+), 199 deletions(-) delete mode 100644 local/path/envoy/.allowed-services.lua diff --git a/.gitignore b/.gitignore index 13801d5d..29446559 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ go.work.sum # Config .*.yaml +.*.lua .config.yaml .config.*.yaml !.config.example.yaml diff --git a/local/path/envoy/.allowed-services.lua b/local/path/envoy/.allowed-services.lua deleted file mode 100644 index 2857667e..00000000 --- a/local/path/envoy/.allowed-services.lua +++ /dev/null @@ -1,199 +0,0 @@ --- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. --- --- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. --- --- eg. the alias "eth" = F00C" enables the URL "http://eth.path.grove.city" to be routed to the service with the ID "F00C". --- --- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. --- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. -return { - -- 1. Shannon Service IDs - ["anvil"] = "anvil", -- Anvil (Authoritative ID) - - -- 2. Morse Service IDs - ["F000"] = "F000", -- Pocket (Authoritative ID) - ["mainnet"] = "F000", -- Pocket (Alias) - ["mainnet-archival"] = "F000", -- Pocket (Alias) - ["pocket"] = "F000", -- Pocket (Alias) - ["pokt-archival"] = "F000", -- Pocket (Alias) - - ["F001"] = "F001", -- Arbitrum-one (Authoritative ID) - ["arbitrum-one"] = "F001", -- Arbitrum-one (Alias) - - ["F002"] = "F002", -- Arbitrum-sepolia-testnet (Authoritative ID) - ["arbitrum-sepolia-archival"] = "F002", -- Arbitrum-sepolia-testnet (Alias) - ["arbitrum-sepolia-testnet"] = "F002", -- Arbitrum-sepolia-testnet (Alias) - - ["F003"] = "F003", -- Avax (Authoritative ID) - ["avax"] = "F003", -- Avax (Alias) - ["avax-archival"] = "F003", -- Avax (Alias) - ["avax-mainnet"] = "F003", -- Avax (Alias) - - ["F004"] = "F004", -- Avax-dfk (Authoritative ID) - ["avax-dfk"] = "F004", -- Avax-dfk (Alias) - - ["F005"] = "F005", -- Base (Authoritative ID) - ["base"] = "F005", -- Base (Alias) - ["base-mainnet"] = "F005", -- Base (Alias) - ["base-mainnet-archival"] = "F005", -- Base (Alias) - - ["F006"] = "F006", -- Base-testnet (Authoritative ID) - ["base-testnet"] = "F006", -- Base-testnet (Alias) - ["base-testnet-archival"] = "F006", -- Base-testnet (Alias) - - ["F008"] = "F008", -- Blast (Authoritative ID) - ["blast"] = "F008", -- Blast (Alias) - ["blast-archival"] = "F008", -- Blast (Alias) - ["blast-mainnet"] = "F008", -- Blast (Alias) - - ["F009"] = "F009", -- Bsc (Authoritative ID) - ["bsc"] = "F009", -- Bsc (Alias) - ["bsc-archival"] = "F009", -- Bsc (Alias) - ["bsc-mainnet"] = "F009", -- Bsc (Alias) - - ["F00A"] = "F00A", -- Boba (Authoritative ID) - ["boba"] = "F00A", -- Boba (Alias) - ["boba-mainnet"] = "F00A", -- Boba (Alias) - - ["F00B"] = "F00B", -- Celo (Authoritative ID) - ["celo"] = "F00B", -- Celo (Alias) - ["celo-mainnet"] = "F00B", -- Celo (Alias) - - ["F00C"] = "F00C", -- Eth (Authoritative ID) - ["eth"] = "F00C", -- Eth (Alias) - ["eth-archival"] = "F00C", -- Eth (Alias) - ["eth-mainnet"] = "F00C", -- Eth (Alias) - ["eth-trace"] = "F00C", -- Eth (Alias) - - ["F00D"] = "F00D", -- Eth-holesky-testnet (Authoritative ID) - ["eth-holesky-testnet"] = "F00D", -- Eth-holesky-testnet (Alias) - ["holesky-fullnode-testnet"] = "F00D", -- Eth-holesky-testnet (Alias) - - ["F00E"] = "F00E", -- Eth-sepolia-testnet (Authoritative ID) - ["eth-sepolia-testnet"] = "F00E", -- Eth-sepolia-testnet (Alias) - ["sepolia"] = "F00E", -- Eth-sepolia-testnet (Alias) - ["sepolia-archival"] = "F00E", -- Eth-sepolia-testnet (Alias) - - ["F00F"] = "F00F", -- Evmos (Authoritative ID) - ["evmos"] = "F00F", -- Evmos (Alias) - ["evmos-mainnet"] = "F00F", -- Evmos (Alias) - - ["F010"] = "F010", -- Fantom (Authoritative ID) - ["fantom"] = "F010", -- Fantom (Alias) - ["fantom-mainnet"] = "F010", -- Fantom (Alias) - - ["F011"] = "F011", -- Fraxtal (Authoritative ID) - ["fraxtal"] = "F011", -- Fraxtal (Alias) - ["fraxtal-archival"] = "F011", -- Fraxtal (Alias) - - ["F012"] = "F012", -- Fuse (Authoritative ID) - ["fuse"] = "F012", -- Fuse (Alias) - ["fuse-archival"] = "F012", -- Fuse (Alias) - ["fuse-mainnet"] = "F012", -- Fuse (Alias) - - ["F013"] = "F013", -- Gnosis (Authoritative ID) - ["gnosis"] = "F013", -- Gnosis (Alias) - ["gnosischain-archival"] = "F013", -- Gnosis (Alias) - ["gnosischain-mainnet"] = "F013", -- Gnosis (Alias) - ["poa-xdai"] = "F013", -- Gnosis (Alias) - - ["F014"] = "F014", -- Harmony (Authoritative ID) - ["harmony"] = "F014", -- Harmony (Alias) - ["harmony-0"] = "F014", -- Harmony (Alias) - - ["F015"] = "F015", -- Iotex (Authoritative ID) - ["iotex"] = "F015", -- Iotex (Alias) - ["iotex-mainnet"] = "F015", -- Iotex (Alias) - - ["F016"] = "F016", -- Kaia (Authoritative ID) - ["kaia"] = "F016", -- Kaia (Alias) - ["kaia-mainnet"] = "F016", -- Kaia (Alias) - ["klaytn-mainnet"] = "F016", -- Kaia (Alias) - - ["F017"] = "F017", -- Kava (Authoritative ID) - ["kava"] = "F017", -- Kava (Alias) - ["kava-mainnet"] = "F017", -- Kava (Alias) - ["kava-mainnet-archival"] = "F017", -- Kava (Alias) - - ["F018"] = "F018", -- Metis (Authoritative ID) - ["metis"] = "F018", -- Metis (Alias) - ["metis-mainnet"] = "F018", -- Metis (Alias) - - ["F019"] = "F019", -- Moonbeam (Authoritative ID) - ["moonbeam"] = "F019", -- Moonbeam (Alias) - ["moonbeam-mainnet"] = "F019", -- Moonbeam (Alias) - - ["F01A"] = "F01A", -- Moonriver (Authoritative ID) - ["moonriver"] = "F01A", -- Moonriver (Alias) - ["moonriver-mainnet"] = "F01A", -- Moonriver (Alias) - - ["F01B"] = "F01B", -- Near (Authoritative ID) - ["near"] = "F01B", -- Near (Alias) - ["near-mainnet"] = "F01B", -- Near (Alias) - - ["F01C"] = "F01C", -- Oasys (Authoritative ID) - ["oasys"] = "F01C", -- Oasys (Alias) - ["oasys-mainnet"] = "F01C", -- Oasys (Alias) - ["oasys-mainnet-archival"] = "F01C", -- Oasys (Alias) - - ["F01D"] = "F01D", -- Optimism (Authoritative ID) - ["optimism"] = "F01D", -- Optimism (Alias) - ["optimism-archival"] = "F01D", -- Optimism (Alias) - ["optimism-mainnet"] = "F01D", -- Optimism (Alias) - - ["F01E"] = "F01E", -- Optimism-sepolia-testnet (Authoritative ID) - ["optimism-sepolia-archival"] = "F01E", -- Optimism-sepolia-testnet (Alias) - ["optimism-sepolia-testnet"] = "F01E", -- Optimism-sepolia-testnet (Alias) - - ["F01F"] = "F01F", -- Opbnb (Authoritative ID) - ["opbnb"] = "F01F", -- Opbnb (Alias) - ["opbnb-archival"] = "F01F", -- Opbnb (Alias) - - ["F020"] = "F020", -- Osmosis (Authoritative ID) - ["osmosis"] = "F020", -- Osmosis (Alias) - ["osmosis-mainnet"] = "F020", -- Osmosis (Alias) - - ["F021"] = "F021", -- Polygon (Authoritative ID) - ["poly-archival"] = "F021", -- Polygon (Alias) - ["polygon"] = "F021", -- Polygon (Alias) - ["poly-mainnet"] = "F021", -- Polygon (Alias) - - ["F022"] = "F022", -- Polygon-amoy-testnet (Authoritative ID) - ["amoy-testnet-archival"] = "F022", -- Polygon-amoy-testnet (Alias) - ["polygon-amoy-testnet"] = "F022", -- Polygon-amoy-testnet (Alias) - - ["F023"] = "F023", -- Radix (Authoritative ID) - ["radix"] = "F023", -- Radix (Alias) - ["radix-mainnet"] = "F023", -- Radix (Alias) - - ["F024"] = "F024", -- Scroll (Authoritative ID) - ["scroll"] = "F024", -- Scroll (Alias) - - ["F025"] = "F025", -- Solana (Authoritative ID) - ["solana"] = "F025", -- Solana (Alias) - ["solana-mainnet"] = "F025", -- Solana (Alias) - ["solana-mainnet-custom"] = "F025", -- Solana (Alias) - - ["F026"] = "F026", -- Sui (Authoritative ID) - ["sui"] = "F026", -- Sui (Alias) - ["sui-mainnet"] = "F026", -- Sui (Alias) - - ["F027"] = "F027", -- Taiko (Authoritative ID) - ["taiko"] = "F027", -- Taiko (Alias) - - ["F028"] = "F028", -- Taiko-hekla-testnet (Authoritative ID) - ["taiko-hekla-testnet"] = "F028", -- Taiko-hekla-testnet (Alias) - - ["F029"] = "F029", -- Polygon-zkevm (Authoritative ID) - ["polygon-zkevm"] = "F029", -- Polygon-zkevm (Alias) - ["polygon-zkevm-mainnet"] = "F029", -- Polygon-zkevm (Alias) - ["zkevm-polygon-mainnet"] = "F029", -- Polygon-zkevm (Alias) - - ["F02A"] = "F02A", -- Zklink-nova (Authoritative ID) - ["zklink-nova"] = "F02A", -- Zklink-nova (Alias) - ["zklink-nova-archival"] = "F02A", -- Zklink-nova (Alias) - - ["F02B"] = "F02B", -- Zksync-era (Authoritative ID) - ["zksync-era"] = "F02B", -- Zksync-era (Alias) - ["zksync-era-mainnet"] = "F02B", -- Zksync-era (Alias) -} From 9917603aca73e2751a8062339e57f68b686f06a7 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 15:50:05 +0000 Subject: [PATCH 38/48] Remove .allowed-service.lua from the repository --- .gitignore | 1 + Makefile | 2 +- local/path/envoy/.allowed-services.lua | 199 ------------------------- 3 files changed, 2 insertions(+), 200 deletions(-) delete mode 100644 local/path/envoy/.allowed-services.lua diff --git a/.gitignore b/.gitignore index 13801d5d..29446559 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ go.work.sum # Config .*.yaml +.*.lua .config.yaml .config.*.yaml !.config.example.yaml diff --git a/Makefile b/Makefile index 1db013df..c1018acf 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ path_down: localnet_down ## Tears down local Tilt development environment which ######################### .PHONY: test_all ## Run all tests -test_all: test_unit test_auth_plugin test_e2e_shannon_relay test_e2e_morse_relay +test_all: test_unit test_auth_server test_e2e_shannon_relay test_e2e_morse_relay .PHONY: test_unit test_unit: ## Run all unit tests diff --git a/local/path/envoy/.allowed-services.lua b/local/path/envoy/.allowed-services.lua deleted file mode 100644 index 2857667e..00000000 --- a/local/path/envoy/.allowed-services.lua +++ /dev/null @@ -1,199 +0,0 @@ --- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. --- --- If you wish to define aliases for existing services, you must define the alias as the key and the service ID as the value. --- --- eg. the alias "eth" = F00C" enables the URL "http://eth.path.grove.city" to be routed to the service with the ID "F00C". --- --- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. --- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. -return { - -- 1. Shannon Service IDs - ["anvil"] = "anvil", -- Anvil (Authoritative ID) - - -- 2. Morse Service IDs - ["F000"] = "F000", -- Pocket (Authoritative ID) - ["mainnet"] = "F000", -- Pocket (Alias) - ["mainnet-archival"] = "F000", -- Pocket (Alias) - ["pocket"] = "F000", -- Pocket (Alias) - ["pokt-archival"] = "F000", -- Pocket (Alias) - - ["F001"] = "F001", -- Arbitrum-one (Authoritative ID) - ["arbitrum-one"] = "F001", -- Arbitrum-one (Alias) - - ["F002"] = "F002", -- Arbitrum-sepolia-testnet (Authoritative ID) - ["arbitrum-sepolia-archival"] = "F002", -- Arbitrum-sepolia-testnet (Alias) - ["arbitrum-sepolia-testnet"] = "F002", -- Arbitrum-sepolia-testnet (Alias) - - ["F003"] = "F003", -- Avax (Authoritative ID) - ["avax"] = "F003", -- Avax (Alias) - ["avax-archival"] = "F003", -- Avax (Alias) - ["avax-mainnet"] = "F003", -- Avax (Alias) - - ["F004"] = "F004", -- Avax-dfk (Authoritative ID) - ["avax-dfk"] = "F004", -- Avax-dfk (Alias) - - ["F005"] = "F005", -- Base (Authoritative ID) - ["base"] = "F005", -- Base (Alias) - ["base-mainnet"] = "F005", -- Base (Alias) - ["base-mainnet-archival"] = "F005", -- Base (Alias) - - ["F006"] = "F006", -- Base-testnet (Authoritative ID) - ["base-testnet"] = "F006", -- Base-testnet (Alias) - ["base-testnet-archival"] = "F006", -- Base-testnet (Alias) - - ["F008"] = "F008", -- Blast (Authoritative ID) - ["blast"] = "F008", -- Blast (Alias) - ["blast-archival"] = "F008", -- Blast (Alias) - ["blast-mainnet"] = "F008", -- Blast (Alias) - - ["F009"] = "F009", -- Bsc (Authoritative ID) - ["bsc"] = "F009", -- Bsc (Alias) - ["bsc-archival"] = "F009", -- Bsc (Alias) - ["bsc-mainnet"] = "F009", -- Bsc (Alias) - - ["F00A"] = "F00A", -- Boba (Authoritative ID) - ["boba"] = "F00A", -- Boba (Alias) - ["boba-mainnet"] = "F00A", -- Boba (Alias) - - ["F00B"] = "F00B", -- Celo (Authoritative ID) - ["celo"] = "F00B", -- Celo (Alias) - ["celo-mainnet"] = "F00B", -- Celo (Alias) - - ["F00C"] = "F00C", -- Eth (Authoritative ID) - ["eth"] = "F00C", -- Eth (Alias) - ["eth-archival"] = "F00C", -- Eth (Alias) - ["eth-mainnet"] = "F00C", -- Eth (Alias) - ["eth-trace"] = "F00C", -- Eth (Alias) - - ["F00D"] = "F00D", -- Eth-holesky-testnet (Authoritative ID) - ["eth-holesky-testnet"] = "F00D", -- Eth-holesky-testnet (Alias) - ["holesky-fullnode-testnet"] = "F00D", -- Eth-holesky-testnet (Alias) - - ["F00E"] = "F00E", -- Eth-sepolia-testnet (Authoritative ID) - ["eth-sepolia-testnet"] = "F00E", -- Eth-sepolia-testnet (Alias) - ["sepolia"] = "F00E", -- Eth-sepolia-testnet (Alias) - ["sepolia-archival"] = "F00E", -- Eth-sepolia-testnet (Alias) - - ["F00F"] = "F00F", -- Evmos (Authoritative ID) - ["evmos"] = "F00F", -- Evmos (Alias) - ["evmos-mainnet"] = "F00F", -- Evmos (Alias) - - ["F010"] = "F010", -- Fantom (Authoritative ID) - ["fantom"] = "F010", -- Fantom (Alias) - ["fantom-mainnet"] = "F010", -- Fantom (Alias) - - ["F011"] = "F011", -- Fraxtal (Authoritative ID) - ["fraxtal"] = "F011", -- Fraxtal (Alias) - ["fraxtal-archival"] = "F011", -- Fraxtal (Alias) - - ["F012"] = "F012", -- Fuse (Authoritative ID) - ["fuse"] = "F012", -- Fuse (Alias) - ["fuse-archival"] = "F012", -- Fuse (Alias) - ["fuse-mainnet"] = "F012", -- Fuse (Alias) - - ["F013"] = "F013", -- Gnosis (Authoritative ID) - ["gnosis"] = "F013", -- Gnosis (Alias) - ["gnosischain-archival"] = "F013", -- Gnosis (Alias) - ["gnosischain-mainnet"] = "F013", -- Gnosis (Alias) - ["poa-xdai"] = "F013", -- Gnosis (Alias) - - ["F014"] = "F014", -- Harmony (Authoritative ID) - ["harmony"] = "F014", -- Harmony (Alias) - ["harmony-0"] = "F014", -- Harmony (Alias) - - ["F015"] = "F015", -- Iotex (Authoritative ID) - ["iotex"] = "F015", -- Iotex (Alias) - ["iotex-mainnet"] = "F015", -- Iotex (Alias) - - ["F016"] = "F016", -- Kaia (Authoritative ID) - ["kaia"] = "F016", -- Kaia (Alias) - ["kaia-mainnet"] = "F016", -- Kaia (Alias) - ["klaytn-mainnet"] = "F016", -- Kaia (Alias) - - ["F017"] = "F017", -- Kava (Authoritative ID) - ["kava"] = "F017", -- Kava (Alias) - ["kava-mainnet"] = "F017", -- Kava (Alias) - ["kava-mainnet-archival"] = "F017", -- Kava (Alias) - - ["F018"] = "F018", -- Metis (Authoritative ID) - ["metis"] = "F018", -- Metis (Alias) - ["metis-mainnet"] = "F018", -- Metis (Alias) - - ["F019"] = "F019", -- Moonbeam (Authoritative ID) - ["moonbeam"] = "F019", -- Moonbeam (Alias) - ["moonbeam-mainnet"] = "F019", -- Moonbeam (Alias) - - ["F01A"] = "F01A", -- Moonriver (Authoritative ID) - ["moonriver"] = "F01A", -- Moonriver (Alias) - ["moonriver-mainnet"] = "F01A", -- Moonriver (Alias) - - ["F01B"] = "F01B", -- Near (Authoritative ID) - ["near"] = "F01B", -- Near (Alias) - ["near-mainnet"] = "F01B", -- Near (Alias) - - ["F01C"] = "F01C", -- Oasys (Authoritative ID) - ["oasys"] = "F01C", -- Oasys (Alias) - ["oasys-mainnet"] = "F01C", -- Oasys (Alias) - ["oasys-mainnet-archival"] = "F01C", -- Oasys (Alias) - - ["F01D"] = "F01D", -- Optimism (Authoritative ID) - ["optimism"] = "F01D", -- Optimism (Alias) - ["optimism-archival"] = "F01D", -- Optimism (Alias) - ["optimism-mainnet"] = "F01D", -- Optimism (Alias) - - ["F01E"] = "F01E", -- Optimism-sepolia-testnet (Authoritative ID) - ["optimism-sepolia-archival"] = "F01E", -- Optimism-sepolia-testnet (Alias) - ["optimism-sepolia-testnet"] = "F01E", -- Optimism-sepolia-testnet (Alias) - - ["F01F"] = "F01F", -- Opbnb (Authoritative ID) - ["opbnb"] = "F01F", -- Opbnb (Alias) - ["opbnb-archival"] = "F01F", -- Opbnb (Alias) - - ["F020"] = "F020", -- Osmosis (Authoritative ID) - ["osmosis"] = "F020", -- Osmosis (Alias) - ["osmosis-mainnet"] = "F020", -- Osmosis (Alias) - - ["F021"] = "F021", -- Polygon (Authoritative ID) - ["poly-archival"] = "F021", -- Polygon (Alias) - ["polygon"] = "F021", -- Polygon (Alias) - ["poly-mainnet"] = "F021", -- Polygon (Alias) - - ["F022"] = "F022", -- Polygon-amoy-testnet (Authoritative ID) - ["amoy-testnet-archival"] = "F022", -- Polygon-amoy-testnet (Alias) - ["polygon-amoy-testnet"] = "F022", -- Polygon-amoy-testnet (Alias) - - ["F023"] = "F023", -- Radix (Authoritative ID) - ["radix"] = "F023", -- Radix (Alias) - ["radix-mainnet"] = "F023", -- Radix (Alias) - - ["F024"] = "F024", -- Scroll (Authoritative ID) - ["scroll"] = "F024", -- Scroll (Alias) - - ["F025"] = "F025", -- Solana (Authoritative ID) - ["solana"] = "F025", -- Solana (Alias) - ["solana-mainnet"] = "F025", -- Solana (Alias) - ["solana-mainnet-custom"] = "F025", -- Solana (Alias) - - ["F026"] = "F026", -- Sui (Authoritative ID) - ["sui"] = "F026", -- Sui (Alias) - ["sui-mainnet"] = "F026", -- Sui (Alias) - - ["F027"] = "F027", -- Taiko (Authoritative ID) - ["taiko"] = "F027", -- Taiko (Alias) - - ["F028"] = "F028", -- Taiko-hekla-testnet (Authoritative ID) - ["taiko-hekla-testnet"] = "F028", -- Taiko-hekla-testnet (Alias) - - ["F029"] = "F029", -- Polygon-zkevm (Authoritative ID) - ["polygon-zkevm"] = "F029", -- Polygon-zkevm (Alias) - ["polygon-zkevm-mainnet"] = "F029", -- Polygon-zkevm (Alias) - ["zkevm-polygon-mainnet"] = "F029", -- Polygon-zkevm (Alias) - - ["F02A"] = "F02A", -- Zklink-nova (Authoritative ID) - ["zklink-nova"] = "F02A", -- Zklink-nova (Alias) - ["zklink-nova-archival"] = "F02A", -- Zklink-nova (Alias) - - ["F02B"] = "F02B", -- Zksync-era (Authoritative ID) - ["zksync-era"] = "F02B", -- Zksync-era (Alias) - ["zksync-era-mainnet"] = "F02B", -- Zksync-era (Alias) -} From 660d6cee7aca2b1875093c6bc310e691dc689463 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 19:31:46 +0000 Subject: [PATCH 39/48] chore: remove deprecated 0021 alias --- config/service_alias.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/config/service_alias.go b/config/service_alias.go index e77d08b0..f8e90fc9 100644 --- a/config/service_alias.go +++ b/config/service_alias.go @@ -60,9 +60,6 @@ var testQoSTypes = map[protocol.ServiceID]ServiceQoSType{ // TODO_TECHDEBT(@fredteumer): Revisit and consider removing these once #105 is complete. // Service IDs transferred from Morse to Shannon for backwards compatibility. var legacyMorseQoSTypes = map[protocol.ServiceID]ServiceQoSType{ - // Deprecated Morse EVM chains - "0021": ServiceIDEVM, // ETH Mainnet - // All Morse EVM F-Chain Services as of 12/17/2024 (#103) "F001": ServiceIDEVM, // Arbitrum One "F002": ServiceIDEVM, // Arbitrum Sepolia Testnet From 2e14f57e1d72582b2a7848606845309a9aa6c0f2 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 19:43:02 +0000 Subject: [PATCH 40/48] fix: update E2E test branch fixes --- e2e/morse_relay_test.go | 6 ++++-- e2e/shannon_relay_test.go | 6 ++++-- request/parser.go | 13 ++++--------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/e2e/morse_relay_test.go b/e2e/morse_relay_test.go index a9e94163..8cd7c684 100644 --- a/e2e/morse_relay_test.go +++ b/e2e/morse_relay_test.go @@ -9,8 +9,10 @@ import ( "net/http" "testing" - "github.com/buildwithgrove/path/protocol" "github.com/stretchr/testify/require" + + "github.com/buildwithgrove/path/protocol" + "github.com/buildwithgrove/path/request" ) const ( @@ -72,7 +74,7 @@ func Test_MorseRelay(t *testing.T) { req.Header.Set("Content-Type", "application/json") // Assign the target service ID to the request header. - req.Header.Set("target-service-id", string(test.serviceID)) + req.Header.Set(request.HTTPHeaderTargetServiceID, string(test.serviceID)) var success bool var allErrors []error diff --git a/e2e/shannon_relay_test.go b/e2e/shannon_relay_test.go index ca95542f..67e20bdb 100644 --- a/e2e/shannon_relay_test.go +++ b/e2e/shannon_relay_test.go @@ -11,8 +11,10 @@ import ( "net/http" "testing" - "github.com/buildwithgrove/path/protocol" "github.com/stretchr/testify/require" + + "github.com/buildwithgrove/path/protocol" + "github.com/buildwithgrove/path/request" ) const ( @@ -70,7 +72,7 @@ func Test_ShannonRelay(t *testing.T) { req.Header.Set("Content-Type", "application/json") // Assign the target service ID to the request header. - req.Header.Set("target-service-id", string(test.serviceID)) + req.Header.Set(request.HTTPHeaderTargetServiceID, string(test.serviceID)) var success bool var allErrors []error diff --git a/request/parser.go b/request/parser.go index 8090de74..8b78bd23 100644 --- a/request/parser.go +++ b/request/parser.go @@ -26,12 +26,10 @@ import ( // https://www.rfc-editor.org/rfc/rfc6648#section-3 const HTTPHeaderTargetServiceID = "target-service-id" -type ( - Parser struct { - QoSServices map[protocol.ServiceID]gateway.QoSService - Logger polylog.Logger - } -) +type Parser struct { + QoSServices map[protocol.ServiceID]gateway.QoSService + Logger polylog.Logger +} func NewParser(enabledServices map[protocol.ServiceID]gateway.QoSService, logger polylog.Logger) (*Parser, error) { return &Parser{ @@ -43,7 +41,6 @@ func NewParser(enabledServices map[protocol.ServiceID]gateway.QoSService, logger /* --------------------------------- HTTP Request Parsing -------------------------------- */ func (p *Parser) GetQoSService(ctx context.Context, req *http.Request) (protocol.ServiceID, gateway.QoSService, error) { - serviceID, err := p.getServiceID(req) if err != nil { return "", nil, err @@ -66,10 +63,8 @@ func (p *Parser) GetHTTPErrorResponse(ctx context.Context, err error) gateway.HT // getServiceID extracts the target service ID from the HTTP request's headers. func (p *Parser) getServiceID(req *http.Request) (protocol.ServiceID, error) { - // Prefer the custom HTTP Header for specification of the Target Service ID if serviceID := req.Header.Get(HTTPHeaderTargetServiceID); serviceID != "" { return protocol.ServiceID(serviceID), nil } - return "", errNoServiceIDProvided } From aa63306f59d1d3d837e83a8928ca0af25ac18d2c Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Wed, 18 Dec 2024 21:12:23 +0000 Subject: [PATCH 41/48] fix: disable lua filter for /healthz route --- envoy/envoy.template.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 5cc387e5..44d78965 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -233,6 +233,11 @@ static_resources: route: cluster: path_gateway_service typed_per_filter_config: + # Disable Lua filter for health check requests. + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + disabled: true + # Disable ext_authz filter for health check requests. envoy.filters.http.ext_authz: "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute disabled: true From c24867df5953e6ece1023ebaa085d9fd9bd16067 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Thu, 19 Dec 2024 12:33:53 -0800 Subject: [PATCH 42/48] Review envoy/allowed-services.template.lua --- envoy/allowed-services.template.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua index ec818197..a4c9f203 100644 --- a/envoy/allowed-services.template.lua +++ b/envoy/allowed-services.template.lua @@ -1,4 +1,5 @@ -- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. +-- -- The service IDs configured here are used in the `envoy.filters.http.lua` HTTP filter defined in `.envoy.yaml` config file. -- The `.allowed-services.lua` file must be mounted as a file in the Envoy Proxy container at `/etc/envoy/.allowed-services.lua`. -- @@ -9,6 +10,7 @@ -- -- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. -- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. +-- return { -- 1. Shannon Service IDs ["anvil"] = "anvil", -- Anvil (Authoritative ID) From 3e78dce70eca6756979bfc2358a6aaabff16949c Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 19 Dec 2024 20:44:04 +0000 Subject: [PATCH 43/48] chore: update endpoint store test to be consistent with example gateway endpoints file --- .../endpoint_store/client_mock_test.go | 3 +- .../endpoint_store/endpoint_store_test.go | 111 ++++++++---------- 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/envoy/auth_server/endpoint_store/client_mock_test.go b/envoy/auth_server/endpoint_store/client_mock_test.go index fe721772..543fe519 100644 --- a/envoy/auth_server/endpoint_store/client_mock_test.go +++ b/envoy/auth_server/endpoint_store/client_mock_test.go @@ -13,9 +13,10 @@ import ( context "context" reflect "reflect" - proto "github.com/buildwithgrove/path/envoy/auth_server/proto" gomock "go.uber.org/mock/gomock" grpc "google.golang.org/grpc" + + proto "github.com/buildwithgrove/path/envoy/auth_server/proto" ) // MockGatewayEndpointsClient is a mock of GatewayEndpointsClient interface. diff --git a/envoy/auth_server/endpoint_store/endpoint_store_test.go b/envoy/auth_server/endpoint_store/endpoint_store_test.go index 51b5b002..5a961ec0 100644 --- a/envoy/auth_server/endpoint_store/endpoint_store_test.go +++ b/envoy/auth_server/endpoint_store/endpoint_store_test.go @@ -5,12 +5,13 @@ import ( "io" "testing" - "github.com/buildwithgrove/path/envoy/auth_server/proto" "github.com/pokt-network/poktroll/pkg/polylog/polyzero" "github.com/stretchr/testify/require" gomock "go.uber.org/mock/gomock" "google.golang.org/grpc" protoPkg "google.golang.org/protobuf/proto" + + "github.com/buildwithgrove/path/envoy/auth_server/proto" ) // MockStream is a mock implementation of the grpc.ClientStream interface. @@ -53,40 +54,40 @@ func Test_GetGatewayEndpoint(t *testing.T) { }{ { name: "should return gateway endpoint when found", - endpointID: "endpoint_1", - expectedGatewayEndpoint: getTestGatewayEndpoints().Endpoints["endpoint_1"], + endpointID: "endpoint_1_static_key", + expectedGatewayEndpoint: getTestGatewayEndpoints().Endpoints["endpoint_1_static_key"], expectedEndpointFound: true, }, { name: "should return different gateway endpoint when found", - endpointID: "endpoint_2", - expectedGatewayEndpoint: getTestGatewayEndpoints().Endpoints["endpoint_2"], + endpointID: "endpoint_2_jwt", + expectedGatewayEndpoint: getTestGatewayEndpoints().Endpoints["endpoint_2_jwt"], expectedEndpointFound: true, }, { name: "should return brand new gateway endpoint when update is received for new endpoint", - endpointID: "endpoint_3", - update: getTestUpdate("endpoint_3"), - expectedGatewayEndpoint: getTestUpdate("endpoint_3").GatewayEndpoint, + endpointID: "endpoint_3_no_auth", + update: getTestUpdate("endpoint_3_no_auth"), + expectedGatewayEndpoint: getTestUpdate("endpoint_3_no_auth").GatewayEndpoint, expectedEndpointFound: true, }, { name: "should return updated existing gateway endpoint when update is received for existing endpoint", - endpointID: "endpoint_2", - update: getTestUpdate("endpoint_2"), - expectedGatewayEndpoint: getTestUpdate("endpoint_2").GatewayEndpoint, + endpointID: "endpoint_2_jwt", + update: getTestUpdate("endpoint_2_jwt"), + expectedGatewayEndpoint: getTestUpdate("endpoint_2_jwt").GatewayEndpoint, expectedEndpointFound: true, }, { name: "should not return gateway endpoint when update is received to delete endpoint", - endpointID: "endpoint_1", - update: getTestUpdate("endpoint_1"), + endpointID: "endpoint_1_static_key", + update: getTestUpdate("endpoint_1_static_key"), expectedGatewayEndpoint: nil, expectedEndpointFound: false, }, { name: "should return false when gateway endpoint not found", - endpointID: "endpoint_3", + endpointID: "endpoint_3_no_auth", expectedGatewayEndpoint: nil, expectedEndpointFound: false, }, @@ -122,47 +123,39 @@ func Test_GetGatewayEndpoint(t *testing.T) { func getTestGatewayEndpoints() *proto.AuthDataResponse { return &proto.AuthDataResponse{ Endpoints: map[string]*proto.GatewayEndpoint{ - "endpoint_1": { - EndpointId: "endpoint_1", + "endpoint_1_static_key": { + EndpointId: "endpoint_1_static_key", Auth: &proto.Auth{ - AuthType: &proto.Auth_Jwt{ - Jwt: &proto.JWT{ - AuthorizedUsers: map[string]*proto.Empty{ - "auth0|user_1": {}, - "auth0|user_4": {}, - }, + AuthType: &proto.Auth_StaticApiKey{ + StaticApiKey: &proto.StaticAPIKey{ + ApiKey: "api_key_1", }, }, }, - RateLimiting: &proto.RateLimiting{ - ThroughputLimit: 30, - CapacityLimit: 100, - CapacityLimitPeriod: proto.CapacityLimitPeriod_CAPACITY_LIMIT_PERIOD_DAILY, - }, + RateLimiting: &proto.RateLimiting{}, Metadata: &proto.Metadata{ AccountId: "account_1", - PlanType: "PLAN_FREE", + PlanType: "PLAN_UNLIMITED", + Email: "amos.burton@opa.belt", }, }, - "endpoint_2": { - EndpointId: "endpoint_2", + "endpoint_2_jwt": { + EndpointId: "endpoint_2_jwt", Auth: &proto.Auth{ AuthType: &proto.Auth_Jwt{ Jwt: &proto.JWT{ AuthorizedUsers: map[string]*proto.Empty{ + "auth0|user_1": {}, "auth0|user_2": {}, }, }, }, }, - RateLimiting: &proto.RateLimiting{ - ThroughputLimit: 50, - CapacityLimit: 200, - CapacityLimitPeriod: proto.CapacityLimitPeriod_CAPACITY_LIMIT_PERIOD_MONTHLY, - }, + RateLimiting: &proto.RateLimiting{}, Metadata: &proto.Metadata{ AccountId: "account_2", PlanType: "PLAN_UNLIMITED", + Email: "paul.atreides@arrakis.com", }, }, }, @@ -176,59 +169,51 @@ func getTestGatewayEndpoints() *proto.AuthDataResponse { // 3. An existing GatewayEndpoint was deleted (endpoint_1) func getTestUpdate(endpointID string) *proto.AuthDataUpdate { updatesMap := map[string]*proto.AuthDataUpdate{ - "endpoint_3": { - EndpointId: "endpoint_3", + "endpoint_2_jwt": { + EndpointId: "endpoint_2_jwt", GatewayEndpoint: &proto.GatewayEndpoint{ - EndpointId: "endpoint_3", + EndpointId: "endpoint_2_jwt", Auth: &proto.Auth{ AuthType: &proto.Auth_Jwt{ Jwt: &proto.JWT{ AuthorizedUsers: map[string]*proto.Empty{ - "auth0|user_3": {}, + "auth0|user_1": {}, + "auth0|user_2": {}, }, }, }, }, - RateLimiting: &proto.RateLimiting{ - ThroughputLimit: 100, - CapacityLimit: 500, - CapacityLimitPeriod: proto.CapacityLimitPeriod_CAPACITY_LIMIT_PERIOD_MONTHLY, - }, + RateLimiting: &proto.RateLimiting{}, Metadata: &proto.Metadata{ - AccountId: "account_3", - PlanType: "PLAN_ENTERPRISE", + AccountId: "account_2", + PlanType: "PLAN_UNLIMITED", + Email: "paul.atreides@arrakis.com", }, }, Delete: false, }, - "endpoint_2": { - EndpointId: "endpoint_2", + "endpoint_3_no_auth": { + EndpointId: "endpoint_3_no_auth", GatewayEndpoint: &proto.GatewayEndpoint{ - EndpointId: "endpoint_2", + EndpointId: "endpoint_3_no_auth", Auth: &proto.Auth{ - AuthType: &proto.Auth_Jwt{ - Jwt: &proto.JWT{ - AuthorizedUsers: map[string]*proto.Empty{ - "auth0|user_2": {}, - "auth0|user_5": {}, - }, - }, - }, + AuthType: &proto.Auth_NoAuth{}, }, RateLimiting: &proto.RateLimiting{ - ThroughputLimit: 60, - CapacityLimit: 250, - CapacityLimitPeriod: proto.CapacityLimitPeriod_CAPACITY_LIMIT_PERIOD_WEEKLY, + ThroughputLimit: 30, + CapacityLimit: 100_000, + CapacityLimitPeriod: proto.CapacityLimitPeriod_CAPACITY_LIMIT_PERIOD_MONTHLY, }, Metadata: &proto.Metadata{ AccountId: "account_2", - PlanType: "PLAN_UNLIMITED", + PlanType: "PLAN_FREE", + Email: "frodo.baggins@shire.io", }, }, Delete: false, }, - "endpoint_1": { - EndpointId: "endpoint_1", + "endpoint_1_static_key": { + EndpointId: "endpoint_1_static_key", Delete: true, }, } From da64767f1fef2e341f71a70337b0103dbb7860d6 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 19 Dec 2024 20:47:38 +0000 Subject: [PATCH 44/48] chore: add link to service_qos.go file --- envoy/allowed-services.template.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/envoy/allowed-services.template.lua b/envoy/allowed-services.template.lua index ec818197..f09c649a 100644 --- a/envoy/allowed-services.template.lua +++ b/envoy/allowed-services.template.lua @@ -1,4 +1,4 @@ --- IMPORTANT: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. +-- IMPORTANT README: All Services for the PATH Service Gateway must be listed here for Envoy Proxy to forward requests to PATH. -- The service IDs configured here are used in the `envoy.filters.http.lua` HTTP filter defined in `.envoy.yaml` config file. -- The `.allowed-services.lua` file must be mounted as a file in the Envoy Proxy container at `/etc/envoy/.allowed-services.lua`. -- @@ -7,8 +7,8 @@ -- eg 1. the service ID ["F000"] = "F000" enables Envoy to forward requests with the subdomain "F000.path.grove.city" to PATH with the service ID "F000". -- eg 2. the alias ["pocket"] = "F000" enables Envoy to forward requests with the subdomain "pocket.path.grove.city" to PATH with the service ID "F000". -- --- To utilize PATH's Quality of Service (QoS) features, the service ID must match the value in PATH's `qos` module. --- TODO_IMPROVE(@commoddity): Add link to the file & line in the QoS module once 'no-op' QoS feature is completed. +-- IMPORTANT README: To utilize PATH's Quality of Service (QoS) features, the authoritative service ID values must match the values in the `ServiceQoSTypes` +-- map in `config/service_qos.go`. See here for further details: https://github.com/buildwithgrove/path/blob/main/config/service_qos.go#L34 return { -- 1. Shannon Service IDs ["anvil"] = "anvil", -- Anvil (Authoritative ID) From 783bca170f3b9b36c71acecf04db65cb4f8cdbf1 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 19 Dec 2024 21:03:52 +0000 Subject: [PATCH 45/48] chore: fix casing in header examples --- docusaurus/docs/develop/path/introduction.md | 2 +- request/parser.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docusaurus/docs/develop/path/introduction.md b/docusaurus/docs/develop/path/introduction.md index dd604f21..bd180f50 100644 --- a/docusaurus/docs/develop/path/introduction.md +++ b/docusaurus/docs/develop/path/introduction.md @@ -151,7 +151,7 @@ docker pull ghcr.io/buildwithgrove/path curl http://localhost:3000/v1 \ -X POST \ -H "Content-Type: application/json" \ - -H "Target-Service-ID: eth" \ + -H "target-service-id: eth" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` diff --git a/request/parser.go b/request/parser.go index 674c4bcc..47106ec6 100644 --- a/request/parser.go +++ b/request/parser.go @@ -24,7 +24,7 @@ import ( const HTTPHeaderTargetServiceID = "target-service-id" // The Parser struct is responsible for parsing the authoritative service ID from the request's -// 'Target-Service-ID' header and returning the corresponding QoS service implementation. +// 'target-service-id' header and returning the corresponding QoS service implementation. type Parser struct { qosServices map[protocol.ServiceID]gateway.QoSService logger polylog.Logger @@ -58,7 +58,7 @@ func (p *Parser) GetQoSService(ctx context.Context, req *http.Request) (protocol return serviceID, noop.NoOpQoS{}, nil } -// getServiceID extracts the authoritative service ID from the HTTP request's `Target-Service-ID` header. +// getServiceID extracts the authoritative service ID from the HTTP request's `target-service-id` header. func (p *Parser) getServiceID(req *http.Request) (protocol.ServiceID, error) { if serviceID := req.Header.Get(HTTPHeaderTargetServiceID); serviceID != "" { return protocol.ServiceID(serviceID), nil From 52a147e7088470085ad4e8990932907fecfd60b6 Mon Sep 17 00:00:00 2001 From: Pascal van Leeuwen Date: Thu, 19 Dec 2024 21:54:39 +0000 Subject: [PATCH 46/48] chore: makefile target fix and remove unused struct in config --- Makefile | 2 +- config/config.go | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 84929f4b..e6bd05e8 100644 --- a/Makefile +++ b/Makefile @@ -168,7 +168,7 @@ config_shannon_localnet: ## Create a localnet config file to serve as a Shannon copy_morse_config: ## copies the example morse configuration yaml file to .config.yaml file @if [ ! -f ./bin/config/.config.yaml ]; then \ mkdir -p bin/config; \ - cp ./cmd/.config.morse_example.yaml ./bin/config/.config.yaml; \ + cp ./config/examples/config.morse_example.yaml ./bin/config/.config.yaml; \ echo "######################################################################"; \ echo "### Created ./bin/config/.config.yaml ###"; \ echo "### README: Please update the the following in .morse.config.yaml: ###"; \ diff --git a/config/config.go b/config/config.go index fa40a14d..d58c1675 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,6 @@ package config import ( "errors" "os" - "time" "gopkg.in/yaml.v3" @@ -16,20 +15,15 @@ import ( // GatewayConfig is the top level struct that contains configuration details // that which are parsed from a YAML config file. It contains all the various // configuration details that are needed to operate a gateway. -type ( - GatewayConfig struct { - // Only one of the following configs may be set - MorseConfig *morse.MorseGatewayConfig `yaml:"morse_config"` - ShannonConfig *shannon.ShannonGatewayConfig `yaml:"shannon_config"` - - Router RouterConfig `yaml:"router_config"` - HydratorConfig EndpointHydratorConfig `yaml:"hydrator_config"` - MessagingConfig MessagingConfig `yaml:"messaging_config"` - } - ServiceConfig struct { - RequestTimeout time.Duration `yaml:"request_timeout"` - } -) +type GatewayConfig struct { + // Only one of the following configs may be set + MorseConfig *morse.MorseGatewayConfig `yaml:"morse_config"` + ShannonConfig *shannon.ShannonGatewayConfig `yaml:"shannon_config"` + + Router RouterConfig `yaml:"router_config"` + HydratorConfig EndpointHydratorConfig `yaml:"hydrator_config"` + MessagingConfig MessagingConfig `yaml:"messaging_config"` +} // LoadGatewayConfigFromYAML reads a YAML configuration file from the specified path // and unmarshals its content into a GatewayConfig instance. From d21fae04b4096d33b399e106299742f5d7411854 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Thu, 19 Dec 2024 14:05:32 -0800 Subject: [PATCH 47/48] Review envoy_config.md and add support for proper syntax highlighting --- docusaurus/docs/develop/envoy/introduction.md | 19 +- .../docs/operate/configs/_category_.json | 4 +- .../docs/operate/configs/envoy_config.md | 154 +- docusaurus/docusaurus.config.js | 27 + docusaurus/package-lock.json | 5317 +++++++++++------ docusaurus/package.json | 2 + docusaurus/yarn.lock | 912 ++- envoy/envoy.template.yaml | 34 +- 8 files changed, 4558 insertions(+), 1911 deletions(-) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index d3c8d070..fc680b49 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -92,12 +92,11 @@ A [Tiltfile](https://github.com/buildwithgrove/path/blob/main/Tiltfile) is provi - _PADS (PATH Auth Data Server) is provided as a functional implementation of the remote gRPC server that loads data from a YAML file or simple Postgres database._ - _See [PATH Auth Data Server](#path-auth-data-server) for more information._ - ### Architecture Diagram ```mermaid graph TD - User@{ shape: trapezoid, label: "PATH
User
" } + User[/"PATH
User
"\] Envoy[Envoy Proxy] AUTH["Auth Server
"] @@ -109,11 +108,11 @@ graph TD GRPCServer["Remote gRPC Server
(e.g. PADS)"] GRPCDB[("Postgres
Database")] - GRPCConfig@{ shape: notch-rect, label: "YAML Config File" } + GRPCConfig["YAML Config File"] subgraph AUTH["Auth Server (ext_authz)"] GRPCClient["gRPC Client"] - Cache@{ shape: odd, label: "Gateway Endpoint
Data Store" } + Cache["Gateway Endpoint
Data Store"] end User -->|1.Send Request| Envoy @@ -225,7 +224,6 @@ There are two methods for specifying this header in the request: ### Allowed Services File - The file `local/path/envoy/.allowed-services.lua` defines the mapping of service IDs to the service IDs used by the PATH service. All service IDs (and optional service aliases) used by the PATH service must be defined in this file. @@ -233,6 +231,7 @@ All service IDs (and optional service aliases) used by the PATH service must be :::info _`.allowed-services.lua` format:_ + ```lua return { -- 1. Shannon Service IDs @@ -274,6 +273,7 @@ eg. `host = "anvil.path.grove.city" -> Header: "target-service-id: anvil"` :::info _Example request:_ + ```bash curl http://anvil.localhost:3001/v1 \ -X POST \ @@ -351,6 +351,7 @@ curl http://anvil.localhost:3001/v1 \ A variety of example cURL requests to the PATH service [may be found in the test_requests.mk file](https://github.com/buildwithgrove/path/blob/main/makefiles/test_requests.mk). _eg._ + ```bash ## Test request with no auth, endpoint ID passed in the URL path and the service ID passed as the subdomain make test_request_no_auth_url_path @@ -385,7 +386,7 @@ For GatewayEndpoints with the `AuthType` field set to `JWT_AUTH`, a valid JWT is :::tip -The `make init_envoy` command will prompt you about whether you wish to use JWT authorization. +The `make init_envoy` command will prompt you about whether you wish to use JWT authorization. If you do wish to use it, you will be asked to enter your auth provider's domain and audience. @@ -394,7 +395,7 @@ Auth0 is an example of a JWT issuer that can be used with PATH. Their docs page on JWTs gives a good overview of the JWT format and how to issue JWTs to your users: - [Auth0 JWT Docs](https://auth0.com/docs/secure/tokens/json-web-tokens) -::: + ::: _Example Request Header:_ @@ -568,7 +569,6 @@ The PADS repo also provides a [YAML schema for the `gateway-endpoints.yaml` file ::: - #### Implementing a Custom Remote gRPC Server If the Gateway operator wishes to implement a custom remote gRPC server, the implementation must import the Go `github.com/buildwithgrove/path/envoy/auth_server/proto` package, which is autogenerated from the [`gateway_endpoint.proto`](https://github.com/buildwithgrove/path/blob/main/envoy/auth_server/proto/gateway_endpoint.proto) file. @@ -624,12 +624,11 @@ If you wish to implement your own custom database driver, forking the PADS repo :::info The default throughput limit is **30 requests per second** for GatewayEndpoints with the `PLAN_FREE` plan type based on the `rl-endpoint-id` and `rl-throughput` descriptors. - + _The rate limiting configuration may be configured to suit the needs of the Gateway Operator in the `.ratelimit.yaml` file._ ::: - ### Documentation and Examples As Envoy's rate limiting configuration is fairly complex, this blog article provides a good overview of the configuration options: diff --git a/docusaurus/docs/operate/configs/_category_.json b/docusaurus/docs/operate/configs/_category_.json index e89e5390..ae764883 100644 --- a/docusaurus/docs/operate/configs/_category_.json +++ b/docusaurus/docs/operate/configs/_category_.json @@ -1,8 +1,8 @@ { "label": "PATH Operator Guide", - "position": 3, + "position": 2, "link": { "type": "generated-index", "description": "Documentation for PATH operators." } -} \ No newline at end of file +} diff --git a/docusaurus/docs/operate/configs/envoy_config.md b/docusaurus/docs/operate/configs/envoy_config.md index 0e3200b9..aa61f44b 100644 --- a/docusaurus/docs/operate/configs/envoy_config.md +++ b/docusaurus/docs/operate/configs/envoy_config.md @@ -1,6 +1,6 @@ --- -title: Envoy Proxy config -sidebar_position: 4 +title: Envoy Proxy Configuration +sidebar_position: 1 ---
@@ -10,50 +10,39 @@ sidebar_position: 4
-# Envoy Proxy Configuration +This document describes the configuration options for PATH's Envoy Proxy which is responsible for: -This document describes the configuration options for PATH's Envoy Proxy. +1. Defining the set of allowed services it can process +2. Authorizing incoming requests +3. Rate limiting -In PATH, Envoy Proxy is responsible for: - -- Defining allowed services -- Request authorization -- Rate limiting - -:::info +### tl;dr Just show me the config files There are a total of four files used to configure Envoy Proxy in PATH: -**Envoy Config Files** - -1. `.allowed-services.lua` -2. `.envoy.yaml` -3. `.ratelimit.yaml` - - The templates used to generate these Envoy config files [may be found here](https://github.com/buildwithgrove/path/tree/main/envoy). - -**Gateway Endpoints File** - -4. `.gateway-endpoints.yaml` - - The example `.gateway-endpoints.yaml` file is located in the [PADS repo](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml). +1. `.allowed-services.lua` ([template example](https://github.com/buildwithgrove/path/blob/main/envoy/allowed-services.template.lua)) +2. `.envoy.yaml` ([template example](https://github.com/buildwithgrove/path/blob/main/envoy/envoy.template.yaml)) +3. `.ratelimit.yaml` ([template example](https://github.com/buildwithgrove/path/blob/main/envoy/ratelimit.yaml)) +4. `.gateway-endpoints.yaml` ([template example](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml)) -::: +## Table of Contents -- [Initialization](#initialization) +- [Initialization of configuration files](#initialization-of-configuration-files) - [Allowed Services - `.allowed-services.lua`](#allowed-services---allowed-serviceslua) - - [File Format](#file-format) - [Terminology](#terminology) + - [Allowed Services Functionality](#allowed-services-functionality) + - [Allowed Services File Format](#allowed-services-file-format) - [Envoy Proxy Configuration - `.envoy.yaml`](#envoy-proxy-configuration---envoyyaml) - [Ratelimit Configuration - `.ratelimit.yaml`](#ratelimit-configuration---ratelimityaml) - - [File Format](#file-format-1) + - [Ratelimit File Format](#ratelimit-file-format) + - [Ratelimit Customizations](#ratelimit-customizations) - [Gateway Endpoints Data - `.gateway-endpoints.yaml`](#gateway-endpoints-data---gateway-endpointsyaml) - - [File Format](#file-format-2) - + - [Gateway Endpoint Functionality](#gateway-endpoint-functionality) + - [Gateway Endpoint File Format](#gateway-endpoint-file-format) -## Initialization +## Initialization of configuration files -The required Envoy configuration files may be generated from their templates by running the make target: +The required Envoy configuration files can be generated from their templates by running: ```bash make init_envoy @@ -66,15 +55,34 @@ This will generate the following files in the `local/path/envoy` directory: - `.ratelimit.yaml` - `.gateway-endpoints.yaml` -**All of these files are git ignored from the PATH repo as they are specific to each PATH instance and may contain sensitive information.** +:::note +All of these files are git ignored from the PATH repo as they are specific to +each PATH instance and may contain sensitive information. -## Allowed Services - `.allowed-services.lua` +::: +## Allowed Services - `.allowed-services.lua` The `.allowed-services.lua` file is used to define the allowed services for the Envoy Proxy. -Once created in `local/path/envoy`, the `.allowed-services.lua` file is mounted as a file in the Envoy Proxy container at `/etc/envoy/.allowed-services.lua`. +Once created in `local/path/envoy`, the `.allowed-services.lua` file is mounted as a +file in the Envoy Proxy container at `/etc/envoy/.allowed-services.lua`. + +### Terminology + +- **Authoritative ID**: The service ID that that PATH uses to identify a service. +- **Alias**: A string that resolves to a service ID, useful for creating human-readable subdomains for services. + +The **key** in the config file may either be the **authoritative service ID** or an **alias**. + +The **value** in the config file must be the **authoritative service ID**. + +### Allowed Services Functionality + +The Envoy Proxy's Lua filter will forward requests to PATH with the `authoritative ID` set in the `target-service-id` header. + +For more information, see the [Service ID Specification section of the Envoy Proxy documentation](../../develop/envoy/introduction.md#service-id-specification). :::warning @@ -84,9 +92,11 @@ All service IDs allowed by the PATH instance must be defined in the `.allowed-se ::: -### File Format +### Allowed Services File Format + +Below is the expect file format for `.allowed-services.lua`. +You can find a template file [here](https://github.com/buildwithgrove/path/tree/main/envoy/allowed-services.template.lua). -_`.allowed-services.lua` format:_ ```lua return { -- 1. Shannon Service IDs @@ -97,22 +107,6 @@ return { ["pocket"] = "F000", -- Pocket (Alias) } ``` -- [`.allowed-services.lua` template file](https://github.com/buildwithgrove/path/tree/main/envoy/allowed-services.template.lua). - -### Terminology - -- **Authoritative ID**: The service ID that that PATH uses to identify a service. -- **Alias**: A string that resolves to a service ID, which is useful for creating human-readable subdomains for services. - -The key may either be the **authoritative service ID** or an **alias**. The value must be the **authoritative service ID**. - -:::info - -The Envoy Proxy's Lua filter will forward requests to PATH with the `authoritative ID` set in the `target-service-id` header. - -For more information, see the [ Service ID Specification section of the Envoy Proxy documentation](../../develop/envoy/introduction.md#service-id-specification). - -::: ## Envoy Proxy Configuration - `.envoy.yaml` @@ -120,6 +114,8 @@ The `.envoy.yaml` file is used to configure the Envoy Proxy. Once created in `local/path/envoy`, the `.envoy.yaml` file is mounted as a file in the Envoy Proxy container at `/etc/envoy/.envoy.yaml`. +For more information on Envoy Proxy configuration file, see the [Envoy Proxy documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/overview/examples#static). + :::warning Once configured using the prompts in the `make init_envoy` target, the `.envoy.yaml` file does not require further modification. @@ -128,12 +124,6 @@ Once configured using the prompts in the `make init_envoy` target, the `.envoy.y ::: -:::tip - -[For more information on Envoy Proxy configuration file, see the Envoy Proxy documentation.](https://www.envoyproxy.io/docs/envoy/latest/configuration/overview/examples#static) - -::: - ## Ratelimit Configuration - `.ratelimit.yaml` The `.ratelimit.yaml` file is used to configure the Ratelimit service. @@ -142,12 +132,18 @@ Once created in `local/path/envoy`, the `.ratelimit.yaml` file is mounted as a f **To make changes to the Ratelimit service, modify the `.ratelimit.yaml` file.** -### File Format +### Ratelimit File Format + +Below is the expect file format for `.ratelimit.yaml`. +You can find a template file [here](https://github.com/buildwithgrove/path/tree/main/envoy/ratelimit.template.lua). + +For more information on Rate Limit descriptors, see the [documentation in the Envoy Rate Limit repository](https://github.com/envoyproxy/ratelimit?tab=readme-ov-file#definitions). + +`.ratelimit.yaml` format: -_`.ratelimit.yaml` format:_ ```yaml --- -domain: rl +domain: rl descriptors: - key: rl-endpoint-id descriptors: @@ -158,28 +154,21 @@ descriptors: requests_per_unit: 30 ``` -- [`.ratelimit.yaml` template file](https://github.com/buildwithgrove/path/tree/main/envoy/ratelimit.template.yaml). +### Ratelimit Customizations To add new throughput limits, add a new descriptor array item under the `descriptors` key. -:::tip - -[For more information on Rate Limit descriptors, see the documentation in the Envoy Rate Limit repository.](https://github.com/envoyproxy/ratelimit?tab=readme-ov-file#definitions) - -::: - +For more information on Rate Limit descriptors, see the [documentation in the Envoy Rate Limit repository](https://github.com/envoyproxy/ratelimit?tab=readme-ov-file#definitions). ## Gateway Endpoints Data - `.gateway-endpoints.yaml` -:::info - A `GatewayEndpoint` is how PATH defines a single endpoint that is authorized to use the PATH service. It is used to define the **authorization method**, **rate limits**, and **metadata** for an endpoint. For more information, see the [Gateway Endpoint section in the Envoy docs](../../develop/envoy/introduction.md#gateway-endpoint-authorization). -::: +### Gateway Endpoint Functionality The `.gateway-endpoints.yaml` file is used to define the Gateway Endpoints that are authorized to make requests to the PATH instance. @@ -187,20 +176,25 @@ Once created in `local/path/envoy`, the `.gateway-endpoints.yaml` file is mounte :::tip -This YAML file is provided as an easy default way to define Gateway Endpoints to get started with PATH. For more complex use cases, you may wish to use a database as the data source for Gateway Endpoints. +This YAML file is provided as an easy default way to define Gateway Endpoints to get started with PATH. + +For more complex use cases, you may wish to use a database as the data source for Gateway Endpoints. **For more information on how to use a database as the data source for Gateway Endpoints, [see the PATH Auth Data Server (PADS) section of the Envoy docs](../../develop/envoy/introduction.md#path-auth-data-server).** ::: +### Gateway Endpoint File Format -### File Format +Below is the expect file format for `.gateway-endpoints.yaml`. +You can find an example file [here](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml) which +uses [this schema](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/gateway-endpoints.schema.yaml). ```yaml endpoints: - endpoint_1_static_key: - auth: - api_key: "api_key_1" + endpoint_1_static_key: + auth: + api_key: "api_key_1" endpoint_2_jwt: auth: @@ -214,9 +208,3 @@ endpoints: capacity_limit: 100000 capacity_limit_period: "CAPACITY_LIMIT_PERIOD_MONTHLY" ``` - -- [`.gateway-endpoints.yaml` example file](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/testdata/gateway-endpoints.example.yaml). - -- [Gateway Endpoint YAML File Schema](https://github.com/buildwithgrove/path-auth-data-server/blob/main/yaml/gateway-endpoints.schema.yaml). - -To define the Gateway Endpoints that are authorized to use the PATH service, edit the `.gateway-endpoints.yaml` file. diff --git a/docusaurus/docusaurus.config.js b/docusaurus/docusaurus.config.js index 45f572bd..642ab251 100644 --- a/docusaurus/docusaurus.config.js +++ b/docusaurus/docusaurus.config.js @@ -12,6 +12,24 @@ const config = { tagline: "All paths lead to Grove", favicon: "img/grove-leaf.jpeg", + markdown: { + mermaid: true, + }, + themes: [ + "@docusaurus/theme-mermaid", + [ + require.resolve("@easyops-cn/docusaurus-search-local"), + /** @type {import('@easyops-cn/docusaurus-search-local').PluginOptions} **/ + { + docsRouteBasePath: "/", + hashed: false, + indexBlog: false, + highlightSearchTermsOnTargetPage: true, + explicitSearchResultPath: true, + }, + ], + ], + // Set the production url of your site here url: "https://grove.city", baseUrl: "/", @@ -123,6 +141,15 @@ const config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + additionalLanguages: [ + "gherkin", + "protobuf", + "json", + "makefile", + "diff", + "lua", + "bash", + ], }, }), }; diff --git a/docusaurus/package-lock.json b/docusaurus/package-lock.json index 6db07ed4..11b8afe3 100644 --- a/docusaurus/package-lock.json +++ b/docusaurus/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@docusaurus/core": "3.5.2", "@docusaurus/preset-classic": "3.5.2", + "@docusaurus/theme-mermaid": "^3.5.2", + "@easyops-cn/docusaurus-search-local": "^0.46.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", @@ -2137,6 +2139,12 @@ "node": ">=6.9.0" } }, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==", + "license": "MIT" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2724,6 +2732,28 @@ "react-dom": "^18.0.0" } }, + "node_modules/@docusaurus/theme-mermaid": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.5.2.tgz", + "integrity": "sha512-7vWCnIe/KoyTN1Dc55FIyqO5hJ3YaV08Mr63Zej0L0mX1iGzt+qKSmeVUAJ9/aOalUhF0typV0RmNUSy5FAmCg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "mermaid": "^10.4.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@docusaurus/theme-search-algolia": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz", @@ -2867,6 +2897,147 @@ "node": ">=18.0" } }, + "node_modules/@easyops-cn/autocomplete.js": { + "version": "0.38.1", + "resolved": "https://registry.npmjs.org/@easyops-cn/autocomplete.js/-/autocomplete.js-0.38.1.tgz", + "integrity": "sha512-drg76jS6syilOUmVNkyo1c7ZEBPcPuK+aJA7AksM5ZIIbV57DMHCywiCr+uHyv8BE5jUTU98j/H7gVrkHrWW3Q==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "immediate": "^3.2.3" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.46.1.tgz", + "integrity": "sha512-kgenn5+pctVlJg8s1FOAm9KuZLRZvkBTMMGJvTTcvNTmnFIHVVYzYfA2Eg+yVefzsC8/cSZGKKJ0kLf8I+mQyw==", + "license": "MIT", + "dependencies": { + "@docusaurus/plugin-content-docs": "^2 || ^3", + "@docusaurus/theme-translations": "^2 || ^3", + "@docusaurus/utils": "^2 || ^3", + "@docusaurus/utils-common": "^2 || ^3", + "@docusaurus/utils-validation": "^2 || ^3", + "@easyops-cn/autocomplete.js": "^0.38.1", + "@node-rs/jieba": "^1.6.0", + "cheerio": "^1.0.0", + "clsx": "^1.1.1", + "comlink": "^4.4.2", + "debug": "^4.2.0", + "fs-extra": "^10.0.0", + "klaw-sync": "^6.0.0", + "lunr": "^2.3.9", + "lunr-languages": "^1.4.0", + "mark.js": "^8.11.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@docusaurus/theme-common": "^2 || ^3", + "react": "^16.14.0 || ^17 || ^18", + "react-dom": "^16.14.0 || 17 || ^18" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz", + "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", + "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -3028,6 +3199,271 @@ "react": ">=16" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.6.tgz", + "integrity": "sha512-z8YVS3XszxFTO73iwvFDNpQIzdMmSDTP/mB3E/ucR37V3Sx57hSExcXyMoNwaucWxnsWf4xfbZv0iZ30jr0M4Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.3.1", + "@emnapi/runtime": "^1.3.1", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@node-rs/jieba": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba/-/jieba-1.10.4.tgz", + "integrity": "sha512-GvDgi8MnBiyWd6tksojej8anIx18244NmIOc1ovEw8WKNUejcccLfyu8vj66LWSuoZuKILVtNsOy4jvg3aoxIw==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/jieba-android-arm-eabi": "1.10.4", + "@node-rs/jieba-android-arm64": "1.10.4", + "@node-rs/jieba-darwin-arm64": "1.10.4", + "@node-rs/jieba-darwin-x64": "1.10.4", + "@node-rs/jieba-freebsd-x64": "1.10.4", + "@node-rs/jieba-linux-arm-gnueabihf": "1.10.4", + "@node-rs/jieba-linux-arm64-gnu": "1.10.4", + "@node-rs/jieba-linux-arm64-musl": "1.10.4", + "@node-rs/jieba-linux-x64-gnu": "1.10.4", + "@node-rs/jieba-linux-x64-musl": "1.10.4", + "@node-rs/jieba-wasm32-wasi": "1.10.4", + "@node-rs/jieba-win32-arm64-msvc": "1.10.4", + "@node-rs/jieba-win32-ia32-msvc": "1.10.4", + "@node-rs/jieba-win32-x64-msvc": "1.10.4" + } + }, + "node_modules/@node-rs/jieba-android-arm-eabi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-1.10.4.tgz", + "integrity": "sha512-MhyvW5N3Fwcp385d0rxbCWH42kqDBatQTyP8XbnYbju2+0BO/eTeCCLYj7Agws4pwxn2LtdldXRSKavT7WdzNA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-android-arm64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-android-arm64/-/jieba-android-arm64-1.10.4.tgz", + "integrity": "sha512-XyDwq5+rQ+Tk55A+FGi6PtJbzf974oqnpyCcCPzwU3QVXJCa2Rr4Lci+fx8oOpU4plT3GuD+chXMYLsXipMgJA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-darwin-arm64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-1.10.4.tgz", + "integrity": "sha512-G++RYEJ2jo0rxF9626KUy90wp06TRUjAsvY/BrIzEOX/ingQYV/HjwQzNPRR1P1o32a6/U8RGo7zEBhfdybL6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-darwin-x64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-1.10.4.tgz", + "integrity": "sha512-MmDNeOb2TXIZCPyWCi2upQnZpPjAxw5ZGEj6R8kNsPXVFALHIKMa6ZZ15LCOkSTsKXVC17j2t4h+hSuyYb6qfQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-freebsd-x64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-1.10.4.tgz", + "integrity": "sha512-/x7aVQ8nqUWhpXU92RZqd333cq639i/olNpd9Z5hdlyyV5/B65LLy+Je2B2bfs62PVVm5QXRpeBcZqaHelp/bg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-arm-gnueabihf": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-1.10.4.tgz", + "integrity": "sha512-crd2M35oJBRLkoESs0O6QO3BBbhpv+tqXuKsqhIG94B1d02RVxtRIvSDwO33QurxqSdvN9IeSnVpHbDGkuXm3g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-arm64-gnu": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-1.10.4.tgz", + "integrity": "sha512-omIzNX1psUzPcsdnUhGU6oHeOaTCuCjUgOA/v/DGkvWC1jLcnfXe4vdYbtXMh4XOCuIgS1UCcvZEc8vQLXFbXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-arm64-musl": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-1.10.4.tgz", + "integrity": "sha512-Y/tiJ1+HeS5nnmLbZOE+66LbsPOHZ/PUckAYVeLlQfpygLEpLYdlh0aPpS5uiaWMjAXYZYdFkpZHhxDmSLpwpw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-x64-gnu": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-1.10.4.tgz", + "integrity": "sha512-WZO8ykRJpWGE9MHuZpy1lu3nJluPoeB+fIJJn5CWZ9YTVhNDWoCF4i/7nxz1ntulINYGQ8VVuCU9LD86Mek97g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-x64-musl": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-1.10.4.tgz", + "integrity": "sha512-uBBD4S1rGKcgCyAk6VCKatEVQb6EDD5I40v/DxODi5CuZVCANi9m5oee/MQbAoaX7RydA2f0OSCE9/tcwXEwUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-wasm32-wasi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-wasm32-wasi/-/jieba-wasm32-wasi-1.10.4.tgz", + "integrity": "sha512-Y2umiKHjuIJy0uulNDz9SDYHdfq5Hmy7jY5nORO99B4pySKkcrMjpeVrmWXJLIsEKLJwcCXHxz8tjwU5/uhz0A==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/jieba-win32-arm64-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-1.10.4.tgz", + "integrity": "sha512-nwMtViFm4hjqhz1it/juQnxpXgqlGltCuWJ02bw70YUDMDlbyTy3grCJPpQQpueeETcALUnTxda8pZuVrLRcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-win32-ia32-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-1.10.4.tgz", + "integrity": "sha512-DCAvLx7Z+W4z5oKS+7vUowAJr0uw9JBw8x1Y23Xs/xMA4Em+OOSiaF5/tCJqZUCJ8uC4QeImmgDFiBqGNwxlyA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-win32-x64-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-1.10.4.tgz", + "integrity": "sha512-+sqemSfS1jjb+Tt7InNbNzrRh1Ua3vProVvC4BZRPg010/leCbGFFiQHpzcPRfpxAXZrzG5Y0YBTsPzN/I4yHQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3438,6 +3874,16 @@ "node": ">=10.13.0" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/acorn": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", @@ -3485,6 +3931,27 @@ "@types/node": "*" } }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -5065,6 +5532,12 @@ "node": ">=10" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -5362,10 +5835,19 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", @@ -5709,580 +6191,665 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/cytoscape": { + "version": "3.30.4", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz", + "integrity": "sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10" } }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", "license": "MIT", "dependencies": { - "character-entities": "^2.0.0" + "cose-base": "^1.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "peerDependencies": { + "cytoscape": "^3.2.0" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", "dependencies": { - "mimic-response": "^3.1.0" + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", "engines": { - "node": ">=4.0.0" + "node": ">=12" } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "license": "BSD-2-Clause", + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", "dependencies": { - "execa": "^5.0.0" + "d3-path": "1 - 3" }, "engines": { - "node": ">= 10" + "node": ">=12" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "d3-array": "^3.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=12" } }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "d3-dispatch": "1 - 3", + "d3-selection": "3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "license": "MIT", + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" }, - "engines": { - "node": ">=10" + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 10" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=12" } }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" - }, - "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", - "license": "MIT", + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" + "d3-dsv": "1 - 3" }, "engines": { - "node": ">= 4.0.0" + "node": ">=12" } }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "license": "MIT", + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">= 4.2.1" + "node": ">=12" } }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", "dependencies": { - "dequal": "^2.0.0" + "d3-array": "2.5.0 - 3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=12" } }, - "node_modules/dir-glob": { + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", "dependencies": { - "path-type": "^4.0.0" + "d3-color": "1 - 3" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "license": "MIT", + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", "dependencies": { - "utila": "~0.4" + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "internmap": "^1.0.0" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", "dependencies": { - "domelementtype": "^2.3.0" + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=12" } }, - "node_modules/domutils": { + "node_modules/d3-scale-chromatic": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "engines": { + "node": ">=12" } }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "license": "MIT", + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", "dependencies": { - "is-obj": "^2.0.0" + "d3-path": "^3.1.0" }, "engines": { - "node": ">=10" + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12" } }, - "node_modules/dot-prop/node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "license": "MIT", + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/electron-to-chromium": { - "version": "1.5.49", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz", - "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==", - "license": "ISC" + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "license": "MIT", + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", "license": "MIT" }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", "license": "MIT" }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">= 4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/emoticon": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", - "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">=10" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" + "engines": { + "node": ">=4.0.0" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "license": "BSD-2-Clause", "dependencies": { - "get-intrinsic": "^1.2.4" + "execa": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 10" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/es-module-lexer": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", - "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", - "license": "MIT" - }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/escape-goat": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", - "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "license": "MIT", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, "engines": { "node": ">=10" }, @@ -6290,1210 +6857,1679 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "license": "BSD-2-Clause", + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "robust-predicates": "^3.0.2" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=6" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "license": "BSD-2-Clause", + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "node_modules/detect-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", + "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0" + "address": "^1.0.1", + "debug": "4" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + }, + "engines": { + "node": ">= 4.0.0" } }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" } }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" + "dequal": "^2.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" + "path-type": "^4.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=8" } }, - "node_modules/estree-util-value-to-estree": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.2.tgz", - "integrity": "sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==", + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" + "engines": { + "node": ">=6" } }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "utila": "~0.4" } }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" }, - "node_modules/eta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", - "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", - "license": "MIT", + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">= 4" }, "funding": { - "url": "https://github.com/eta-dev/eta?sponsor=1" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } + "node_modules/dompurify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", + "license": "(MPL-2.0 OR Apache-2.0)" }, - "node_modules/eval": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", - "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", "dependencies": { - "@types/node": "*", - "require-like": ">= 0.1.1" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, - "engines": { - "node": ">= 0.8" + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "license": "MIT", - "engines": { - "node": ">=0.8.x" + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "is-obj": "^2.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, "engines": { - "node": ">= 0.10.0" + "node": ">=8" } }, - "node_modules/express/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.49", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz", + "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==", + "license": "ISC" + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "license": "EPL-2.0" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">= 4" } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/emoticon": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", + "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/express/node_modules/ms": { + "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "license": "MIT" - }, - "node_modules/express/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/encoding-sniffer/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=8.6.0" + "node": ">=10.13.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "license": "MIT", "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "is-arrayish": "^0.2.1" } }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "license": "Apache-2.0", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", "dependencies": { - "websocket-driver": ">=0.5.1" + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=0.8.0" + "node": ">= 0.4" } }, - "node_modules/feed": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", - "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "dependencies": { - "xml-js": "^1.6.11" - }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.4" } }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", "license": "MIT", "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "url": "https://opencollective.com/unified" } }, - "node_modules/file-loader/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/file-loader/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "node": ">=6" } }, - "node_modules/file-loader/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, - "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, "engines": { - "node": ">= 10.13.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "license": "BSD-3-Clause", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, "engines": { - "node": ">= 0.4.0" + "node": ">=8.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.8" + "node": ">=4.0" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } }, - "node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", "license": "MIT", "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" + "@types/estree": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", "license": "MIT", "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", "license": "MIT", - "engines": { - "node": ">=4.0" + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/estree-util-value-to-estree": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-3.1.2.tgz", + "integrity": "sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@types/estree": "^1.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/remcohaszing" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", "license": "MIT", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" + "@types/estree": "^1.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "license": "MIT" + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "node_modules/eta": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.2.0.tgz", + "integrity": "sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==", "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, "engines": { - "node": ">= 8.9.0" + "node": ">=6.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/eta-dev/eta?sponsor=1" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "license": "MIT", + "node_modules/eval": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", + "dependencies": { + "@types/node": "*", + "require-like": ">= 0.1.1" + }, "engines": { - "node": ">= 14.17" + "node": ">= 0.8" } }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=0.8.x" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, "engines": { - "node": "*" + "node": ">=10" }, "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.10.0" } }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "node_modules/express/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=14.14" + "node": ">= 0.6" } }, - "node_modules/fs-monkey": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" + }, + "node_modules/express/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.6" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "is-extendable": "^0.1.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "license": "ISC" + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", - "license": "ISC" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.6.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "reusify": "^1.0.4" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", "license": "MIT", "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" + "format": "^0.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "license": "ISC", + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, "engines": { - "node": ">=10" + "node": ">=0.8.0" } }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", "license": "MIT", "dependencies": { - "global-prefix": "^3.0.0" + "xml-js": "^1.6.11" }, "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "license": "MIT", "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "license": "ISC", + "node_modules/file-loader/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "bin": { - "which": "bin/which" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/file-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "license": "MIT", - "engines": { - "node": ">=4" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 0.4.0" } }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" + "to-regex-range": "^5.0.1" }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "license": "MIT", "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "node": ">=8" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=6.0" + "node": ">= 0.8" } }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "ms": "2.0.0" } }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "license": "MIT", "dependencies": { - "duplexer": "^0.1.2" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "license": "MIT" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10", + "yarn": ">=1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/has-yarn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", - "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", - "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "license": "MIT", "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^8.0.0", + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "license": "ISC" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "license": "ISC" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-yarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", "property-information": "^6.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", @@ -8050,6 +9086,12 @@ "node": ">=16.x" } }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", + "license": "MIT" + }, "node_modules/immer": { "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", @@ -8141,6 +9183,15 @@ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -8515,482 +9566,998 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/katex": { + "version": "0.16.18", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.18.tgz", + "integrity": "sha512-LRuk0rPdXrecAFwQucYjMiIs0JFefk6N1q/04mlw14aVIVgxq1FO0MA9RiIIGVaKOB5GIP5GH4aBBNraZERmaQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "license": "MIT", + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, - "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.5", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "loose-envify": "cli.js" } }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "license": "MIT" }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "node_modules/lunr-languages": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.14.0.tgz", + "integrity": "sha512-hWUAb2KqM3L7J5bcrngszzISY4BxrXn/Xhbb9TTCJYEGqlR1nG67/M14sp09+PTIRklobrn57IAxcdcO/ZFyNA==", + "license": "MPL-1.1" + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", + "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/latest-version": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", - "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", "license": "MIT", "dependencies": { - "package-json": "^8.1.0" - }, - "engines": { - "node": ">=14.16" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/launch-editor": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", - "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "license": "MIT", - "engines": { - "node": ">=6" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { - "node": ">=14" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "license": "MIT", - "engines": { - "node": ">=6.11.5" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", "license": "MIT", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=8.9.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "license": "MIT" + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT" }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "bin": { - "loose-envify": "cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "license": "MIT", "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", "license": "MIT", - "engines": { - "node": ">=16" + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-directive": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz", - "integrity": "sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==", + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", "license": "MIT", "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", - "devlop": "^1.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", - "unist-util-visit-parents": "^6.0.0" + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", - "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "license": "MIT", "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", "license": "MIT", "dependencies": { + "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", + "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.1.tgz", + "integrity": "sha512-OrkcCoqAkEg9b1ykXBrA0ehRc8H4fGU/03cACmW2xXzau1+dIdS+qJugh1Cqex3hMumSBgSE/5pc7uqP12nLAw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-from-markdown/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mdast-util-frontmatter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", - "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "escape-string-regexp": "^5.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-extension-frontmatter": "^2.0.0" + "@types/mdast": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdast-util-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", - "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "10.9.3", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.3.tgz", + "integrity": "sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==", "license": "MIT", "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.28.1", + "cytoscape-cose-bilkent": "^4.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5 <3.1.7", + "elkjs": "^0.9.0", + "katex": "^0.16.9", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/mermaid/node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/mermaid/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/mermaid/node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "node_modules/mermaid/node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mermaid/node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/mermaid/node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" } }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "node_modules/mermaid/node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", "funding": [ { "type": "GitHub Sponsors", @@ -9003,14 +10570,15 @@ ], "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "node_modules/mermaid/node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", "funding": [ { "type": "GitHub Sponsors", @@ -9021,268 +10589,312 @@ "url": "https://opencollective.com/unified" } ], - "license": "MIT" - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" } }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "node_modules/mermaid/node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "node_modules/mermaid/node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "node_modules/mermaid/node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "node_modules/mermaid/node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "node_modules/mermaid/node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", - "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "node_modules/mermaid/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "node_modules/mermaid/node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "node_modules/mermaid/node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mermaid/node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mermaid/node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "node_modules/mermaid/node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-types": "^1.0.0" } }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.1.tgz", - "integrity": "sha512-OrkcCoqAkEg9b1ykXBrA0ehRc8H4fGU/03cACmW2xXzau1+dIdS+qJugh1Cqex3hMumSBgSE/5pc7uqP12nLAw==", + "node_modules/mermaid/node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "node_modules/mermaid/node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" } }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "license": "CC0-1.0" + "node_modules/mermaid/node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/mermaid/node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "license": "Unlicense", "dependencies": { - "fs-monkey": "^1.0.4" + "@types/unist": "^2.0.0" }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/mermaid/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "engines": { - "node": ">= 8" + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/methods": { @@ -11194,6 +12806,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -11295,6 +12916,12 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "license": "MIT" }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==", + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -11671,6 +13298,18 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -13586,6 +15225,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, "node_modules/rtl-detect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz", @@ -13633,6 +15278,24 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -14405,6 +16068,12 @@ "postcss": "^8.4.31" } }, + "node_modules/stylis": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz", + "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14688,6 +16357,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/tslib": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", @@ -14763,6 +16441,15 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -15200,6 +16887,33 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -15289,6 +17003,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==", + "license": "Apache-2.0" + }, "node_modules/webpack": { "version": "5.95.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", @@ -15637,6 +17357,39 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/docusaurus/package.json b/docusaurus/package.json index d978c821..683bd6b3 100644 --- a/docusaurus/package.json +++ b/docusaurus/package.json @@ -16,6 +16,8 @@ "dependencies": { "@docusaurus/core": "3.5.2", "@docusaurus/preset-classic": "3.5.2", + "@docusaurus/theme-mermaid": "^3.5.2", + "@easyops-cn/docusaurus-search-local": "^0.46.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", diff --git a/docusaurus/yarn.lock b/docusaurus/yarn.lock index 889e2285..de5314ad 100644 --- a/docusaurus/yarn.lock +++ b/docusaurus/yarn.lock @@ -1190,6 +1190,11 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@braintree/sanitize-url@^6.0.1": + version "6.0.4" + resolved "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz" + integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" @@ -1374,7 +1379,7 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@*", "@docusaurus/plugin-content-docs@3.5.2": +"@docusaurus/plugin-content-docs@*", "@docusaurus/plugin-content-docs@^2 || ^3", "@docusaurus/plugin-content-docs@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz" integrity sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ== @@ -1519,7 +1524,7 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-common@3.5.2": +"@docusaurus/theme-common@^2 || ^3", "@docusaurus/theme-common@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz" integrity sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew== @@ -1537,6 +1542,19 @@ tslib "^2.6.0" utility-types "^3.10.0" +"@docusaurus/theme-mermaid@^3.5.2": + version "3.5.2" + resolved "https://registry.npmjs.org/@docusaurus/theme-mermaid/-/theme-mermaid-3.5.2.tgz" + integrity sha512-7vWCnIe/KoyTN1Dc55FIyqO5hJ3YaV08Mr63Zej0L0mX1iGzt+qKSmeVUAJ9/aOalUhF0typV0RmNUSy5FAmCg== + dependencies: + "@docusaurus/core" "3.5.2" + "@docusaurus/module-type-aliases" "3.5.2" + "@docusaurus/theme-common" "3.5.2" + "@docusaurus/types" "3.5.2" + "@docusaurus/utils-validation" "3.5.2" + mermaid "^10.4.0" + tslib "^2.6.0" + "@docusaurus/theme-search-algolia@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz" @@ -1559,7 +1577,7 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@3.5.2": +"@docusaurus/theme-translations@^2 || ^3", "@docusaurus/theme-translations@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz" integrity sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw== @@ -1582,14 +1600,14 @@ webpack "^5.88.1" webpack-merge "^5.9.0" -"@docusaurus/utils-common@3.5.2": +"@docusaurus/utils-common@^2 || ^3", "@docusaurus/utils-common@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz" integrity sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg== dependencies: tslib "^2.6.0" -"@docusaurus/utils-validation@3.5.2": +"@docusaurus/utils-validation@^2 || ^3", "@docusaurus/utils-validation@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz" integrity sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA== @@ -1603,7 +1621,7 @@ lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/utils@3.5.2": +"@docusaurus/utils@^2 || ^3", "@docusaurus/utils@3.5.2": version "3.5.2" resolved "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz" integrity sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA== @@ -1629,6 +1647,37 @@ utility-types "^3.10.0" webpack "^5.88.1" +"@easyops-cn/autocomplete.js@^0.38.1": + version "0.38.1" + resolved "https://registry.npmjs.org/@easyops-cn/autocomplete.js/-/autocomplete.js-0.38.1.tgz" + integrity sha512-drg76jS6syilOUmVNkyo1c7ZEBPcPuK+aJA7AksM5ZIIbV57DMHCywiCr+uHyv8BE5jUTU98j/H7gVrkHrWW3Q== + dependencies: + cssesc "^3.0.0" + immediate "^3.2.3" + +"@easyops-cn/docusaurus-search-local@^0.46.1": + version "0.46.1" + resolved "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.46.1.tgz" + integrity sha512-kgenn5+pctVlJg8s1FOAm9KuZLRZvkBTMMGJvTTcvNTmnFIHVVYzYfA2Eg+yVefzsC8/cSZGKKJ0kLf8I+mQyw== + dependencies: + "@docusaurus/plugin-content-docs" "^2 || ^3" + "@docusaurus/theme-translations" "^2 || ^3" + "@docusaurus/utils" "^2 || ^3" + "@docusaurus/utils-common" "^2 || ^3" + "@docusaurus/utils-validation" "^2 || ^3" + "@easyops-cn/autocomplete.js" "^0.38.1" + "@node-rs/jieba" "^1.6.0" + cheerio "^1.0.0" + clsx "^1.1.1" + comlink "^4.4.2" + debug "^4.2.0" + fs-extra "^10.0.0" + klaw-sync "^6.0.0" + lunr "^2.3.9" + lunr-languages "^1.4.0" + mark.js "^8.11.1" + tslib "^2.4.0" + "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz" @@ -1742,6 +1791,31 @@ dependencies: "@types/mdx" "^2.0.0" +"@node-rs/jieba-darwin-arm64@1.10.4": + version "1.10.4" + resolved "https://registry.npmjs.org/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-1.10.4.tgz" + integrity sha512-G++RYEJ2jo0rxF9626KUy90wp06TRUjAsvY/BrIzEOX/ingQYV/HjwQzNPRR1P1o32a6/U8RGo7zEBhfdybL6w== + +"@node-rs/jieba@^1.6.0": + version "1.10.4" + resolved "https://registry.npmjs.org/@node-rs/jieba/-/jieba-1.10.4.tgz" + integrity sha512-GvDgi8MnBiyWd6tksojej8anIx18244NmIOc1ovEw8WKNUejcccLfyu8vj66LWSuoZuKILVtNsOy4jvg3aoxIw== + optionalDependencies: + "@node-rs/jieba-android-arm-eabi" "1.10.4" + "@node-rs/jieba-android-arm64" "1.10.4" + "@node-rs/jieba-darwin-arm64" "1.10.4" + "@node-rs/jieba-darwin-x64" "1.10.4" + "@node-rs/jieba-freebsd-x64" "1.10.4" + "@node-rs/jieba-linux-arm-gnueabihf" "1.10.4" + "@node-rs/jieba-linux-arm64-gnu" "1.10.4" + "@node-rs/jieba-linux-arm64-musl" "1.10.4" + "@node-rs/jieba-linux-x64-gnu" "1.10.4" + "@node-rs/jieba-linux-x64-musl" "1.10.4" + "@node-rs/jieba-wasm32-wasi" "1.10.4" + "@node-rs/jieba-win32-arm64-msvc" "1.10.4" + "@node-rs/jieba-win32-ia32-msvc" "1.10.4" + "@node-rs/jieba-win32-x64-msvc" "1.10.4" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -1985,6 +2059,23 @@ dependencies: "@types/node" "*" +"@types/d3-scale-chromatic@^3.0.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz" + integrity sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ== + +"@types/d3-scale@^4.0.3": + version "4.0.8" + resolved "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz" + integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-time@*": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz" + integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g== + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz" @@ -2097,6 +2188,13 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/mdast@^3.0.0": + version "3.0.15" + resolved "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz" + integrity sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ== + dependencies: + "@types/unist" "^2" + "@types/mdast@^4.0.0", "@types/mdast@^4.0.2": version "4.0.4" resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz" @@ -2245,7 +2343,7 @@ resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz" integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== -"@types/unist@^2.0.0": +"@types/unist@^2", "@types/unist@^2.0.0": version "2.0.11" resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz" integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== @@ -2915,6 +3013,23 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" +cheerio@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz" + integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.1.0" + encoding-sniffer "^0.2.0" + htmlparser2 "^9.1.0" + parse5 "^7.1.2" + parse5-htmlparser2-tree-adapter "^7.0.0" + parse5-parser-stream "^7.1.2" + undici "^6.19.5" + whatwg-mimetype "^4.0.0" + cheerio@1.0.0-rc.12: version "1.0.0-rc.12" resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz" @@ -2988,6 +3103,11 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + clsx@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz" @@ -3025,6 +3145,11 @@ combine-promises@^1.1.0: resolved "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz" integrity sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ== +comlink@^4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz" + integrity sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g== + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" @@ -3055,6 +3180,11 @@ commander@^8.3.0: resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@7: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + common-path-prefix@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz" @@ -3185,6 +3315,13 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cose-base@^1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz" + integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== + dependencies: + layout-base "^1.0.0" + cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" @@ -3375,6 +3512,302 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +cytoscape-cose-bilkent@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz" + integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + dependencies: + cose-base "^1.0.0" + +cytoscape@^3.2.0, cytoscape@^3.28.1: + version "3.30.4" + resolved "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz" + integrity sha512-OxtlZwQl1WbwMmLiyPSEBuzeTIQnwZhJYYWFzZ2PhEHVFwpeaqNIkUzSiso00D98qk60l8Gwon2RP304d3BJ1A== + +d3-array@^3.2.0, "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3: + version "3.2.4" + resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-array@1 - 2": + version "2.12.1" + resolved "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + +d3-axis@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz" + integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + +d3-brush@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz" + integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "3" + d3-transition "3" + +d3-chord@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz" + integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + dependencies: + d3-path "1 - 3" + +"d3-color@1 - 3", d3-color@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-contour@4: + version "4.0.2" + resolved "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz" + integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== + dependencies: + d3-array "^3.2.0" + +d3-delaunay@6: + version "6.0.4" + resolved "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz" + integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== + dependencies: + delaunator "5" + +"d3-dispatch@1 - 3", d3-dispatch@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-dsv@1 - 3", d3-dsv@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + dependencies: + commander "7" + iconv-lite "0.6" + rw "1" + +"d3-ease@1 - 3", d3-ease@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +d3-fetch@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz" + integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + dependencies: + d3-dsv "1 - 3" + +d3-force@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + dependencies: + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" + +"d3-format@1 - 3", d3-format@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-geo@3: + version "3.1.1" + resolved "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz" + integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== + dependencies: + d3-array "2.5.0 - 3" + +d3-hierarchy@3: + version "3.1.2" + resolved "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz" + integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== + +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0, "d3-path@1 - 3", d3-path@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-path@1: + version "1.0.9" + resolved "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-polygon@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz" + integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + +"d3-quadtree@1 - 3", d3-quadtree@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + +d3-random@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz" + integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + +d3-sankey@^0.12.3: + version "0.12.3" + resolved "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz" + integrity sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ== + dependencies: + d3-array "1 - 2" + d3-shape "^1.2.0" + +d3-scale-chromatic@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz" + integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + +d3-scale@4: + version "4.0.2" + resolved "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +"d3-selection@2 - 3", d3-selection@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + +d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +d3-shape@3: + version "3.2.0" + resolved "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4", d3-time-format@4: + version "4.1.0" + resolved "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: + version "3.1.0" + resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +"d3-timer@1 - 3", d3-timer@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3", d3-transition@3: + version "3.0.1" + resolved "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@3: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + +d3@^7.4.0, d3@^7.8.2: + version "7.9.0" + resolved "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz" + integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "4" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + +dagre-d3-es@7.0.10: + version "7.0.10" + resolved "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz" + integrity sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A== + dependencies: + d3 "^7.8.2" + lodash-es "^4.17.21" + +dayjs@^1.11.7: + version "1.11.13" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debounce@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz" @@ -3387,10 +3820,10 @@ debug@^2.6.0: dependencies: ms "2.0.0" -debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@4: - version "4.3.7" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== +debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@4: + version "4.4.0" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" @@ -3474,6 +3907,13 @@ del@^6.1.1: rimraf "^3.0.2" slash "^3.0.0" +delaunator@5: + version "5.0.1" + resolved "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz" + integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw== + dependencies: + robust-predicates "^3.0.2" + depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" @@ -3522,6 +3962,11 @@ devlop@^1.0.0, devlop@^1.1.0: dependencies: dequal "^2.0.0" +diff@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -3580,6 +4025,11 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" +"dompurify@^3.0.5 <3.1.7": + version "3.1.6" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz" + integrity sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ== + domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" @@ -3589,7 +4039,7 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -domutils@^3.0.1: +domutils@^3.0.1, domutils@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz" integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== @@ -3633,6 +4083,11 @@ electron-to-chromium@^1.5.41: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz" integrity sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A== +elkjs@^0.9.0: + version "0.9.3" + resolved "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz" + integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" @@ -3668,6 +4123,14 @@ encodeurl@~2.0.0: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== +encoding-sniffer@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz" + integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg== + dependencies: + iconv-lite "^0.6.3" + whatwg-encoding "^3.1.1" + enhanced-resolve@^5.17.1: version "5.17.1" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz" @@ -4115,6 +4578,15 @@ fresh@0.5.2: resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^11.1.1, fs-extra@^11.2.0: version "11.2.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz" @@ -4291,7 +4763,7 @@ got@^12.1.0: p-cancelable "^3.0.0" responselike "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -4582,6 +5054,16 @@ htmlparser2@^8.0.1: domutils "^3.0.1" entities "^4.4.0" +htmlparser2@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" + http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" @@ -4651,6 +5133,13 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -4658,6 +5147,20 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" @@ -4675,6 +5178,11 @@ image-size@^1.0.2: dependencies: queue "6.0.2" +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + immer@^9.0.7: version "9.0.21" resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" @@ -4746,6 +5254,16 @@ inline-style-parser@0.2.4: resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz" integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + interpret@^1.0.0: version "1.4.0" resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" @@ -5057,6 +5575,13 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +katex@^0.16.9: + version "0.16.18" + resolved "https://registry.npmjs.org/katex/-/katex-0.16.18.tgz" + integrity sha512-LRuk0rPdXrecAFwQucYjMiIs0JFefk6N1q/04mlw14aVIVgxq1FO0MA9RiIIGVaKOB5GIP5GH4aBBNraZERmaQ== + dependencies: + commander "^8.3.0" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" @@ -5064,16 +5589,33 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" +khroma@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz" + integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== + kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +kleur@^4.0.3: + version "4.1.5" + resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + latest-version@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz" @@ -5089,6 +5631,11 @@ launch-editor@^2.6.0: picocolors "^1.0.0" shell-quote "^1.8.1" +layout-base@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz" + integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -5145,6 +5692,11 @@ locate-path@^7.1.0: dependencies: p-locate "^6.0.0" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" @@ -5196,6 +5748,21 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lunr-languages@^1.4.0: + version "1.14.0" + resolved "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.14.0.tgz" + integrity sha512-hWUAb2KqM3L7J5bcrngszzISY4BxrXn/Xhbb9TTCJYEGqlR1nG67/M14sp09+PTIRklobrn57IAxcdcO/ZFyNA== + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +mark.js@^8.11.1: + version "8.11.1" + resolved "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz" + integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== + markdown-extensions@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz" @@ -5230,6 +5797,24 @@ mdast-util-find-and-replace@^3.0.0, mdast-util-find-and-replace@^3.0.1: unist-util-is "^6.0.0" unist-util-visit-parents "^6.0.0" +mdast-util-from-markdown@^1.3.0: + version "1.3.1" + resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz" + integrity sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + decode-named-character-reference "^1.0.0" + mdast-util-to-string "^3.1.0" + micromark "^3.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-decode-string "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + unist-util-stringify-position "^3.0.0" + uvu "^0.5.0" + mdast-util-from-markdown@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz" @@ -5416,6 +6001,13 @@ mdast-util-to-markdown@^2.0.0: unist-util-visit "^5.0.0" zwitch "^2.0.0" +mdast-util-to-string@^3.1.0: + version "3.2.0" + resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz" + integrity sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz" @@ -5460,11 +6052,59 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +mermaid@^10.4.0: + version "10.9.3" + resolved "https://registry.npmjs.org/mermaid/-/mermaid-10.9.3.tgz" + integrity sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw== + dependencies: + "@braintree/sanitize-url" "^6.0.1" + "@types/d3-scale" "^4.0.3" + "@types/d3-scale-chromatic" "^3.0.0" + cytoscape "^3.28.1" + cytoscape-cose-bilkent "^4.1.0" + d3 "^7.4.0" + d3-sankey "^0.12.3" + dagre-d3-es "7.0.10" + dayjs "^1.11.7" + dompurify "^3.0.5 <3.1.7" + elkjs "^0.9.0" + katex "^0.16.9" + khroma "^2.0.0" + lodash-es "^4.17.21" + mdast-util-from-markdown "^1.3.0" + non-layered-tidy-tree-layout "^2.0.2" + stylis "^4.1.3" + ts-dedent "^2.2.0" + uuid "^9.0.0" + web-worker "^1.2.0" + methods@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +micromark-core-commonmark@^1.0.1: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz" + integrity sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-factory-destination "^1.0.0" + micromark-factory-label "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-factory-title "^1.0.0" + micromark-factory-whitespace "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-html-tag-name "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + uvu "^0.5.0" + micromark-core-commonmark@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz" @@ -5656,6 +6296,15 @@ micromark-extension-mdxjs@^3.0.0: micromark-util-combine-extensions "^2.0.0" micromark-util-types "^2.0.0" +micromark-factory-destination@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz" + integrity sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-factory-destination@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz" @@ -5665,6 +6314,16 @@ micromark-factory-destination@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark-factory-label@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz" + integrity sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + micromark-factory-label@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz" @@ -5706,6 +6365,16 @@ micromark-factory-space@^2.0.0: micromark-util-character "^2.0.0" micromark-util-types "^2.0.0" +micromark-factory-title@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz" + integrity sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-factory-title@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz" @@ -5716,6 +6385,16 @@ micromark-factory-title@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark-factory-whitespace@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz" + integrity sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-factory-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz" @@ -5742,6 +6421,13 @@ micromark-util-character@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark-util-chunked@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz" + integrity sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-chunked@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz" @@ -5749,6 +6435,15 @@ micromark-util-chunked@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" +micromark-util-classify-character@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz" + integrity sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + micromark-util-classify-character@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz" @@ -5758,6 +6453,14 @@ micromark-util-classify-character@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark-util-combine-extensions@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz" + integrity sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-types "^1.0.0" + micromark-util-combine-extensions@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz" @@ -5766,6 +6469,13 @@ micromark-util-combine-extensions@^2.0.0: micromark-util-chunked "^2.0.0" micromark-util-types "^2.0.0" +micromark-util-decode-numeric-character-reference@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz" + integrity sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-decode-numeric-character-reference@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz" @@ -5773,6 +6483,16 @@ micromark-util-decode-numeric-character-reference@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" +micromark-util-decode-string@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz" + integrity sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-decode-string@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz" @@ -5783,6 +6503,11 @@ micromark-util-decode-string@^2.0.0: micromark-util-decode-numeric-character-reference "^2.0.0" micromark-util-symbol "^2.0.0" +micromark-util-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz" + integrity sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw== + micromark-util-encode@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz" @@ -5802,11 +6527,23 @@ micromark-util-events-to-acorn@^2.0.0: micromark-util-types "^2.0.0" vfile-message "^4.0.0" +micromark-util-html-tag-name@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz" + integrity sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q== + micromark-util-html-tag-name@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz" integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== +micromark-util-normalize-identifier@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz" + integrity sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-normalize-identifier@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz" @@ -5814,6 +6551,13 @@ micromark-util-normalize-identifier@^2.0.0: dependencies: micromark-util-symbol "^2.0.0" +micromark-util-resolve-all@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz" + integrity sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA== + dependencies: + micromark-util-types "^1.0.0" + micromark-util-resolve-all@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz" @@ -5821,6 +6565,15 @@ micromark-util-resolve-all@^2.0.0: dependencies: micromark-util-types "^2.0.0" +micromark-util-sanitize-uri@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz" + integrity sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-sanitize-uri@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz" @@ -5830,6 +6583,16 @@ micromark-util-sanitize-uri@^2.0.0: micromark-util-encode "^2.0.0" micromark-util-symbol "^2.0.0" +micromark-util-subtokenize@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz" + integrity sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + micromark-util-subtokenize@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz" @@ -5850,7 +6613,7 @@ micromark-util-symbol@^2.0.0: resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz" integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== -micromark-util-types@^1.0.0: +micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: version "1.1.0" resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz" integrity sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg== @@ -5860,6 +6623,29 @@ micromark-util-types@^2.0.0: resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz" integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== +micromark@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz" + integrity sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + micromark-core-commonmark "^1.0.1" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + uvu "^0.5.0" + micromark@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz" @@ -5986,6 +6772,11 @@ minimist@^1.2.0: resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +mri@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + mrmime@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz" @@ -6057,6 +6848,11 @@ node-releases@^2.0.18: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== +non-layered-tidy-tree-layout@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz" + integrity sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -6290,7 +7086,14 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.3" parse5 "^7.0.0" -parse5@^7.0.0: +parse5-parser-stream@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz" + integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== + dependencies: + parse5 "^7.0.0" + +parse5@^7.0.0, parse5@^7.1.2: version "7.2.1" resolved "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz" integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== @@ -6854,7 +7657,7 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@*, "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", react-dom@^18.0.0, "react-dom@>= 16.8.0 < 19.0.0": +react-dom@*, "react-dom@^16.14.0 || 17 || ^18", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", react-dom@^18.0.0, "react-dom@>= 16.8.0 < 19.0.0": version "18.3.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -6942,7 +7745,7 @@ react-router@^5.3.4, react-router@>=5, react-router@5.3.4: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react@*, "react@^16.13.1 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.3.1, "react@>= 16.8.0 < 19.0.0", react@>=15, react@>=16, react@>=16.0.0: +react@*, "react@^16.13.1 || ^17.0.0 || ^18.0.0", "react@^16.14.0 || ^17 || ^18", "react@^16.6.0 || ^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.3.1, "react@>= 16.8.0 < 19.0.0", react@>=15, react@>=16, react@>=16.0.0: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -7277,6 +8080,11 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +robust-predicates@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz" + integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== + rtl-detect@^1.0.4: version "1.1.2" resolved "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz" @@ -7299,6 +8107,18 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rw@1: + version "1.3.3" + resolved "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz" + integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== + +sade@^1.7.3: + version "1.8.1" + resolved "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz" + integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== + dependencies: + mri "^1.1.0" + safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -7309,7 +8129,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -7810,6 +8630,11 @@ stylehacks@^6.1.1: browserslist "^4.23.0" postcss-selector-parser "^6.0.16" +stylis@^4.1.3: + version "4.3.4" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz" + integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" @@ -7925,7 +8750,12 @@ trough@^2.0.0: resolved "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz" integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== -tslib@^2.0.3, tslib@^2.6.0: +ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + +tslib@^2.0.3, tslib@^2.4.0, tslib@^2.6.0: version "2.8.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz" integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== @@ -7965,6 +8795,11 @@ undici-types@~6.19.8: resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== +undici@^6.19.5: + version "6.21.0" + resolved "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz" + integrity sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz" @@ -8034,6 +8869,13 @@ unist-util-position@^5.0.0: dependencies: "@types/unist" "^3.0.0" +unist-util-stringify-position@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz" + integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz" @@ -8137,6 +8979,21 @@ uuid@^8.3.2: resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +uvu@^0.5.0: + version "0.5.6" + resolved "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz" + integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== + dependencies: + dequal "^2.0.0" + diff "^5.0.0" + kleur "^4.0.3" + sade "^1.7.3" + value-equal@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz" @@ -8191,6 +9048,11 @@ web-namespaces@^2.0.0: resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz" integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== +web-worker@^1.2.0: + version "1.3.0" + resolved "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz" + integrity sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA== + webpack-bundle-analyzer@^4.9.0: version "4.10.2" resolved "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz" @@ -8323,6 +9185,18 @@ websocket-extensions@>=0.1.1: resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" diff --git a/envoy/envoy.template.yaml b/envoy/envoy.template.yaml index 44d78965..6c8cad11 100644 --- a/envoy/envoy.template.yaml +++ b/envoy/envoy.template.yaml @@ -90,18 +90,22 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog http_filters: - # Extracts the Service ID from either the subdomain of the request's host field or the - # `target-service-id` header and attaches it to the request as the `target-service-id` header. + # Extracts the Service ID from one of: + # 1. The subdomain of the request's host field + # 2. The `target-service-id` header # - # This will require the service ID to be specified in the request's subdomain or a valid - # `target-service-id` header; otherwise, the request will be rejected. + # In both of the above cases, the filter attaches the extracted + # service ID to the request as the `target-service-id` header. # - # eg 1. subdomain = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" - # eg 2. header = "target-service-id: anvil" will be transformed to Header: "target-service-id: anvil" - # eg 3. header (aliased) = "target-service-id: eth" will be transformed to Header: "target-service-id: F00C" + # If a valid service ID is not found, the request will be rejected. + # + # Three concrete examples: + # 1. subdomain = "anvil.path.grove.city" will be transformed to Header: "target-service-id: anvil" + # 2. header = "target-service-id: anvil" will be transformed to Header: "target-service-id: anvil" + # 3. header (aliased) = "target-service-id: eth" will be transformed to Header: "target-service-id: F00C" # # IMPORTANT: The `.allowed-services.lua` file must contain all service IDs allowed by the PATH instance. - # Only requests for services defined in `.allowed-services.lua` will be forwarded to PATH; + # Only requests for services defined in `.allowed-services.lua` will be forwarded to PATH; # otherwise, the request will be rejected. # # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter @@ -115,24 +119,24 @@ static_resources: -- Load the mapping of subdomains to service IDs from an external file _G.subdomain_to_service_id = dofile("/etc/envoy/.allowed-services.lua") end - + local host = handle:headers():get(":authority") local subdomain = string.match(host, "^([^.]+)") local target_service_id_header = handle:headers():get("target-service-id") - + -- Function to resolve service ID from a given key local function resolve_service_id(key) return _G.subdomain_to_service_id[key] end - + -- Attempt to resolve service ID from subdomain local service_id = resolve_service_id(subdomain) - + -- If not found, attempt to resolve from "target-service-id" header if not service_id and target_service_id_header then service_id = resolve_service_id(target_service_id_header) end - + if service_id then -- Update the "target-service-id" header with the resolved service ID handle:headers():replace("target-service-id", service_id) @@ -147,7 +151,7 @@ static_resources: # Removes the `jwt-user-id` header before forwarding the request to the external authorization filter. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_mutation_filter # - # DEV_NOTE: If the Gateway Operator does not wish to use JWT authorization, + # DEV_NOTE: If the Gateway Operator does not wish to use JWT authorization, # this filter will be removed from the configuration file. - name: envoy.filters.http.header_mutation typed_config: @@ -158,7 +162,7 @@ static_resources: # Verifies JWT tokens and sets the `jwt-user-id` header based on the token claims. # See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter # - # DEV_NOTE: If the Gateway Operator does not wish to use JWT authorization, + # DEV_NOTE: If the Gateway Operator does not wish to use JWT authorization, # this filter will be removed from the configuration file. - name: envoy.filters.http.jwt_authn typed_config: From 96cfe9d59524116b5a2782a749ef12b248c3612e Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Thu, 19 Dec 2024 14:13:01 -0800 Subject: [PATCH 48/48] Review the introduction --- docusaurus/docs/develop/envoy/introduction.md | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/docusaurus/docs/develop/envoy/introduction.md b/docusaurus/docs/develop/envoy/introduction.md index fc680b49..7562101b 100644 --- a/docusaurus/docs/develop/envoy/introduction.md +++ b/docusaurus/docs/develop/envoy/introduction.md @@ -56,17 +56,22 @@ title: Introduction 2. Run `make init_envoy` to create all the required config files - - `.allowed-services.lua` is created with the service IDs allowed by the PATH instance. - - ℹ️ _Please update `allowed-services.lua` with the service IDs allowed by your PATH instance._ - - For more details, see the [Allowed Services Map](#allowed-services-map) section. - `.envoy.yaml` is created with your auth provider's domain and audience. - `.ratelimit.yaml` is created with the rate limiting configuration for the PATH instance. + - `.allowed-services.lua` is created with the service IDs allowed by the PATH instance. + - For more details, see the [Allowed Services Map](#allowed-services-map) section. - `.gateway-endpoints.yaml` is created from the example file in the [PADS Repository](https://github.com/buildwithgrove/path-auth-data-server/tree/main/yaml/testdata). - - ℹ️ _Please update `gateway-endpoints.yaml` with your own data._ - For more details, see the [Gateway Endpoint YAML File](#gateway-endpoint-yaml-file) section. 3. Run `make path_up` to start the services with all auth and rate limiting dependencies. +:::info MAKE SURE TO UPDATE THESE FILES + +ℹ️ Please update `allowed-services.lua` with the service IDs allowed by your PATH instance +and `gateway-endpoints.yaml` with your own data. + +::: + ## Overview This folder contains everything necessary for managing authorization and rate limiting in the PATH service. @@ -219,8 +224,8 @@ The `target-service-id` header is used to specify the Service ID in the request. There are two methods for specifying this header in the request: -1. [Target Service ID Header](#target-service-id-header) -2. [URL Subdomain](#url-subdomain) +1. [Target Service ID Header](#target-service-id-header) (_e.g. -H "target-service-id: anvil"_) +2. [URL Subdomain](#url-subdomain) (_e.g. http://anvil.localhost:3001/v1_) ### Allowed Services File @@ -228,9 +233,7 @@ The file `local/path/envoy/.allowed-services.lua` defines the mapping of service All service IDs (and optional service aliases) used by the PATH service must be defined in this file. -:::info - -_`.allowed-services.lua` format:_ +:::info `.allowed-services.lua` format ```lua return { @@ -249,9 +252,7 @@ return { The service ID (or a configured alias) may be specified in the `target-service-id` header. -:::info - -_Example request:_ +:::info Example request ```bash curl http://localhost:3001/v1 \ @@ -270,9 +271,7 @@ The service ID (or a configured alias) may be specified in the URL subdomain. eg. `host = "anvil.path.grove.city" -> Header: "target-service-id: anvil"` -:::info - -_Example request:_ +:::info Example request ```bash curl http://anvil.localhost:3001/v1 \ @@ -346,11 +345,9 @@ curl http://anvil.localhost:3001/v1 \ -d '{"jsonrpc": "2.0", "id": 1, "method": "eth_blockNumber" }' ``` -:::tip - -A variety of example cURL requests to the PATH service [may be found in the test_requests.mk file](https://github.com/buildwithgrove/path/blob/main/makefiles/test_requests.mk). +:::tip Example curl requests -_eg._ +A variety of example cURL requests to the PATH service can be found in the [`test_requests.mk` file](https://github.com/buildwithgrove/path/blob/main/makefiles/test_requests.mk). ```bash ## Test request with no auth, endpoint ID passed in the URL path and the service ID passed as the subdomain